diff --git a/DEPS b/DEPS
index 0388ba0..f57094ac 100644
--- a/DEPS
+++ b/DEPS
@@ -128,7 +128,7 @@
   'checkout_simplechrome': '(checkout_chromeos and host_os == "linux") and ("{cros_board}" != "")',
   # Surround the board var in quotes so gclient doesn't try parsing the string
   # as an expression.
-  'cros_download_vm': '("{cros_board}" == "amd64-generic") or ("{cros_board}" == "betty")',
+  'cros_download_vm': '(("{cros_board}" == "amd64-generic") or ("{cros_board}" == "betty")) or ("{cros_board}" == "betty-pi-arc")',
   # Should we build and test for public (ie: full) CrOS images, or private
   # (ie: release) images.
   'use_public_cros_config': 'not checkout_src_internal',
@@ -162,11 +162,11 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling Skia
   # and whatever else without interference from each other.
-  'skia_revision': 'f07a36341330877adc12ab61582e13801dd7fe42',
+  'skia_revision': '4c2146f2ca2a1c3a6735f597a0552e2146971edd',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling V8
   # and whatever else without interference from each other.
-  'v8_revision': '1a76e68814fab189993fa11e0cd534eb49df7977',
+  'v8_revision': 'c38bb894636dfbea39aac5754446dfe107557498',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling swarming_client
   # and whatever else without interference from each other.
@@ -174,11 +174,11 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling ANGLE
   # and whatever else without interference from each other.
-  'angle_revision': '3c049a34714db5d033ba2edc82c8dfe7d911dc48',
+  'angle_revision': '01f7e545a37e83529a36b63e14c027cdb25aae30',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling SwiftShader
   # and whatever else without interference from each other.
-  'swiftshader_revision': 'e3a5983705da24448382bc696b0663dc5d0a3ed7',
+  'swiftshader_revision': '645a8dea641aae363ee9e47fa7fcb9c930312365',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling PDFium
   # and whatever else without interference from each other.
@@ -225,7 +225,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling catapult
   # and whatever else without interference from each other.
-  'catapult_revision': '410370705505e49240d054d69de3cb626e641331',
+  'catapult_revision': 'a8bbe3b252a54046040b0b4725778d2fd95097ed',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling libFuzzer
   # and whatever else without interference from each other.
@@ -233,7 +233,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling devtools-node-modules
   # and whatever else without interference from each other.
-  'devtools_node_modules_revision': '9f563a2b5303b181a5a938ed3cca7b015d8ddb46',
+  'devtools_node_modules_revision': '56245d8ba398ac98d39974c3552b25e30a60a1b9',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling libprotobuf-mutator
   # and whatever else without interference from each other.
@@ -853,7 +853,7 @@
 
   # Build tools for Chrome OS. Note: This depends on third_party/pyelftools.
   'src/third_party/chromite': {
-      'url': Var('chromium_git') + '/chromiumos/chromite.git' + '@' + '8de7737d65f367549efce0ad1677b6a3f2e79e10',
+      'url': Var('chromium_git') + '/chromiumos/chromite.git' + '@' + 'b87974eebeaa98e5df5c03ddf1c634b5ebd1fd42',
       'condition': 'checkout_linux',
   },
 
@@ -878,7 +878,7 @@
   },
 
   'src/third_party/depot_tools':
-    Var('chromium_git') + '/chromium/tools/depot_tools.git' + '@' + 'ba5bc99b6ab9b2e39188ce6054c00e301dd86c1b',
+    Var('chromium_git') + '/chromium/tools/depot_tools.git' + '@' + '8b95534d333ce22acd9e30d972d09d323950483c',
 
   'src/third_party/devtools-node-modules':
     Var('chromium_git') + '/external/github.com/ChromeDevTools/devtools-node-modules' + '@' + Var('devtools_node_modules_revision'),
@@ -1257,7 +1257,7 @@
   },
 
   'src/third_party/perfetto':
-    Var('android_git') + '/platform/external/perfetto.git' + '@' + '5a9d89d158eb86d62e11caf7afac82060da98d2f',
+    Var('android_git') + '/platform/external/perfetto.git' + '@' + '61918356b65e603c99843c7351748ccce6d2b775',
 
   'src/third_party/perl': {
       'url': Var('chromium_git') + '/chromium/deps/perl.git' + '@' + '6f3e5028eb65d0b4c5fdd792106ac4c84eee1eb3',
@@ -1425,7 +1425,7 @@
     Var('chromium_git') + '/external/khronosgroup/webgl.git' + '@' + '7c4e67ff117d6c640e6dd17989afe2fb7da7eecb',
 
   'src/third_party/webrtc':
-    Var('webrtc_git') + '/src.git' + '@' + '67309ef93c4f8ff0c62d2d8806f79e69cd553b5c',
+    Var('webrtc_git') + '/src.git' + '@' + 'af3fdc069d528439fcd208ed4c4afd406ba9d63c',
 
   'src/third_party/xdg-utils': {
       'url': Var('chromium_git') + '/chromium/deps/xdg-utils.git' + '@' + 'd80274d5869b17b8c9067a1022e4416ee7ed5e0d',
@@ -1487,7 +1487,7 @@
     Var('chromium_git') + '/v8/v8.git' + '@' +  Var('v8_revision'),
 
   'src-internal': {
-    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@a4f7741ee65c7dfc7b353ccf806d32b10feb32d4',
+    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@30eeb0c5bf9c4c2786b1468591e784c20c318bbd',
     'condition': 'checkout_src_internal',
   },
 
diff --git a/android_webview/BUILD.gn b/android_webview/BUILD.gn
index 6f5916e..5d2454b 100644
--- a/android_webview/BUILD.gn
+++ b/android_webview/BUILD.gn
@@ -72,6 +72,7 @@
     "java/src/org/chromium/android_webview/InputStreamUtil.java",
     "java/src/org/chromium/android_webview/JsReplyProxy.java",
     "java/src/org/chromium/android_webview/PopupTouchHandleDrawable.java",
+    "java/src/org/chromium/android_webview/WebMessageListenerHolder.java",
     "java/src/org/chromium/android_webview/gfx/AwDrawFnImpl.java",
     "java/src/org/chromium/android_webview/gfx/AwGLFunctor.java",
     "java/src/org/chromium/android_webview/gfx/AwPicture.java",
@@ -563,14 +564,12 @@
     "browser/icon_helper.h",
     "browser/input_stream.cc",
     "browser/input_stream.h",
-    "browser/js_java_interaction/js_api_handler.cc",
-    "browser/js_java_interaction/js_api_handler.h",
-    "browser/js_java_interaction/js_api_handler_factory.cc",
-    "browser/js_java_interaction/js_api_handler_factory.h",
     "browser/js_java_interaction/js_java_configurator_host.cc",
     "browser/js_java_interaction/js_java_configurator_host.h",
     "browser/js_java_interaction/js_reply_proxy.cc",
     "browser/js_java_interaction/js_reply_proxy.h",
+    "browser/js_java_interaction/js_to_java_messaging.cc",
+    "browser/js_java_interaction/js_to_java_messaging.h",
     "browser/memory_metrics_logger.cc",
     "browser/memory_metrics_logger.h",
     "browser/network_service/android_stream_reader_url_loader.cc",
@@ -845,6 +844,7 @@
     "java/src/org/chromium/android_webview/VariationsSeedLoader.java",
     "java/src/org/chromium/android_webview/ViewPositionObserver.java",
     "java/src/org/chromium/android_webview/WebMessageListener.java",
+    "java/src/org/chromium/android_webview/WebMessageListenerHolder.java",
     "java/src/org/chromium/android_webview/WebViewChromiumRunQueue.java",
     "java/src/org/chromium/android_webview/gfx/AwDrawFnImpl.java",
     "java/src/org/chromium/android_webview/gfx/AwFunctor.java",
diff --git a/android_webview/browser/aw_content_browser_client.cc b/android_webview/browser/aw_content_browser_client.cc
index 9ff2a42b..71f5986 100644
--- a/android_webview/browser/aw_content_browser_client.cc
+++ b/android_webview/browser/aw_content_browser_client.cc
@@ -27,7 +27,6 @@
 #include "android_webview/browser/aw_speech_recognition_manager_delegate.h"
 #include "android_webview/browser/aw_web_contents_view_delegate.h"
 #include "android_webview/browser/cookie_manager.h"
-#include "android_webview/browser/js_java_interaction/js_api_handler_factory.h"
 #include "android_webview/browser/network_service/aw_proxy_config_monitor.h"
 #include "android_webview/browser/network_service/aw_proxying_restricted_cookie_manager.h"
 #include "android_webview/browser/network_service/aw_proxying_url_loader_factory.h"
@@ -37,7 +36,6 @@
 #include "android_webview/common/aw_content_client.h"
 #include "android_webview/common/aw_descriptors.h"
 #include "android_webview/common/aw_switches.h"
-#include "android_webview/common/js_java_interaction/interfaces.mojom.h"
 #include "android_webview/common/render_view_messages.h"
 #include "android_webview/common/url_constants.h"
 #include "android_webview/grit/aw_resources.h"
@@ -713,13 +711,6 @@
         render_frame_host);
     return true;
   }
-  if (interface_name == mojom::JsToJavaMessaging::Name_) {
-    JsApiHandlerFactory::BindJsToJavaMessaging(
-        mojo::PendingAssociatedReceiver<mojom::JsToJavaMessaging>(
-            std::move(*handle)),
-        render_frame_host);
-    return true;
-  }
 
   return false;
 }
@@ -934,6 +925,17 @@
   return false;
 }
 
+bool AwContentBrowserClient::ShouldLockToOrigin(
+    content::BrowserContext* browser_context,
+    const GURL& effective_url) {
+  // TODO(lukasza): https://crbug.cmo/869494: Once Android WebView supports
+  // OOPIFs, we should remove this ShouldLockToOrigin overload.  Till then,
+  // returning false helps avoid accidentally applying citadel-style Site
+  // Isolation enforcement to Android WebView (and causing incorrect renderer
+  // kills).
+  return false;
+}
+
 bool AwContentBrowserClient::WillCreateURLLoaderFactory(
     content::BrowserContext* browser_context,
     content::RenderFrameHost* frame,
diff --git a/android_webview/browser/aw_content_browser_client.h b/android_webview/browser/aw_content_browser_client.h
index 5ff324c..361114c 100644
--- a/android_webview/browser/aw_content_browser_client.h
+++ b/android_webview/browser/aw_content_browser_client.h
@@ -191,6 +191,8 @@
       NonNetworkURLLoaderFactoryMap* factories) override;
   bool ShouldIsolateErrorPage(bool in_main_frame) override;
   bool ShouldEnableStrictSiteIsolation() override;
+  bool ShouldLockToOrigin(content::BrowserContext* browser_context,
+                          const GURL& effective_url) override;
   bool WillCreateURLLoaderFactory(
       content::BrowserContext* browser_context,
       content::RenderFrameHost* frame,
diff --git a/android_webview/browser/aw_contents.cc b/android_webview/browser/aw_contents.cc
index 0bd89d6..e2c29a4 100644
--- a/android_webview/browser/aw_contents.cc
+++ b/android_webview/browser/aw_contents.cc
@@ -1290,33 +1290,21 @@
   return js_java_configurator_host_.get();
 }
 
-base::android::ScopedJavaLocalRef<jstring> AwContents::SetJsApiService(
+base::android::ScopedJavaLocalRef<jstring> AwContents::AddWebMessageListener(
     JNIEnv* env,
     const base::android::JavaParamRef<jobject>& obj,
-    jboolean need_to_inject_js_object,
+    const base::android::JavaParamRef<jobject>& listener,
     const base::android::JavaParamRef<jstring>& js_object_name,
     const base::android::JavaParamRef<jobjectArray>& allowed_origin_rules) {
-  return GetJsJavaConfiguratorHost()->SetJsApiService(
-      env, need_to_inject_js_object, js_object_name, allowed_origin_rules);
+  return GetJsJavaConfiguratorHost()->AddWebMessageListener(
+      env, listener, js_object_name, allowed_origin_rules);
 }
 
-bool AwContents::IsOriginAllowedForOnPostMessage(url::Origin& origin) {
-  return GetJsJavaConfiguratorHost()->IsOriginAllowedForOnPostMessage(origin);
-}
-
-void AwContents::OnPostMessage(
+void AwContents::RemoveWebMessageListener(
     JNIEnv* env,
-    const base::android::JavaRef<jstring>& message,
-    const base::android::JavaRef<jstring>& origin,
-    jboolean is_main_frame,
-    const base::android::JavaRef<jintArray>& ports,
-    const base::android::JavaRef<jobject>& reply_proxy) {
-  const ScopedJavaLocalRef<jobject> obj = java_ref_.get(env);
-  if (obj.is_null())
-    return;
-
-  Java_AwContents_onPostMessage(env, obj, message, origin, is_main_frame, ports,
-                                reply_proxy);
+    const base::android::JavaParamRef<jobject>& obj,
+    const base::android::JavaParamRef<jstring>& js_object_name) {
+  GetJsJavaConfiguratorHost()->RemoveWebMessageListener(env, js_object_name);
 }
 
 void AwContents::ClearView(JNIEnv* env, const JavaParamRef<jobject>& obj) {
diff --git a/android_webview/browser/aw_contents.h b/android_webview/browser/aw_contents.h
index 3e110a51d..bc1d55d 100644
--- a/android_webview/browser/aw_contents.h
+++ b/android_webview/browser/aw_contents.h
@@ -218,21 +218,17 @@
 
   JsJavaConfiguratorHost* GetJsJavaConfiguratorHost();
 
-  base::android::ScopedJavaLocalRef<jstring> SetJsApiService(
+  base::android::ScopedJavaLocalRef<jstring> AddWebMessageListener(
       JNIEnv* env,
       const base::android::JavaParamRef<jobject>& obj,
-      jboolean need_to_inject_js_object,
+      const base::android::JavaParamRef<jobject>& listener,
       const base::android::JavaParamRef<jstring>& js_object_name,
       const base::android::JavaParamRef<jobjectArray>& allowed_origins);
 
-  bool IsOriginAllowedForOnPostMessage(url::Origin& origin);
-
-  void OnPostMessage(JNIEnv* env,
-                     const base::android::JavaRef<jstring>& message,
-                     const base::android::JavaRef<jstring>& origin,
-                     jboolean is_main_frame,
-                     const base::android::JavaRef<jintArray>& ports,
-                     const base::android::JavaRef<jobject>& reply_proxy);
+  void RemoveWebMessageListener(
+      JNIEnv* env,
+      const base::android::JavaParamRef<jobject>& obj,
+      const base::android::JavaParamRef<jstring>& js_object_name);
 
   bool GetViewTreeForceDarkState() { return view_tree_force_dark_state_; }
 
diff --git a/android_webview/browser/aw_variations_seed_bridge.cc b/android_webview/browser/aw_variations_seed_bridge.cc
index cc29c18c..5726af6 100644
--- a/android_webview/browser/aw_variations_seed_bridge.cc
+++ b/android_webview/browser/aw_variations_seed_bridge.cc
@@ -19,28 +19,27 @@
 namespace android_webview {
 
 std::unique_ptr<variations::SeedResponse> GetAndClearJavaSeed() {
-  using namespace base::android;
-
-  JNIEnv* env = AttachCurrentThread();
+  JNIEnv* env = base::android::AttachCurrentThread();
   if (!Java_AwVariationsSeedBridge_haveSeed(env))
     return nullptr;
 
-  ScopedJavaLocalRef<jstring> j_signature =
+  base::android::ScopedJavaLocalRef<jstring> j_signature =
       Java_AwVariationsSeedBridge_getSignature(env);
-  ScopedJavaLocalRef<jstring> j_country =
+  base::android::ScopedJavaLocalRef<jstring> j_country =
       Java_AwVariationsSeedBridge_getCountry(env);
-  ScopedJavaLocalRef<jstring> j_date = Java_AwVariationsSeedBridge_getDate(env);
+  base::android::ScopedJavaLocalRef<jstring> j_date =
+      Java_AwVariationsSeedBridge_getDate(env);
   jboolean j_is_gzip_compressed =
       Java_AwVariationsSeedBridge_getIsGzipCompressed(env);
-  ScopedJavaLocalRef<jbyteArray> j_data =
+  base::android::ScopedJavaLocalRef<jbyteArray> j_data =
       Java_AwVariationsSeedBridge_getData(env);
   Java_AwVariationsSeedBridge_clearSeed(env);
 
   auto java_seed = std::make_unique<variations::SeedResponse>();
   base::android::JavaByteArrayToString(env, j_data, &java_seed->data);
-  java_seed->signature = ConvertJavaStringToUTF8(j_signature);
-  java_seed->country = ConvertJavaStringToUTF8(j_country);
-  java_seed->date = ConvertJavaStringToUTF8(j_date);
+  java_seed->signature = base::android::ConvertJavaStringToUTF8(j_signature);
+  java_seed->country = base::android::ConvertJavaStringToUTF8(j_country);
+  java_seed->date = base::android::ConvertJavaStringToUTF8(j_date);
   java_seed->is_gzip_compressed = static_cast<bool>(j_is_gzip_compressed);
   return java_seed;
 }
diff --git a/android_webview/browser/js_java_interaction/js_api_handler.h b/android_webview/browser/js_java_interaction/js_api_handler.h
deleted file mode 100644
index bb87897..0000000
--- a/android_webview/browser/js_java_interaction/js_api_handler.h
+++ /dev/null
@@ -1,53 +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 ANDROID_WEBVIEW_BROWSER_JS_JAVA_INTERACTION_JS_API_HANDLER_H_
-#define ANDROID_WEBVIEW_BROWSER_JS_JAVA_INTERACTION_JS_API_HANDLER_H_
-
-#include <vector>
-
-#include "android_webview/browser/js_java_interaction/js_reply_proxy.h"
-#include "android_webview/common/js_java_interaction/interfaces.mojom.h"
-#include "base/strings/string16.h"
-#include "mojo/public/cpp/bindings/associated_receiver.h"
-#include "mojo/public/cpp/bindings/associated_remote.h"
-#include "mojo/public/cpp/bindings/pending_associated_receiver.h"
-#include "mojo/public/cpp/system/message_pipe.h"
-
-namespace content {
-class RenderFrameHost;
-}
-
-namespace android_webview {
-
-// Implementation of mojo::JsToJavaMessaging interface. Receives PostMessage()
-// call from renderer JsBinding.
-class JsApiHandler : public mojom::JsToJavaMessaging {
- public:
-  explicit JsApiHandler(content::RenderFrameHost* rfh);
-  ~JsApiHandler() override;
-
-  // Binds mojo.
-  void BindPendingReceiver(
-      mojo::PendingAssociatedReceiver<mojom::JsToJavaMessaging>
-          pending_receiver);
-
-  // mojom::JsToJavaMessaging implementation.
-  void PostMessage(const base::string16& message,
-                   std::vector<mojo::ScopedMessagePipeHandle> ports) override;
-  void SetJavaToJsMessaging(mojo::PendingRemote<mojom::JavaToJsMessaging>
-                                java_to_js_messaging) override;
-
- private:
-  std::unique_ptr<JsReplyProxy> reply_proxy_;
-  content::RenderFrameHost* render_frame_host_;
-  mojo::AssociatedRemote<mojom::JsJavaConfigurator> configurator_remote_;
-  mojo::AssociatedReceiver<mojom::JsToJavaMessaging> receiver_{this};
-
-  DISALLOW_COPY_AND_ASSIGN(JsApiHandler);
-};
-
-}  // namespace android_webview
-
-#endif  // ANDROID_WEBVIEW_BROWSER_JS_JAVA_INTERACTION_JS_API_HANDLER_H_
diff --git a/android_webview/browser/js_java_interaction/js_api_handler_factory.cc b/android_webview/browser/js_java_interaction/js_api_handler_factory.cc
deleted file mode 100644
index a1dc2930..0000000
--- a/android_webview/browser/js_java_interaction/js_api_handler_factory.cc
+++ /dev/null
@@ -1,78 +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 "android_webview/browser/js_java_interaction/js_api_handler_factory.h"
-
-#include "android_webview/browser/js_java_interaction/js_api_handler.h"
-#include "content/public/browser/web_contents.h"
-
-namespace android_webview {
-namespace {
-const void* const kUserDataKey = &kUserDataKey;
-}
-
-JsApiHandlerFactory::JsApiHandlerFactory(content::WebContents* web_contents)
-    : content::WebContentsObserver(web_contents) {
-  for (content::RenderFrameHost* render_frame_host :
-       web_contents->GetAllFrames()) {
-    RenderFrameCreated(render_frame_host);
-  }
-}
-
-JsApiHandlerFactory::~JsApiHandlerFactory() = default;
-
-// static
-JsApiHandlerFactory* JsApiHandlerFactory::FromWebContents(
-    content::WebContents* web_contents) {
-  JsApiHandlerFactory* factory = static_cast<JsApiHandlerFactory*>(
-      web_contents->GetUserData(kUserDataKey));
-
-  if (!factory) {
-    factory = new JsApiHandlerFactory(web_contents);
-    web_contents->SetUserData(kUserDataKey, base::WrapUnique(factory));
-  }
-  return factory;
-}
-
-// static
-void JsApiHandlerFactory::BindJsToJavaMessaging(
-    mojo::PendingAssociatedReceiver<mojom::JsToJavaMessaging> pending_receiver,
-    content::RenderFrameHost* render_frame_host) {
-  content::WebContents* web_contents =
-      content::WebContents::FromRenderFrameHost(render_frame_host);
-
-  if (!web_contents)
-    return;
-
-  JsApiHandlerFactory* factory =
-      JsApiHandlerFactory::FromWebContents(web_contents);
-  if (!factory)
-    return;
-
-  JsApiHandler* handler = factory->JsApiHandlerForFrame(render_frame_host);
-  if (handler)
-    handler->BindPendingReceiver(std::move(pending_receiver));
-}
-
-void JsApiHandlerFactory::RenderFrameCreated(
-    content::RenderFrameHost* render_frame_host) {
-  if (JsApiHandlerForFrame(render_frame_host))
-    return;
-
-  frame_map_[render_frame_host] =
-      std::make_unique<JsApiHandler>(render_frame_host);
-}
-
-void JsApiHandlerFactory::RenderFrameDeleted(
-    content::RenderFrameHost* render_frame_host) {
-  frame_map_.erase(render_frame_host);
-}
-
-JsApiHandler* JsApiHandlerFactory::JsApiHandlerForFrame(
-    content::RenderFrameHost* render_frame_host) {
-  auto itr = frame_map_.find(render_frame_host);
-  return itr == frame_map_.end() ? nullptr : itr->second.get();
-}
-
-}  // namespace android_webview
diff --git a/android_webview/browser/js_java_interaction/js_api_handler_factory.h b/android_webview/browser/js_java_interaction/js_api_handler_factory.h
deleted file mode 100644
index 822d685..0000000
--- a/android_webview/browser/js_java_interaction/js_api_handler_factory.h
+++ /dev/null
@@ -1,58 +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 ANDROID_WEBVIEW_BROWSER_JS_JAVA_INTERACTION_JS_API_HANDLER_FACTORY_H_
-#define ANDROID_WEBVIEW_BROWSER_JS_JAVA_INTERACTION_JS_API_HANDLER_FACTORY_H_
-
-#include <map>
-
-#include "android_webview/common/js_java_interaction/interfaces.mojom.h"
-#include "base/supports_user_data.h"
-#include "content/public/browser/web_contents_observer.h"
-#include "mojo/public/cpp/bindings/pending_associated_receiver.h"
-
-namespace content {
-class WebContents;
-}  // namespace content
-
-namespace android_webview {
-
-class JsApiHandler;
-
-// A WebContentsObserver that creates JsApiHandler for each RenderFrameHost. It
-// also destroy the corresponding JsApiHandler when that RenderFrameHost has
-// been deleted. It also works as a broker, passes message binding to the
-// correct JsApiHandler.
-class JsApiHandlerFactory : public content::WebContentsObserver,
-                            public base::SupportsUserData::Data {
- public:
-  ~JsApiHandlerFactory() override;
-
-  static JsApiHandlerFactory* FromWebContents(content::WebContents* contents);
-
-  // Finds the corresponding JsApiHandler from |render_frame_host| and
-  // dispatches the |pending_receiver| to it.
-  static void BindJsToJavaMessaging(
-      mojo::PendingAssociatedReceiver<mojom::JsToJavaMessaging>
-          pending_receiver,
-      content::RenderFrameHost* render_frame_host);
-
-  // content::WebContentsObserver
-  void RenderFrameCreated(content::RenderFrameHost* render_frame_host) override;
-  void RenderFrameDeleted(content::RenderFrameHost* render_frame_host) override;
-
-  JsApiHandler* JsApiHandlerForFrame(
-      content::RenderFrameHost* render_frame_host);
-
- private:
-  explicit JsApiHandlerFactory(content::WebContents* web_contents);
-
-  std::map<content::RenderFrameHost*, std::unique_ptr<JsApiHandler>> frame_map_;
-
-  DISALLOW_COPY_AND_ASSIGN(JsApiHandlerFactory);
-};
-
-}  // namespace android_webview
-
-#endif  // ANDROID_WEBVIEW_BROWSER_JS_JAVA_INTERACTION_JS_API_HANDLER_FACTORY_H_
diff --git a/android_webview/browser/js_java_interaction/js_java_configurator_host.cc b/android_webview/browser/js_java_interaction/js_java_configurator_host.cc
index 147dcabd..aecc3a2 100644
--- a/android_webview/browser/js_java_interaction/js_java_configurator_host.cc
+++ b/android_webview/browser/js_java_interaction/js_java_configurator_host.cc
@@ -4,18 +4,46 @@
 
 #include "android_webview/browser/js_java_interaction/js_java_configurator_host.h"
 
+#include "android_webview/browser/js_java_interaction/js_to_java_messaging.h"
 #include "base/android/jni_array.h"
 #include "base/android/jni_string.h"
 #include "base/bind.h"
+#include "base/strings/utf_string_conversions.h"
 #include "content/public/browser/web_contents.h"
+#include "mojo/public/cpp/bindings/pending_associated_remote.h"
 #include "third_party/blink/public/common/associated_interfaces/associated_interface_provider.h"
 
-namespace url {
-class Origin;
-}
-
 namespace android_webview {
 
+struct JsObject {
+  JsObject(base::string16 name,
+           net::ProxyBypassRules allowed_origin_rules,
+           const base::android::JavaRef<jobject>& listener)
+      : name_(std::move(name)),
+        allowed_origin_rules_(std::move(allowed_origin_rules)),
+        listener_ref_(listener) {}
+
+  JsObject(JsObject&& other)
+      : name_(std::move(other.name_)),
+        allowed_origin_rules_(std::move(other.allowed_origin_rules_)),
+        listener_ref_(std::move(other.listener_ref_)) {}
+
+  JsObject& operator=(JsObject&& other) {
+    name_ = std::move(other.name_);
+    allowed_origin_rules_ = std::move(other.allowed_origin_rules_);
+    listener_ref_ = std::move(other.listener_ref_);
+    return *this;
+  }
+
+  ~JsObject() = default;
+
+  base::string16 name_;
+  net::ProxyBypassRules allowed_origin_rules_;
+  base::android::ScopedJavaGlobalRef<jobject> listener_ref_;
+
+  DISALLOW_COPY_AND_ASSIGN(JsObject);
+};
+
 JsJavaConfiguratorHost::JsJavaConfiguratorHost(
     content::WebContents* web_contents)
     : content::WebContentsObserver(web_contents) {}
@@ -23,35 +51,57 @@
 JsJavaConfiguratorHost::~JsJavaConfiguratorHost() = default;
 
 base::android::ScopedJavaLocalRef<jstring>
-JsJavaConfiguratorHost::SetJsApiService(
+JsJavaConfiguratorHost::AddWebMessageListener(
     JNIEnv* env,
-    bool need_to_inject_js_object,
+    const base::android::JavaParamRef<jobject>& listener,
     const base::android::JavaParamRef<jstring>& js_object_name,
     const base::android::JavaParamRef<jobjectArray>& allowed_origin_rules) {
-  base::android::ConvertJavaStringToUTF16(env, js_object_name,
-                                          &js_object_name_);
+  base::string16 native_js_object_name =
+      base::android::ConvertJavaStringToUTF16(env, js_object_name);
 
-  std::vector<std::string> native_allowed_origin_rules;
+  std::vector<std::string> native_allowed_origin_rule_strings;
   AppendJavaStringArrayToStringVector(env, allowed_origin_rules,
-                                      &native_allowed_origin_rules);
+                                      &native_allowed_origin_rule_strings);
 
-  need_to_inject_js_object_ = need_to_inject_js_object;
-  allowed_origin_rules_ = net::ProxyBypassRules();
-  for (auto& rule : native_allowed_origin_rules) {
-    if (!allowed_origin_rules_.AddRuleFromString(rule)) {
+  net::ProxyBypassRules native_allowed_origin_rules;
+  for (auto& rule : native_allowed_origin_rule_strings) {
+    if (!native_allowed_origin_rules.AddRuleFromString(rule)) {
       return base::android::ConvertUTF8ToJavaString(
           env, "allowedOriginRules " + rule + " is invalid");
     }
   }
 
+  for (const auto& js_object : js_objects_) {
+    if (js_object.name_ == native_js_object_name) {
+      return base::android::ConvertUTF16ToJavaString(
+          env, base::ASCIIToUTF16("jsObjectName ") + js_object.name_ +
+                   base::ASCIIToUTF16(" is already added."));
+    }
+  }
+
+  js_objects_.emplace_back(native_js_object_name, native_allowed_origin_rules,
+                           listener);
+
   web_contents()->ForEachFrame(base::BindRepeating(
       &JsJavaConfiguratorHost::NotifyFrame, base::Unretained(this)));
   return nullptr;
 }
 
-bool JsJavaConfiguratorHost::IsOriginAllowedForOnPostMessage(
-    const url::Origin& origin) {
-  return allowed_origin_rules_.Matches(origin.GetURL());
+void JsJavaConfiguratorHost::RemoveWebMessageListener(
+    JNIEnv* env,
+    const base::android::JavaParamRef<jstring>& js_object_name) {
+  base::string16 native_js_object_name =
+      ConvertJavaStringToUTF16(env, js_object_name);
+
+  for (auto iterator = js_objects_.begin(); iterator != js_objects_.end();
+       ++iterator) {
+    if (iterator->name_ == native_js_object_name) {
+      js_objects_.erase(iterator);
+      web_contents()->ForEachFrame(base::BindRepeating(
+          &JsJavaConfiguratorHost::NotifyFrame, base::Unretained(this)));
+      break;
+    }
+  }
 }
 
 void JsJavaConfiguratorHost::RenderFrameCreated(
@@ -59,13 +109,30 @@
   NotifyFrame(render_frame_host);
 }
 
+void JsJavaConfiguratorHost::RenderFrameDeleted(
+    content::RenderFrameHost* render_frame_host) {
+  js_to_java_messagings_.erase(render_frame_host);
+}
+
 void JsJavaConfiguratorHost::NotifyFrame(
     content::RenderFrameHost* render_frame_host) {
   mojo::AssociatedRemote<mojom::JsJavaConfigurator> configurator_remote;
   render_frame_host->GetRemoteAssociatedInterfaces()->GetInterface(
       &configurator_remote);
-  configurator_remote->SetJsApiService(need_to_inject_js_object_,
-                                       js_object_name_, allowed_origin_rules_);
+  std::vector<mojom::JsObjectPtr> js_objects;
+  js_objects.reserve(js_objects_.size());
+  for (const auto& js_object : js_objects_) {
+    mojo::PendingAssociatedRemote<mojom::JsToJavaMessaging> pending_remote;
+    js_to_java_messagings_[render_frame_host].emplace_back(
+        std::make_unique<JsToJavaMessaging>(
+            render_frame_host,
+            pending_remote.InitWithNewEndpointAndPassReceiver(),
+            js_object.listener_ref_, js_object.allowed_origin_rules_));
+    js_objects.push_back(std::move(
+        mojom::JsObject::New(js_object.name_, std::move(pending_remote),
+                             js_object.allowed_origin_rules_)));
+  }
+  configurator_remote->SetJsObjects(std::move(js_objects));
 }
 
 }  // namespace android_webview
diff --git a/android_webview/browser/js_java_interaction/js_java_configurator_host.h b/android_webview/browser/js_java_interaction/js_java_configurator_host.h
index b943dab..80e6af63 100644
--- a/android_webview/browser/js_java_interaction/js_java_configurator_host.h
+++ b/android_webview/browser/js_java_interaction/js_java_configurator_host.h
@@ -11,7 +11,6 @@
 #include "content/public/browser/web_contents_observer.h"
 #include "net/proxy_resolution/proxy_bypass_rules.h"
 #include "services/network/public/mojom/proxy_config.mojom.h"
-#include "url/origin.h"
 
 namespace content {
 class RenderFrameHost;
@@ -19,8 +18,12 @@
 
 namespace android_webview {
 
-// This class is 1:1 with WebContents, when SetJsApiService is called, it stores
-// the information in this class and send them to renderer side
+struct JsObject;
+
+class JsToJavaMessaging;
+
+// This class is 1:1 with WebContents, when AddWebMessageListener() is called,
+// it stores the information in this class and send them to renderer side
 // JsJavaConfigurator if there is any. When RenderFrameCreated() gets called, it
 // needs to configure that new RenderFrame with the information stores in this
 // class.
@@ -29,25 +32,29 @@
   explicit JsJavaConfiguratorHost(content::WebContents* web_contents);
   ~JsJavaConfiguratorHost() override;
 
-  base::android::ScopedJavaLocalRef<jstring> SetJsApiService(
+  // Native side AddWebMessageListener, returns an error message if the
+  // parameters didn't pass necessary checks.
+  base::android::ScopedJavaLocalRef<jstring> AddWebMessageListener(
       JNIEnv* env,
-      bool need_to_inject_js_object,
+      const base::android::JavaParamRef<jobject>& listener,
       const base::android::JavaParamRef<jstring>& js_object_name,
       const base::android::JavaParamRef<jobjectArray>& allowed_origin_rules);
 
-  bool IsOriginAllowedForOnPostMessage(const url::Origin& origin);
+  void RemoveWebMessageListener(
+      JNIEnv* env,
+      const base::android::JavaParamRef<jstring>& js_object_name);
 
   // content::WebContentsObserver implementations
   void RenderFrameCreated(content::RenderFrameHost* render_frame_host) override;
+  void RenderFrameDeleted(content::RenderFrameHost* render_frame_host) override;
 
  private:
   void NotifyFrame(content::RenderFrameHost* render_frame_host);
 
-  bool need_to_inject_js_object_ = false;
-  base::string16 js_object_name_;
-  // We use ProxyBypassRules because it has the functionality that suitable
-  // here, but it is not for proxy bypass.
-  net::ProxyBypassRules allowed_origin_rules_;
+  std::vector<JsObject> js_objects_;
+  std::map<content::RenderFrameHost*,
+           std::vector<std::unique_ptr<JsToJavaMessaging>>>
+      js_to_java_messagings_;
 
   DISALLOW_COPY_AND_ASSIGN(JsJavaConfiguratorHost);
 };
diff --git a/android_webview/browser/js_java_interaction/js_api_handler.cc b/android_webview/browser/js_java_interaction/js_to_java_messaging.cc
similarity index 65%
rename from android_webview/browser/js_java_interaction/js_api_handler.cc
rename to android_webview/browser/js_java_interaction/js_to_java_messaging.cc
index a938676..ad50c178 100644
--- a/android_webview/browser/js_java_interaction/js_api_handler.cc
+++ b/android_webview/browser/js_java_interaction/js_to_java_messaging.cc
@@ -2,39 +2,43 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "android_webview/browser/js_java_interaction/js_api_handler.h"
+#include "android_webview/browser/js_java_interaction/js_to_java_messaging.h"
 
 #include "android_webview/browser/aw_contents.h"
+#include "android_webview/browser_jni_headers/WebMessageListenerHolder_jni.h"
 #include "base/android/jni_android.h"
 #include "base/android/jni_array.h"
 #include "base/android/jni_string.h"
+#include "base/stl_util.h"
 #include "content/public/browser/render_process_host.h"
 #include "content/public/browser/web_contents.h"
 #include "third_party/blink/public/common/associated_interfaces/associated_interface_provider.h"
+#include "url/origin.h"
 
 namespace android_webview {
 
-JsApiHandler::JsApiHandler(content::RenderFrameHost* render_frame_host)
-    : render_frame_host_(render_frame_host) {}
-
-JsApiHandler::~JsApiHandler() {}
-
-void JsApiHandler::BindPendingReceiver(
-    mojo::PendingAssociatedReceiver<mojom::JsToJavaMessaging>
-        pending_receiver) {
-  receiver_.Bind(std::move(pending_receiver));
+JsToJavaMessaging::JsToJavaMessaging(
+    content::RenderFrameHost* render_frame_host,
+    mojo::PendingAssociatedReceiver<mojom::JsToJavaMessaging> receiver,
+    base::android::ScopedJavaGlobalRef<jobject> listener_ref,
+    const net::ProxyBypassRules& allowed_origin_rules)
+    : render_frame_host_(render_frame_host),
+      listener_ref_(listener_ref),
+      allowed_origin_rules_(allowed_origin_rules) {
+  receiver_.Bind(std::move(receiver));
 }
 
-void JsApiHandler::PostMessage(
+JsToJavaMessaging::~JsToJavaMessaging() {}
+
+void JsToJavaMessaging::PostMessage(
     const base::string16& message,
     std::vector<mojo::ScopedMessagePipeHandle> ports) {
   DCHECK(render_frame_host_);
 
   content::WebContents* web_contents =
       content::WebContents::FromRenderFrameHost(render_frame_host_);
-  AwContents* aw_contents = AwContents::FromWebContents(web_contents);
 
-  if (!aw_contents)
+  if (!web_contents)
     return;
 
   // |source_origin| has no race with this PostMessage call, because of
@@ -42,7 +46,7 @@
   // in sequence.
   url::Origin source_origin = render_frame_host_->GetLastCommittedOrigin();
 
-  if (!aw_contents->IsOriginAllowedForOnPostMessage(source_origin))
+  if (!allowed_origin_rules_.Matches(source_origin.GetURL()))
     return;
 
   std::vector<int> int_ports(ports.size(), MOJO_HANDLE_INVALID /* 0 */);
@@ -51,15 +55,15 @@
   }
 
   JNIEnv* env = base::android::AttachCurrentThread();
-  aw_contents->OnPostMessage(
-      env, base::android::ConvertUTF16ToJavaString(env, message),
+  Java_WebMessageListenerHolder_onPostMessage(
+      env, listener_ref_, base::android::ConvertUTF16ToJavaString(env, message),
       base::android::ConvertUTF8ToJavaString(env, source_origin.Serialize()),
       web_contents->GetMainFrame() == render_frame_host_,
       base::android::ToJavaIntArray(env, int_ports.data(), int_ports.size()),
       reply_proxy_->GetJavaPeer());
 }
 
-void JsApiHandler::SetJavaToJsMessaging(
+void JsToJavaMessaging::SetJavaToJsMessaging(
     mojo::PendingRemote<mojom::JavaToJsMessaging> java_to_js_messaging) {
   // A RenderFrame may inject JsToJavaMessaging in the JavaScript context more
   // than once because of reusing of RenderFrame.
diff --git a/android_webview/browser/js_java_interaction/js_to_java_messaging.h b/android_webview/browser/js_java_interaction/js_to_java_messaging.h
new file mode 100644
index 0000000..25197e1
--- /dev/null
+++ b/android_webview/browser/js_java_interaction/js_to_java_messaging.h
@@ -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.
+
+#ifndef ANDROID_WEBVIEW_BROWSER_JS_JAVA_INTERACTION_JS_TO_JAVA_MESSAGING_H_
+#define ANDROID_WEBVIEW_BROWSER_JS_JAVA_INTERACTION_JS_TO_JAVA_MESSAGING_H_
+
+#include <vector>
+
+#include "android_webview/browser/js_java_interaction/js_reply_proxy.h"
+#include "android_webview/common/js_java_interaction/interfaces.mojom.h"
+#include "base/android/scoped_java_ref.h"
+#include "base/strings/string16.h"
+#include "mojo/public/cpp/bindings/associated_receiver_set.h"
+#include "mojo/public/cpp/bindings/associated_remote.h"
+#include "mojo/public/cpp/bindings/pending_associated_receiver.h"
+#include "mojo/public/cpp/system/message_pipe.h"
+#include "net/proxy_resolution/proxy_bypass_rules.h"
+
+namespace content {
+class RenderFrameHost;
+}
+
+namespace android_webview {
+
+// Implementation of mojo::JsToJavaMessaging interface. Receives PostMessage()
+// call from renderer JsBinding.
+class JsToJavaMessaging : public mojom::JsToJavaMessaging {
+ public:
+  JsToJavaMessaging(
+      content::RenderFrameHost* rfh,
+      mojo::PendingAssociatedReceiver<mojom::JsToJavaMessaging> receiver,
+      base::android::ScopedJavaGlobalRef<jobject> listener_ref,
+      const net::ProxyBypassRules& allowed_origin_rules);
+  ~JsToJavaMessaging() override;
+
+  // mojom::JsToJavaMessaging implementation.
+  void PostMessage(const base::string16& message,
+                   std::vector<mojo::ScopedMessagePipeHandle> ports) override;
+  void SetJavaToJsMessaging(mojo::PendingRemote<mojom::JavaToJsMessaging>
+                                java_to_js_messaging) override;
+
+ private:
+  content::RenderFrameHost* render_frame_host_;
+  std::unique_ptr<JsReplyProxy> reply_proxy_;
+  base::android::ScopedJavaGlobalRef<jobject> listener_ref_;
+  net::ProxyBypassRules allowed_origin_rules_;
+  mojo::AssociatedReceiver<mojom::JsToJavaMessaging> receiver_{this};
+
+  DISALLOW_COPY_AND_ASSIGN(JsToJavaMessaging);
+};
+
+}  // namespace android_webview
+
+#endif  // ANDROID_WEBVIEW_BROWSER_JS_JAVA_INTERACTION_JS_TO_JAVA_MESSAGING_H_
diff --git a/android_webview/common/js_java_interaction/interfaces.mojom b/android_webview/common/js_java_interaction/interfaces.mojom
index 5a7ea23c..ec016b3 100644
--- a/android_webview/common/js_java_interaction/interfaces.mojom
+++ b/android_webview/common/js_java_interaction/interfaces.mojom
@@ -7,9 +7,20 @@
 import "mojo/public/mojom/base/string16.mojom";
 import "services/network/public/mojom/proxy_config.mojom";
 
+// JsObject struct represents a JavaScript object we will inject in the main
+// JavaScript world of a frame. |js_object_name| will be used as the name
+// of the JavaScript object. We will inject the object if the frame's origin
+// matches |allowed_origin_rules|. |js_to_java_messaging| will be used for
+// that JavaScript object to send message back to browser (hence Java) side.
+struct JsObject {
+  mojo_base.mojom.String16 js_object_name;
+  pending_associated_remote<JsToJavaMessaging> js_to_java_messaging;
+  network.mojom.ProxyBypassRules allowed_origin_rules;
+};
+
 // For JavaScript postMessage() API, implemented by browser.
 interface JsToJavaMessaging {
-  // Called from renderer, browser receives |message| and possible |ports|.
+  // Called from renderer, browser receives |message| and possible |ports|,
   // The |message| is an opaque type and the contents are defined by the client
   // of this API.
   PostMessage(mojo_base.mojom.String16 message,
@@ -30,10 +41,6 @@
 // For browser to configure renderer, implemented by renderer.
 interface JsJavaConfigurator {
   // Called from browser, to tell renderer that if we need to inject
-  // a JavaScript object with the given |js_object_name| based on
-  // |need_to_inject_js_object| flag. Only frames with the origin matches
-  // |allowed_origin_rules| will have the object injected.
-  SetJsApiService(bool need_to_inject_js_object,
-                  mojo_base.mojom.String16 js_object_name,
-                  network.mojom.ProxyBypassRules allowed_origin_rules);
+  // JavaScript objects to the frame based on the |js_objects| array.
+  SetJsObjects(array<android_webview.mojom.JsObject> js_objects);
 };
\ No newline at end of file
diff --git a/android_webview/java/src/org/chromium/android_webview/AwContents.java b/android_webview/java/src/org/chromium/android_webview/AwContents.java
index 2eb5e1d..ae0537c 100644
--- a/android_webview/java/src/org/chromium/android_webview/AwContents.java
+++ b/android_webview/java/src/org/chromium/android_webview/AwContents.java
@@ -102,7 +102,6 @@
 import org.chromium.content_public.common.Referrer;
 import org.chromium.content_public.common.UseZoomForDSFPolicy;
 import org.chromium.device.gamepad.GamepadList;
-import org.chromium.mojo.system.impl.CoreImpl;
 import org.chromium.net.NetworkChangeNotifier;
 import org.chromium.network.mojom.ReferrerPolicy;
 import org.chromium.ui.base.ActivityWindowAndroid;
@@ -400,7 +399,6 @@
     // This can be accessed on any thread after construction. See AwContentsIoThreadClient.
     private final AwSettings mSettings;
     private final ScrollAccessibilityHelper mScrollAccessibilityHelper;
-    private WebMessageListener mWebMessageListener;
 
     private final ObserverList<PopupTouchHandleDrawable> mTouchHandleDrawables =
             new ObserverList<>();
@@ -2482,16 +2480,13 @@
     }
 
     /**
-     * Controls if we need to inject a JavaScript object to receive postMessage() call, i.e. the
-     * {@link AwContents#onPostMessage} callback. Note that this call doesn't inject the JavaScript
-     * object immediately. We are going to do the actual injection until the next navigation
-     * (DidClearWindowObject) happens.
+     * Add the {@link WebMessageListener} to AwContents, it will also inject the JavaScript object
+     * with the given name to frames that have origins matching the allowedOriginRules. Note that
+     * this call will not inject the JS object immediately. The JS object will be injected only for
+     * future navigations (in DidClearWindowObject).
      *
-     * Caution: Setting a different {@code listener} or {@code allowedOriginRules} will make that
-     * new {@code listener} receives messages immediately if the message's origin also matches the
-     * new {@code allowedOriginRules}. {@code jsObjectName} will take effect since next navigation.
-     *
-     * @param jsObjectName    The name for the injected JavaScript object.
+     * @param jsObjectName    The name for the injected JavaScript object for this {@link
+     *                        WebMessageListener}.
      * @param allowedOrigins  A list of matching rules for the allowed origins.
      *                        The JavaScript object will be injected when the frame's origin matches
      *                        any one of the allowed origins. If a wildcard "*" is provided, it will
@@ -2499,13 +2494,13 @@
      * @param listener        The {@link WebMessageListener} to be called when received
      *                        onPostMessage().
      * @throws IllegalArgumentException if one of the allowedOriginRules is invalid or one of
-     *                                  listener, jsObjectName and allowedOriginRules is {@code
-     *                                  null}.
+     *                                  jsObjectName and allowedOriginRules is {@code null}.
+     * @throws NullPointerException if listener is {@code null}.
      */
-    public void setWebMessageListener(@NonNull String jsObjectName,
+    public void addWebMessageListener(@NonNull String jsObjectName,
             @NonNull String[] allowedOriginRules, @NonNull WebMessageListener listener) {
         if (listener == null) {
-            throw new IllegalArgumentException("listener shouldn't be null");
+            throw new NullPointerException("listener shouldn't be null");
         }
 
         if (TextUtils.isEmpty(jsObjectName)) {
@@ -2519,47 +2514,26 @@
             }
         }
 
-        mWebMessageListener = listener;
         final String exceptionMessage =
-                AwContentsJni.get().setJsApiService(mNativeAwContents, AwContents.this,
-                        /* needToInjectJsObject*/ true, jsObjectName, allowedOriginRules);
+                AwContentsJni.get().addWebMessageListener(mNativeAwContents, AwContents.this,
+                        new WebMessageListenerHolder(listener), jsObjectName, allowedOriginRules);
 
         if (!TextUtils.isEmpty(exceptionMessage)) {
-            mWebMessageListener = null;
             throw new IllegalArgumentException(exceptionMessage);
         }
     }
 
     /**
-     *  Removes the {@link WebMessageListener} sets by {@link setWebMessageListener}. It then won't
-     * inject JavaScript object for navigation since next navigation.
+     * Removes the {@link WebMessageListener} added by {@link addWebMessageListener}. This call will
+     * immediately remove the JavaScript object/WebMessageListener mapping pair. So any messages
+     * from the JavaScript object will be dropped. However the JavaScript object will only be
+     * removed for future navigations.
+     *
+     * @param listener The {@link WebMessageListener} to be removed. Can not be {@code null}.
      */
-    public void unsetWebMessageListener() {
-        mWebMessageListener = null;
-        AwContentsJni.get().setJsApiService(mNativeAwContents, AwContents.this,
-                /* needToInjectJsObject */ false, "", new String[0]);
-    }
-
-    /**
-     * Receives JavaScript postMessage from renderer side, passes the message to {@link
-     * WebMessageListener}.
-     */
-    @CalledByNative
-    public void onPostMessage(String message, String sourceOrigin, boolean isMainFrame, int[] ports,
-            JsReplyProxy replyProxy) {
-        if (mWebMessageListener == null) return;
-        MessagePort[] messagePorts = new MessagePort[ports.length];
-        for (int i = 0; i < ports.length; ++i) {
-            messagePorts[i] = convertRawHandleToMessagePort(ports[i]);
-        }
-
-        mWebMessageListener.onPostMessage(
-                message, Uri.parse(sourceOrigin), isMainFrame, replyProxy, messagePorts);
-    }
-
-    private static MessagePort convertRawHandleToMessagePort(int rawHandle) {
-        return MessagePort.create(
-                CoreImpl.getInstance().acquireNativeHandle(rawHandle).toMessagePipeHandle());
+    public void removeWebMessageListener(@NonNull String jsObjectName) {
+        AwContentsJni.get().removeWebMessageListener(
+                mNativeAwContents, AwContents.this, jsObjectName);
     }
 
     /**
@@ -4054,7 +4028,9 @@
         void grantFileSchemeAccesstoChildProcess(long nativeAwContents, AwContents caller);
         void resumeLoadingCreatedPopupWebContents(long nativeAwContents, AwContents caller);
         AwRenderProcess getRenderProcess(long nativeAwContents, AwContents caller);
-        String setJsApiService(long nativeAwContents, AwContents caller,
-                boolean needToInjectJsObject, String jsObjectName, String[] allowedOrigins);
+        String addWebMessageListener(long nativeAwContents, AwContents caller,
+                WebMessageListenerHolder listener, String jsObjectName, String[] allowedOrigins);
+        void removeWebMessageListener(
+                long nativeAwContents, AwContents caller, String jsObjectName);
     }
 }
diff --git a/android_webview/java/src/org/chromium/android_webview/WebMessageListenerHolder.java b/android_webview/java/src/org/chromium/android_webview/WebMessageListenerHolder.java
new file mode 100644
index 0000000..b730e16
--- /dev/null
+++ b/android_webview/java/src/org/chromium/android_webview/WebMessageListenerHolder.java
@@ -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.
+
+package org.chromium.android_webview;
+
+import android.net.Uri;
+import android.support.annotation.NonNull;
+
+import org.chromium.base.annotations.CalledByNative;
+import org.chromium.base.annotations.JNINamespace;
+import org.chromium.content_public.browser.MessagePort;
+import org.chromium.mojo.system.impl.CoreImpl;
+
+/**
+ * Holds the {@link WebMessageListener} instance so that C++ could interact with the {@link
+ * WebMessageListener}.
+ */
+@JNINamespace("android_webview")
+public class WebMessageListenerHolder {
+    private WebMessageListener mListener;
+
+    public WebMessageListenerHolder(@NonNull WebMessageListener listener) {
+        mListener = listener;
+    }
+
+    @CalledByNative
+    public void onPostMessage(String message, String sourceOrigin, boolean isMainFrame, int[] ports,
+            JsReplyProxy replyProxy) {
+        MessagePort[] messagePorts = new MessagePort[ports.length];
+        for (int i = 0; i < ports.length; ++i) {
+            messagePorts[i] = convertRawHandleToMessagePort(ports[i]);
+        }
+        mListener.onPostMessage(
+                message, Uri.parse(sourceOrigin), isMainFrame, replyProxy, messagePorts);
+    }
+
+    private static MessagePort convertRawHandleToMessagePort(int rawHandle) {
+        return MessagePort.create(
+                CoreImpl.getInstance().acquireNativeHandle(rawHandle).toMessagePipeHandle());
+    }
+}
diff --git a/android_webview/javatests/src/org/chromium/android_webview/test/JsJavaInteractionTest.java b/android_webview/javatests/src/org/chromium/android_webview/test/JsJavaInteractionTest.java
index d2e99dd..982b567 100644
--- a/android_webview/javatests/src/org/chromium/android_webview/test/JsJavaInteractionTest.java
+++ b/android_webview/javatests/src/org/chromium/android_webview/test/JsJavaInteractionTest.java
@@ -53,6 +53,7 @@
     private static final String HELLO = "Hello";
     private static final String NEW_TITLE = "new_title";
     private static final String JS_OBJECT_NAME = "myObject";
+    private static final String JS_OBJECT_NAME_2 = "myObject2";
     private static final String DATA_HTML = "<html><body>data</body></html>";
     private static final int MESSAGE_COUNT = 10000;
 
@@ -129,7 +130,7 @@
     @SmallTest
     @Feature({"AndroidWebView", "JsJavaInteraction"})
     public void testPostMessageSimple() throws Throwable {
-        setWebMessageListenerOnUiThread(mAwContents, mListener, new String[] {"*"});
+        addWebMessageListenerOnUiThread(mAwContents, JS_OBJECT_NAME, new String[] {"*"}, mListener);
 
         final String url = loadUrlFromPath(POST_MESSAGE_SIMPLE_HTML);
 
@@ -147,7 +148,7 @@
     @SmallTest
     @Feature({"AndroidWebView", "JsJavaInteraction"})
     public void testPostMessageWithPorts() throws Throwable {
-        setWebMessageListenerOnUiThread(mAwContents, mListener, new String[] {"*"});
+        addWebMessageListenerOnUiThread(mAwContents, JS_OBJECT_NAME, new String[] {"*"}, mListener);
 
         final String url = loadUrlFromPath(POST_MESSAGE_WITH_PORTS_HTML);
 
@@ -175,7 +176,7 @@
     @MediumTest
     @Feature({"AndroidWebView", "JsJavaInteraction"})
     public void testPostMessageRepeated() throws Throwable {
-        setWebMessageListenerOnUiThread(mAwContents, mListener, new String[] {"*"});
+        addWebMessageListenerOnUiThread(mAwContents, JS_OBJECT_NAME, new String[] {"*"}, mListener);
 
         final String url = loadUrlFromPath(POST_MESSAGE_REPEAT_HTML);
         for (int i = 0; i < MESSAGE_COUNT; ++i) {
@@ -191,7 +192,7 @@
     @SmallTest
     @Feature({"AndroidWebView", "JsJavaInteraction"})
     public void testPostMessageFromIframeWorks() throws Throwable {
-        setWebMessageListenerOnUiThread(mAwContents, mListener, new String[] {"*"});
+        addWebMessageListenerOnUiThread(mAwContents, JS_OBJECT_NAME, new String[] {"*"}, mListener);
 
         final String frameUrl = mTestServer.getURL(POST_MESSAGE_SIMPLE_HTML);
         final String html = createCrossOriginAccessTestPageHtml(frameUrl);
@@ -214,11 +215,11 @@
     @Test
     @SmallTest
     @Feature({"AndroidWebView", "JsJavaInterfaction"})
-    public void testSetWebMessageListenerAfterPageLoadWontAffectCurrentPage() throws Throwable {
+    public void testAddWebMessageListenerAfterPageLoadWontAffectCurrentPage() throws Throwable {
         loadUrlFromPath(POST_MESSAGE_SIMPLE_HTML);
 
-        // Set WebMessageListener after the page loaded won't affect the current page.
-        setWebMessageListenerOnUiThread(mAwContents, mListener, new String[] {"*"});
+        // Add WebMessageListener after the page loaded won't affect the current page.
+        addWebMessageListenerOnUiThread(mAwContents, JS_OBJECT_NAME, new String[] {"*"}, mListener);
 
         // Check that we don't have a JavaScript object named JS_OBJECT_NAME
         Assert.assertFalse(hasJavaScriptObject(
@@ -231,11 +232,31 @@
     @Test
     @SmallTest
     @Feature({"AndroidWebView", "JsJavaInterfaction"})
+    public void testAddTheSameWebMessageListenerForDifferentJsObjectsWorks() throws Throwable {
+        addWebMessageListenerOnUiThread(mAwContents, JS_OBJECT_NAME, new String[] {"*"}, mListener);
+        addWebMessageListenerOnUiThread(
+                mAwContents, JS_OBJECT_NAME_2, new String[] {"*"}, mListener);
+
+        loadUrlFromPath(POST_MESSAGE_SIMPLE_HTML);
+
+        TestWebMessageListener.Data data = mListener.waitForOnPostMessage();
+        Assert.assertEquals(HELLO, data.mMessage);
+
+        mActivityTestRule.executeJavaScriptAndWaitForResult(
+                mAwContents, mContentsClient, JS_OBJECT_NAME_2 + ".postMessage('" + HELLO + "');");
+
+        TestWebMessageListener.Data data2 = mListener.waitForOnPostMessage();
+        Assert.assertEquals(HELLO, data2.mMessage);
+    }
+
+    @Test
+    @SmallTest
+    @Feature({"AndroidWebView", "JsJavaInterfaction"})
     public void testFragmentNavigationWontDoJsInjection() throws Throwable {
         String url = loadUrlFromPath(POST_MESSAGE_SIMPLE_HTML);
 
-        // Set WebMessageListener after the page loaded won't affect the current page.
-        setWebMessageListenerOnUiThread(mAwContents, mListener, new String[] {"*"});
+        // Add WebMessageListener after the page loaded won't affect the current page.
+        addWebMessageListenerOnUiThread(mAwContents, JS_OBJECT_NAME, new String[] {"*"}, mListener);
 
         // Load with fragment url.
         mActivityTestRule.loadUrlSync(
@@ -252,44 +273,14 @@
     @Test
     @SmallTest
     @Feature({"AndroidWebView", "JsJavaInterfaction"})
-    public void testSetDifferentWebMessageListenerWillTakeEffectImmediately() throws Throwable {
-        setWebMessageListenerOnUiThread(mAwContents, mListener, new String[] {"*"});
-
-        loadUrlFromPath(POST_MESSAGE_SIMPLE_HTML);
-
-        // Verify the first listener receives message.
-        TestWebMessageListener.Data data = mListener.waitForOnPostMessage();
-        Assert.assertEquals(HELLO, data.mMessage);
-
-        // Set a second listener.
-        TestWebMessageListener secondListener = new TestWebMessageListener();
-        setWebMessageListenerOnUiThread(mAwContents, secondListener, new String[] {"*"});
-
-        // Call JavaScript postMessage.
-        TestThreadUtils.runOnUiThreadBlocking(
-                ()
-                        -> mAwContents.evaluateJavaScriptForTests(
-                                "myObject.postMessage('second')", null));
-
-        // Verify the second listener receives message.
-        TestWebMessageListener.Data data2 = secondListener.waitForOnPostMessage();
-        Assert.assertEquals("second", data2.mMessage);
-
-        Assert.assertTrue(mListener.hasNoMoreOnPostMessage());
-        Assert.assertTrue(secondListener.hasNoMoreOnPostMessage());
-    }
-
-    @Test
-    @SmallTest
-    @Feature({"AndroidWebView", "JsJavaInterfaction"})
-    public void testSetWebMessageListenerAffectsRendererInitiatedNavigation() throws Throwable {
+    public void testAddWebMessageListenerAffectsRendererInitiatedNavigation() throws Throwable {
         // TODO(crbug.com/969842): We'd either replace the following html file with a file contains
         // no JavaScript code or add a test to ensure that evaluateJavascript() won't
         // over-trigger DidClearWindowObject.
         loadUrlFromPath(POST_MESSAGE_WITH_PORTS_HTML);
 
-        // Set WebMessageListener after the page loaded.
-        setWebMessageListenerOnUiThread(mAwContents, mListener, new String[] {"*"});
+        // Add WebMessageListener after the page loaded.
+        addWebMessageListenerOnUiThread(mAwContents, JS_OBJECT_NAME, new String[] {"*"}, mListener);
 
         // Check that we don't have a JavaScript object named JS_OBJECT_NAME
         Assert.assertFalse(hasJavaScriptObject(
@@ -319,7 +310,7 @@
     @Test
     @SmallTest
     @Feature({"AndroidWebView", "JsJavaInterfaction"})
-    public void testSetWebMessageListenerWontAffectOtherAwContents() throws Throwable {
+    public void testAddWebMessageListenerWontAffectOtherAwContents() throws Throwable {
         // Create another AwContents object.
         final TestAwContentsClient awContentsClient = new TestAwContentsClient();
         final AwTestContainerView awTestContainerView =
@@ -327,7 +318,7 @@
         final AwContents otherAwContents = awTestContainerView.getAwContents();
         AwActivityTestRule.enableJavaScriptOnUiThread(otherAwContents);
 
-        setWebMessageListenerOnUiThread(mAwContents, mListener, new String[] {"*"});
+        addWebMessageListenerOnUiThread(mAwContents, JS_OBJECT_NAME, new String[] {"*"}, mListener);
 
         final String url = mTestServer.getURL(POST_MESSAGE_SIMPLE_HTML);
         mActivityTestRule.loadUrlSync(mAwContents, mContentsClient.getOnPageFinishedHelper(), url);
@@ -351,11 +342,11 @@
     @Test
     @SmallTest
     @Feature({"AndroidWebView", "JsJavaInterfaction"})
-    public void testSetWebMessageListenerAllowsCertainUrlWorksWithIframe() throws Throwable {
+    public void testAddWebMessageListenerAllowsCertainUrlWorksWithIframe() throws Throwable {
         final String frameUrl = mTestServer.getURL(POST_MESSAGE_SIMPLE_HTML);
         final String html = createCrossOriginAccessTestPageHtml(frameUrl);
-        setWebMessageListenerOnUiThread(
-                mAwContents, mListener, new String[] {parseOrigin(frameUrl)});
+        addWebMessageListenerOnUiThread(
+                mAwContents, JS_OBJECT_NAME, new String[] {parseOrigin(frameUrl)}, mListener);
 
         mActivityTestRule.loadDataWithBaseUrlSync(mAwContents,
                 mContentsClient.getOnPageFinishedHelper(), html, "text/html", false,
@@ -375,8 +366,8 @@
     @Test
     @SmallTest
     @Feature({"AndroidWebView", "JsJavaInterfaction"})
-    public void testUnsetWebMessageListenerCouldPreventInjectionForNextPageLoad() throws Throwable {
-        setWebMessageListenerOnUiThread(mAwContents, mListener, new String[] {"*"});
+    public void testRemoveWebMessageListener_preventInjectionForNextPageLoad() throws Throwable {
+        addWebMessageListenerOnUiThread(mAwContents, JS_OBJECT_NAME, new String[] {"*"}, mListener);
 
         // Load the the page.
         loadUrlFromPath(POST_MESSAGE_SIMPLE_HTML);
@@ -387,7 +378,7 @@
         Assert.assertTrue(mListener.hasNoMoreOnPostMessage());
 
         // Remove WebMessageListener will disable injection for next page load.
-        TestThreadUtils.runOnUiThreadBlocking(() -> mAwContents.unsetWebMessageListener());
+        removeWebMessageListenerOnUiThread(mAwContents, JS_OBJECT_NAME);
 
         loadUrlFromPath(POST_MESSAGE_SIMPLE_HTML);
 
@@ -397,46 +388,125 @@
     }
 
     @Test
+    @SmallTest
+    @Feature({"AndroidWebView", "JsJavaInterfaction"})
+    public void testRemoveWebMessageListener_cutJsJavaMappingImmediately() throws Throwable {
+        addWebMessageListenerOnUiThread(mAwContents, JS_OBJECT_NAME, new String[] {"*"}, mListener);
+
+        // Load the the page.
+        loadUrlFromPath(POST_MESSAGE_SIMPLE_HTML);
+
+        TestWebMessageListener.Data data = mListener.waitForOnPostMessage();
+        Assert.assertEquals(HELLO, data.mMessage);
+
+        Assert.assertTrue(mListener.hasNoMoreOnPostMessage());
+
+        // Remove WebMessageListener will disable injection for next page load.
+        removeWebMessageListenerOnUiThread(mAwContents, JS_OBJECT_NAME);
+
+        // Should still have myObject.
+        Assert.assertTrue(hasJavaScriptObject(
+                JS_OBJECT_NAME, mActivityTestRule, mAwContents, mContentsClient));
+
+        // But posting message on myObject will be dropped.
+        mActivityTestRule.executeJavaScriptAndWaitForResult(
+                mAwContents, mContentsClient, JS_OBJECT_NAME + ".postMessage('" + HELLO + "');");
+        Assert.assertTrue(mListener.hasNoMoreOnPostMessage());
+    }
+
+    @Test
+    @SmallTest
+    @Feature({"AndroidWebView", "JsJavaInterfaction"})
+    public void testRemoveWebMessageListener_removeWithNoAddWebMessageListener() throws Throwable {
+        // Call removeWebMessageListener() without addWebMessageListener() shouldn't fail.
+        removeWebMessageListenerOnUiThread(mAwContents, JS_OBJECT_NAME);
+
+        loadUrlFromPath(POST_MESSAGE_SIMPLE_HTML);
+    }
+
+    @Test
+    @SmallTest
+    @Feature({"AndroidWebView", "JsJavaInterfaction"})
+    public void testRemoveWebMessageListener_removeBeforeLoadPage() throws Throwable {
+        addWebMessageListenerOnUiThread(mAwContents, JS_OBJECT_NAME, new String[] {"*"}, mListener);
+        removeWebMessageListenerOnUiThread(mAwContents, JS_OBJECT_NAME);
+
+        loadUrlFromPath(POST_MESSAGE_SIMPLE_HTML);
+
+        Assert.assertFalse(hasJavaScriptObject(
+                JS_OBJECT_NAME, mActivityTestRule, mAwContents, mContentsClient));
+    }
+
+    @Test
+    @SmallTest
+    @Feature({"AndroidWebView", "JsJavaInterfaction"})
+    public void testRemoveWebMessageListener_extraRemove() throws Throwable {
+        addWebMessageListenerOnUiThread(mAwContents, JS_OBJECT_NAME, new String[] {"*"}, mListener);
+        removeWebMessageListenerOnUiThread(mAwContents, JS_OBJECT_NAME);
+        // Extra removeWebMessageListener() does nothing.
+        removeWebMessageListenerOnUiThread(mAwContents, JS_OBJECT_NAME);
+
+        loadUrlFromPath(POST_MESSAGE_SIMPLE_HTML);
+
+        Assert.assertFalse(hasJavaScriptObject(
+                JS_OBJECT_NAME, mActivityTestRule, mAwContents, mContentsClient));
+    }
+
+    @Test
     @MediumTest
     @Feature({"AndroidWebView", "JsJavaInterfaction"})
     public void testAllowedOriginsWorksForVariousBaseUrls() throws Throwable {
         // Set a typical rule.
-        setWebMessageListenerOnUiThread(
-                mAwContents, mListener, new String[] {"https://www.example.com:443"});
+        addWebMessageListenerOnUiThread(mAwContents, JS_OBJECT_NAME,
+                new String[] {"https://www.example.com:443"}, mListener);
 
-        Assert.assertTrue(isJsObjectInjectedWhenLoadingUrl("https://www.example.com"));
-        Assert.assertFalse(isJsObjectInjectedWhenLoadingUrl("https://www.example.com:8080"));
-        Assert.assertFalse(isJsObjectInjectedWhenLoadingUrl("http://www.example.com"));
-        Assert.assertFalse(isJsObjectInjectedWhenLoadingUrl("http://example.com"));
-        Assert.assertFalse(isJsObjectInjectedWhenLoadingUrl("https://www.google.com"));
-        Assert.assertFalse(isJsObjectInjectedWhenLoadingUrl("file://etc"));
-        Assert.assertFalse(isJsObjectInjectedWhenLoadingUrl("ftp://example.com"));
-        Assert.assertFalse(isJsObjectInjectedWhenLoadingUrl(null));
+        Assert.assertTrue(
+                isJsObjectInjectedWhenLoadingUrl("https://www.example.com", JS_OBJECT_NAME));
+        Assert.assertFalse(
+                isJsObjectInjectedWhenLoadingUrl("https://www.example.com:8080", JS_OBJECT_NAME));
+        Assert.assertFalse(
+                isJsObjectInjectedWhenLoadingUrl("http://www.example.com", JS_OBJECT_NAME));
+        Assert.assertFalse(isJsObjectInjectedWhenLoadingUrl("http://example.com", JS_OBJECT_NAME));
+        Assert.assertFalse(
+                isJsObjectInjectedWhenLoadingUrl("https://www.google.com", JS_OBJECT_NAME));
+        Assert.assertFalse(isJsObjectInjectedWhenLoadingUrl("file://etc", JS_OBJECT_NAME));
+        Assert.assertFalse(isJsObjectInjectedWhenLoadingUrl("ftp://example.com", JS_OBJECT_NAME));
+        Assert.assertFalse(isJsObjectInjectedWhenLoadingUrl(null, JS_OBJECT_NAME));
 
         // Inject to all frames.
-        setWebMessageListenerOnUiThread(mAwContents, mListener, new String[] {"*"});
+        addWebMessageListenerOnUiThread(
+                mAwContents, JS_OBJECT_NAME_2, new String[] {"*"}, mListener);
 
-        Assert.assertTrue(isJsObjectInjectedWhenLoadingUrl("https://www.example.com"));
-        Assert.assertTrue(isJsObjectInjectedWhenLoadingUrl("https://www.example.com:8080"));
-        Assert.assertTrue(isJsObjectInjectedWhenLoadingUrl("http://www.example.com"));
-        Assert.assertTrue(isJsObjectInjectedWhenLoadingUrl("http://example.com"));
-        Assert.assertTrue(isJsObjectInjectedWhenLoadingUrl("https://www.google.com"));
-        Assert.assertTrue(isJsObjectInjectedWhenLoadingUrl("file://etc"));
-        Assert.assertTrue(isJsObjectInjectedWhenLoadingUrl("ftp://example.com"));
-        Assert.assertTrue(isJsObjectInjectedWhenLoadingUrl(null));
+        Assert.assertTrue(
+                isJsObjectInjectedWhenLoadingUrl("https://www.example.com", JS_OBJECT_NAME_2));
+        Assert.assertTrue(
+                isJsObjectInjectedWhenLoadingUrl("https://www.example.com:8080", JS_OBJECT_NAME_2));
+        Assert.assertTrue(
+                isJsObjectInjectedWhenLoadingUrl("http://www.example.com", JS_OBJECT_NAME_2));
+        Assert.assertTrue(isJsObjectInjectedWhenLoadingUrl("http://example.com", JS_OBJECT_NAME_2));
+        Assert.assertTrue(
+                isJsObjectInjectedWhenLoadingUrl("https://www.google.com", JS_OBJECT_NAME_2));
+        Assert.assertTrue(isJsObjectInjectedWhenLoadingUrl("file://etc", JS_OBJECT_NAME_2));
+        Assert.assertTrue(isJsObjectInjectedWhenLoadingUrl("ftp://example.com", JS_OBJECT_NAME_2));
+        Assert.assertTrue(isJsObjectInjectedWhenLoadingUrl(null, JS_OBJECT_NAME_2));
 
         // ftp scheme.
-        setWebMessageListenerOnUiThread(mAwContents, mListener, new String[] {"ftp://example.com"});
-        Assert.assertTrue(isJsObjectInjectedWhenLoadingUrl("ftp://example.com"));
+        final String jsObjectName3 = JS_OBJECT_NAME + "3";
+        addWebMessageListenerOnUiThread(
+                mAwContents, jsObjectName3, new String[] {"ftp://example.com"}, mListener);
+        Assert.assertTrue(isJsObjectInjectedWhenLoadingUrl("ftp://example.com", jsObjectName3));
 
         // file scheme.
-        setWebMessageListenerOnUiThread(mAwContents, mListener, new String[] {"file://*"});
-        Assert.assertTrue(isJsObjectInjectedWhenLoadingUrl("file://etc"));
+        final String jsObjectName4 = JS_OBJECT_NAME + "4";
+        addWebMessageListenerOnUiThread(
+                mAwContents, jsObjectName4, new String[] {"file://*"}, mListener);
+        Assert.assertTrue(isJsObjectInjectedWhenLoadingUrl("file://etc", jsObjectName4));
 
         // Pass an URI instead of origin shouldn't work.
+        final String jsObjectName5 = JS_OBJECT_NAME + "5";
         try {
-            setWebMessageListenerOnUiThread(
-                    mAwContents, mListener, new String[] {"https://www.example.com/index.html"});
+            addWebMessageListenerOnUiThread(mAwContents, jsObjectName5,
+                    new String[] {"https://www.example.com/index.html"}, mListener);
             Assert.fail("allowedOriginRules shouldn't be url like");
         } catch (RuntimeException e) {
             // Should catch IllegalArgumentException in the end of the re-throw chain.
@@ -446,82 +516,137 @@
             }
             Assert.assertTrue(ex instanceof IllegalArgumentException);
         }
-        Assert.assertFalse(isJsObjectInjectedWhenLoadingUrl("https://www.example.com"));
+        Assert.assertFalse(
+                isJsObjectInjectedWhenLoadingUrl("https://www.example.com", jsObjectName5));
     }
 
     @Test
     @MediumTest
     @Feature({"AndroidWebView", "JsJavaInterfaction"})
-    public void testCallSetWebMessageListenerAgainForTheSameOrigins() throws Throwable {
+    public void testDontAllowAddWebMessageLitenerWithTheSameJsObjectName() {
+        addWebMessageListenerOnUiThread(mAwContents, JS_OBJECT_NAME, new String[] {"*"}, mListener);
+        try {
+            addWebMessageListenerOnUiThread(
+                    mAwContents, JS_OBJECT_NAME, new String[] {"*"}, new TestWebMessageListener());
+            Assert.fail("Shouldn't allow the same Js object name be added more than once.");
+        } catch (RuntimeException e) {
+            // Should catch IllegalArgumentException in the end of the re-throw chain.
+            Throwable ex = e;
+            while (ex.getCause() != null) {
+                ex = ex.getCause();
+            }
+            Assert.assertTrue(ex instanceof IllegalArgumentException);
+        }
+    }
+
+    @Test
+    @MediumTest
+    @Feature({"AndroidWebView", "JsJavaInterfaction"})
+    public void testAddWebMessageListener_SameOrigins() throws Throwable {
         final String[] allowedOriginRules =
                 new String[] {"https://www.example.com", "https://www.allowed.com"};
-        setWebMessageListenerOnUiThread(mAwContents, mListener, allowedOriginRules);
+        addWebMessageListenerOnUiThread(mAwContents, JS_OBJECT_NAME, allowedOriginRules, mListener);
 
-        Assert.assertTrue(isJsObjectInjectedWhenLoadingUrl("https://www.example.com"));
-        Assert.assertTrue(isJsObjectInjectedWhenLoadingUrl("https://www.allowed.com"));
-        Assert.assertFalse(isJsObjectInjectedWhenLoadingUrl("https://www.noinjection.com"));
+        Assert.assertTrue(
+                isJsObjectInjectedWhenLoadingUrl("https://www.example.com", JS_OBJECT_NAME));
+        Assert.assertTrue(
+                isJsObjectInjectedWhenLoadingUrl("https://www.allowed.com", JS_OBJECT_NAME));
+        Assert.assertFalse(
+                isJsObjectInjectedWhenLoadingUrl("https://www.noinjection.com", JS_OBJECT_NAME));
 
-        // Call setWebMessageListener() with the same set of origins.
-        setWebMessageListenerOnUiThread(mAwContents, mListener, allowedOriginRules);
+        // Call addWebMessageListener() with the same set of origins.
+        addWebMessageListenerOnUiThread(
+                mAwContents, JS_OBJECT_NAME_2, allowedOriginRules, mListener);
 
-        Assert.assertTrue(isJsObjectInjectedWhenLoadingUrl("https://www.example.com"));
-        Assert.assertTrue(isJsObjectInjectedWhenLoadingUrl("https://www.allowed.com"));
-        Assert.assertFalse(isJsObjectInjectedWhenLoadingUrl("https://www.noinjection.com"));
+        Assert.assertTrue(
+                isJsObjectInjectedWhenLoadingUrl("https://www.example.com", JS_OBJECT_NAME_2));
+        Assert.assertTrue(
+                isJsObjectInjectedWhenLoadingUrl("https://www.allowed.com", JS_OBJECT_NAME_2));
+        Assert.assertFalse(
+                isJsObjectInjectedWhenLoadingUrl("https://www.noinjection.com", JS_OBJECT_NAME_2));
     }
 
     @Test
     @MediumTest
     @Feature({"AndroidWebView", "JsJavaInterfaction"})
-    public void testCallSetWebMessageListenerAgainForOverlappingSetOfOrigins() throws Throwable {
+    public void testAddWebMessageListener_OverlappingSetOfOrigins() throws Throwable {
         final String[] allowedOriginRules1 =
                 new String[] {"https://www.example.com", "https://www.allowed.com"};
-        setWebMessageListenerOnUiThread(mAwContents, mListener, allowedOriginRules1);
+        addWebMessageListenerOnUiThread(
+                mAwContents, JS_OBJECT_NAME, allowedOriginRules1, mListener);
 
-        Assert.assertTrue(isJsObjectInjectedWhenLoadingUrl("https://www.example.com"));
-        Assert.assertTrue(isJsObjectInjectedWhenLoadingUrl("https://www.allowed.com"));
-        Assert.assertFalse(isJsObjectInjectedWhenLoadingUrl("https://www.ok.com"));
-        Assert.assertFalse(isJsObjectInjectedWhenLoadingUrl("https://www.noinjection.com"));
+        Assert.assertTrue(
+                isJsObjectInjectedWhenLoadingUrl("https://www.example.com", JS_OBJECT_NAME));
+        Assert.assertTrue(
+                isJsObjectInjectedWhenLoadingUrl("https://www.allowed.com", JS_OBJECT_NAME));
+        Assert.assertFalse(isJsObjectInjectedWhenLoadingUrl("https://www.ok.com", JS_OBJECT_NAME));
+        Assert.assertFalse(
+                isJsObjectInjectedWhenLoadingUrl("https://www.noinjection.com", JS_OBJECT_NAME));
 
         final String[] allowedOriginRules2 =
                 new String[] {"https://www.example.com", "https://www.ok.com"};
-        // Call setWebMessageListener with overlapping set of origins.
-        setWebMessageListenerOnUiThread(mAwContents, mListener, allowedOriginRules2);
+        // Call addWebMessageListener with overlapping set of origins.
+        addWebMessageListenerOnUiThread(
+                mAwContents, JS_OBJECT_NAME_2, allowedOriginRules2, mListener);
 
-        Assert.assertTrue(isJsObjectInjectedWhenLoadingUrl("https://www.example.com"));
-        Assert.assertFalse(isJsObjectInjectedWhenLoadingUrl("https://www.allowed.com"));
-        Assert.assertTrue(isJsObjectInjectedWhenLoadingUrl("https://www.ok.com"));
-        Assert.assertFalse(isJsObjectInjectedWhenLoadingUrl("https://www.noinjection.com"));
+        Assert.assertTrue(
+                isJsObjectInjectedWhenLoadingUrl("https://www.example.com", JS_OBJECT_NAME_2));
+        Assert.assertFalse(
+                isJsObjectInjectedWhenLoadingUrl("https://www.allowed.com", JS_OBJECT_NAME_2));
+        Assert.assertTrue(isJsObjectInjectedWhenLoadingUrl("https://www.ok.com", JS_OBJECT_NAME_2));
+        Assert.assertFalse(
+                isJsObjectInjectedWhenLoadingUrl("https://www.noinjection.com", JS_OBJECT_NAME_2));
+
+        // Remove the listener should remove the js object from the next navigation.
+        removeWebMessageListenerOnUiThread(mAwContents, JS_OBJECT_NAME_2);
+
+        Assert.assertFalse(
+                isJsObjectInjectedWhenLoadingUrl("https://www.example.com", JS_OBJECT_NAME_2));
+        Assert.assertFalse(
+                isJsObjectInjectedWhenLoadingUrl("https://www.allowed.com", JS_OBJECT_NAME_2));
+        Assert.assertFalse(
+                isJsObjectInjectedWhenLoadingUrl("https://www.ok.com", JS_OBJECT_NAME_2));
+        Assert.assertFalse(
+                isJsObjectInjectedWhenLoadingUrl("https://www.noinjection.com", JS_OBJECT_NAME_2));
     }
 
     @Test
     @MediumTest
     @Feature({"AndroidWebView", "JsJavaInterfaction"})
-    public void testCallSetWebMessageListenerAgainForNonOverlappingSetOfOrigins() throws Throwable {
+    public void testAddWebMessageListener_NonOverlappingSetOfOrigins() throws Throwable {
         final String[] allowedOriginRules1 =
                 new String[] {"https://www.example.com", "https://www.allowed.com"};
-        setWebMessageListenerOnUiThread(mAwContents, mListener, allowedOriginRules1);
+        addWebMessageListenerOnUiThread(
+                mAwContents, JS_OBJECT_NAME, allowedOriginRules1, mListener);
 
-        Assert.assertTrue(isJsObjectInjectedWhenLoadingUrl("https://www.example.com"));
-        Assert.assertTrue(isJsObjectInjectedWhenLoadingUrl("https://www.allowed.com"));
-        Assert.assertFalse(isJsObjectInjectedWhenLoadingUrl("https://www.ok.com"));
-        Assert.assertFalse(isJsObjectInjectedWhenLoadingUrl("https://www.noinjection.com"));
+        Assert.assertTrue(
+                isJsObjectInjectedWhenLoadingUrl("https://www.example.com", JS_OBJECT_NAME));
+        Assert.assertTrue(
+                isJsObjectInjectedWhenLoadingUrl("https://www.allowed.com", JS_OBJECT_NAME));
+        Assert.assertFalse(isJsObjectInjectedWhenLoadingUrl("https://www.ok.com", JS_OBJECT_NAME));
+        Assert.assertFalse(
+                isJsObjectInjectedWhenLoadingUrl("https://www.noinjection.com", JS_OBJECT_NAME));
 
         final String[] allowedOriginRules2 = new String[] {"https://www.ok.com"};
-        // Call setWebMessageListener with non-overlapping set of origins.
-        setWebMessageListenerOnUiThread(mAwContents, mListener, allowedOriginRules2);
+        // Call addWebMessageListener with non-overlapping set of origins.
+        addWebMessageListenerOnUiThread(
+                mAwContents, JS_OBJECT_NAME_2, allowedOriginRules2, mListener);
 
-        Assert.assertFalse(isJsObjectInjectedWhenLoadingUrl("https://www.example.com"));
-        Assert.assertFalse(isJsObjectInjectedWhenLoadingUrl("https://www.allowed.com"));
-        Assert.assertTrue(isJsObjectInjectedWhenLoadingUrl("https://www.ok.com"));
-        Assert.assertFalse(isJsObjectInjectedWhenLoadingUrl("https://www.noinjection.com"));
-        Assert.assertFalse(isJsObjectInjectedWhenLoadingUrl(""));
+        Assert.assertFalse(
+                isJsObjectInjectedWhenLoadingUrl("https://www.example.com", JS_OBJECT_NAME_2));
+        Assert.assertFalse(
+                isJsObjectInjectedWhenLoadingUrl("https://www.allowed.com", JS_OBJECT_NAME_2));
+        Assert.assertTrue(isJsObjectInjectedWhenLoadingUrl("https://www.ok.com", JS_OBJECT_NAME_2));
+        Assert.assertFalse(
+                isJsObjectInjectedWhenLoadingUrl("https://www.noinjection.com", JS_OBJECT_NAME_2));
+        Assert.assertFalse(isJsObjectInjectedWhenLoadingUrl("", JS_OBJECT_NAME_2));
     }
 
     @Test
     @MediumTest
     @Feature({"AndroidWebView", "JsJavaInterfaction"})
     public void testJsReplyProxyWorks() throws Throwable {
-        setWebMessageListenerOnUiThread(mAwContents, mListener, new String[] {"*"});
+        addWebMessageListenerOnUiThread(mAwContents, JS_OBJECT_NAME, new String[] {"*"}, mListener);
 
         final String url = loadUrlFromPath(POST_MESSAGE_REPLY_HTML);
 
@@ -542,9 +667,71 @@
 
     @Test
     @MediumTest
+    @Feature({"AndroidWebView", "JsJavaInterfaction"})
+    public void testJsReplyProxyReplyToTheCorrectJsObject() throws Throwable {
+        final TestWebMessageListener webMessageListener2 = new TestWebMessageListener();
+        addWebMessageListenerOnUiThread(mAwContents, JS_OBJECT_NAME, new String[] {"*"}, mListener);
+        addWebMessageListenerOnUiThread(
+                mAwContents, JS_OBJECT_NAME_2, new String[] {"*"}, webMessageListener2);
+
+        final String url = loadUrlFromPath(POST_MESSAGE_REPLY_HTML);
+
+        // Listener for myObject.
+        final String listener1 = "function (event) {"
+                + "  if (window.listenerResult1) {"
+                + "    window.listenerResult1 += event.data;"
+                + "  } else {"
+                + "    window.listenerResult1 = event.data;"
+                + "  }"
+                + "}";
+
+        // Listener for myObject2.
+        final String listener2 = "function (event) {"
+                + "  if (window.listenerResult2) {"
+                + "    window.listenerResult2 += event.data;"
+                + "  } else {"
+                + "    window.listenerResult2 = event.data;"
+                + "  }"
+                + "}";
+
+        // Add two different js objects.
+        addEventListener(listener1, "listener1", JS_OBJECT_NAME, mActivityTestRule, mAwContents,
+                mContentsClient);
+        addEventListener(listener2, "listener2", JS_OBJECT_NAME_2, mActivityTestRule, mAwContents,
+                mContentsClient);
+
+        TestWebMessageListener.Data data = mListener.waitForOnPostMessage();
+
+        final String message = "message";
+        mActivityTestRule.executeJavaScriptAndWaitForResult(mAwContents, mContentsClient,
+                JS_OBJECT_NAME_2 + ".postMessage('" + message + "');");
+        TestWebMessageListener.Data data2 = webMessageListener2.waitForOnPostMessage();
+
+        Assert.assertEquals(message, data2.mMessage);
+
+        final String hello1 = "hello1";
+        final String hello2 = "hello2";
+        // Targeting myObject.
+        data.mReplyProxy.postMessage(hello1);
+        // Targeting myObject2.
+        data2.mReplyProxy.postMessage(hello2);
+
+        Assert.assertEquals(hello1,
+                getJsObjectValue(
+                        "window.listenerResult1", mActivityTestRule, mAwContents, mContentsClient));
+        Assert.assertEquals(hello2,
+                getJsObjectValue(
+                        "window.listenerResult2", mActivityTestRule, mAwContents, mContentsClient));
+
+        Assert.assertTrue(mListener.hasNoMoreOnPostMessage());
+        Assert.assertTrue(webMessageListener2.hasNoMoreOnPostMessage());
+    }
+
+    @Test
+    @MediumTest
     @Feature({"AndroidWebView", "JsJavaInteraction"})
     public void testJsReplyProxyDropsMessageIfJsObjectIsGone() throws Throwable {
-        setWebMessageListenerOnUiThread(mAwContents, mListener, new String[] {"*"});
+        addWebMessageListenerOnUiThread(mAwContents, JS_OBJECT_NAME, new String[] {"*"}, mListener);
 
         final String url = loadUrlFromPath(POST_MESSAGE_REPLY_HTML);
 
@@ -574,7 +761,7 @@
     @MediumTest
     @Feature({"AndroidWebView", "JsJavaInteraction"})
     public void testJsAddAndRemoveEventListener() throws Throwable {
-        setWebMessageListenerOnUiThread(mAwContents, mListener, new String[] {"*"});
+        addWebMessageListenerOnUiThread(mAwContents, JS_OBJECT_NAME, new String[] {"*"}, mListener);
         loadUrlFromPath(POST_MESSAGE_SIMPLE_HTML);
 
         JsReplyProxy proxy = mListener.waitForOnPostMessage().mReplyProxy;
@@ -631,7 +818,7 @@
     @MediumTest
     @Feature({"AndroidWebView", "JsJavaInterfaction"})
     public void testJsObjectRemoveOnMessage() throws Throwable {
-        setWebMessageListenerOnUiThread(mAwContents, mListener, new String[] {"*"});
+        addWebMessageListenerOnUiThread(mAwContents, JS_OBJECT_NAME, new String[] {"*"}, mListener);
 
         final String url = loadUrlFromPath(POST_MESSAGE_REPLY_HTML);
 
@@ -656,11 +843,12 @@
         Assert.assertTrue(mListener.hasNoMoreOnPostMessage());
     }
 
-    private boolean isJsObjectInjectedWhenLoadingUrl(final String baseUrl) throws Throwable {
+    private boolean isJsObjectInjectedWhenLoadingUrl(
+            final String baseUrl, final String jsObjectName) throws Throwable {
         mActivityTestRule.loadDataWithBaseUrlSync(mAwContents,
                 mContentsClient.getOnPageFinishedHelper(), DATA_HTML, "text/html", false, baseUrl,
                 null);
-        return hasJavaScriptObject(JS_OBJECT_NAME, mActivityTestRule, mAwContents, mContentsClient);
+        return hasJavaScriptObject(jsObjectName, mActivityTestRule, mAwContents, mContentsClient);
     }
 
     private String loadUrlFromPath(String path) throws Exception {
@@ -683,12 +871,17 @@
                 + "</body></html>";
     }
 
-    private static void setWebMessageListenerOnUiThread(final AwContents awContents,
-            final WebMessageListener listener, final String[] allowedOriginRules) {
+    private static void addWebMessageListenerOnUiThread(final AwContents awContents,
+            final String jsObjectName, final String[] allowedOriginRules,
+            final WebMessageListener listener) {
         TestThreadUtils.runOnUiThreadBlocking(
-                ()
-                        -> awContents.setWebMessageListener(
-                                JS_OBJECT_NAME, allowedOriginRules, listener));
+                () -> awContents.addWebMessageListener(jsObjectName, allowedOriginRules, listener));
+    }
+
+    private static void removeWebMessageListenerOnUiThread(
+            final AwContents awContents, final String jsObjectName) {
+        TestThreadUtils.runOnUiThreadBlocking(
+                () -> awContents.removeWebMessageListener(jsObjectName));
     }
 
     private static boolean hasJavaScriptObject(final String jsObjectName,
diff --git a/android_webview/renderer/js_java_interaction/js_binding.cc b/android_webview/renderer/js_java_interaction/js_binding.cc
index eef30dc..9a1e1df 100644
--- a/android_webview/renderer/js_java_interaction/js_binding.cc
+++ b/android_webview/renderer/js_java_interaction/js_binding.cc
@@ -6,6 +6,7 @@
 
 #include <vector>
 
+#include "android_webview/renderer/js_java_interaction/js_java_configurator.h"
 #include "base/strings/string_util.h"
 #include "content/public/renderer/render_frame.h"
 #include "gin/data_object_builder.h"
@@ -35,7 +36,8 @@
 // static
 std::unique_ptr<JsBinding> JsBinding::Install(
     content::RenderFrame* render_frame,
-    const base::string16& js_object_name) {
+    const base::string16& js_object_name,
+    JsJavaConfigurator* js_java_configurator) {
   CHECK(!js_object_name.empty())
       << "JavaScript wrapper name shouldn't be empty";
 
@@ -47,7 +49,8 @@
     return nullptr;
 
   v8::Context::Scope context_scope(context);
-  std::unique_ptr<JsBinding> js_binding(new JsBinding(render_frame));
+  std::unique_ptr<JsBinding> js_binding(
+      new JsBinding(render_frame, js_object_name, js_java_configurator));
   gin::Handle<JsBinding> bindings =
       gin::CreateHandle(isolate, js_binding.get());
   if (bindings.IsEmpty())
@@ -63,12 +66,18 @@
   return js_binding;
 }
 
-JsBinding::JsBinding(content::RenderFrame* render_frame)
-    : render_frame_(render_frame) {
-  render_frame->GetRemoteAssociatedInterfaces()->GetInterface(
-      &js_to_java_messaging_);
-  js_to_java_messaging_->SetJavaToJsMessaging(
-      receiver_.BindNewPipeAndPassRemote());
+JsBinding::JsBinding(content::RenderFrame* render_frame,
+                     const base::string16& js_object_name,
+                     JsJavaConfigurator* js_java_configurator)
+    : render_frame_(render_frame),
+      js_object_name_(js_object_name),
+      js_java_configurator_(js_java_configurator) {
+  mojom::JsToJavaMessaging* js_to_java_messaging =
+      js_java_configurator_->GetJsToJavaMessage(js_object_name_);
+  if (js_to_java_messaging) {
+    js_to_java_messaging->SetJavaToJsMessaging(
+        receiver_.BindNewPipeAndPassRemote());
+  }
 }
 
 JsBinding::~JsBinding() = default;
@@ -153,8 +162,12 @@
     ports.emplace_back(port.value());
   }
 
-  js_to_java_messaging_->PostMessage(
-      message, blink::MessagePortChannel::ReleaseHandles(ports));
+  mojom::JsToJavaMessaging* js_to_java_messaging =
+      js_java_configurator_->GetJsToJavaMessage(js_object_name_);
+  if (js_to_java_messaging) {
+    js_to_java_messaging->PostMessage(
+        message, blink::MessagePortChannel::ReleaseHandles(ports));
+  }
 }
 
 // AddEventListener() needs to match EventTarget's AddEventListener() in blink.
diff --git a/android_webview/renderer/js_java_interaction/js_binding.h b/android_webview/renderer/js_java_interaction/js_binding.h
index b6e6138..cb0d57c 100644
--- a/android_webview/renderer/js_java_interaction/js_binding.h
+++ b/android_webview/renderer/js_java_interaction/js_binding.h
@@ -26,6 +26,7 @@
 }  // namespace content
 
 namespace android_webview {
+class JsJavaConfigurator;
 // A gin::Wrappable class used for providing JavaScript API. We will inject the
 // object of this class to JavaScript world in JsJavaConfigurator.
 // JsJavaConfigurator will own at most one instance of this class. When the
@@ -38,7 +39,8 @@
 
   static std::unique_ptr<JsBinding> Install(
       content::RenderFrame* render_frame,
-      const base::string16& js_object_name);
+      const base::string16& js_object_name,
+      JsJavaConfigurator* js_java_configurator);
 
   // mojom::JavaToJsMessaging implementation.
   void OnPostMessage(const base::string16& message) override;
@@ -48,7 +50,9 @@
   ~JsBinding() final;
 
  private:
-  explicit JsBinding(content::RenderFrame* render_frame);
+  explicit JsBinding(content::RenderFrame* render_frame,
+                     const base::string16& js_object_name,
+                     JsJavaConfigurator* js_java_configurator);
 
   // gin::Wrappable implementation
   gin::ObjectTemplateBuilder GetObjectTemplateBuilder(
@@ -66,11 +70,14 @@
   void SetOnMessage(v8::Isolate* isolate, v8::Local<v8::Value> value);
 
   content::RenderFrame* render_frame_;
+  base::string16 js_object_name_;
   v8::Global<v8::Function> on_message_;
   std::vector<v8::Global<v8::Function>> listeners_;
+  // |js_java_configurator| owns JsBinding objects, so it will out live
+  // JsBinding's life cycle, it is safe to access it.
+  JsJavaConfigurator* js_java_configurator_;
 
   mojo::Receiver<mojom::JavaToJsMessaging> receiver_{this};
-  mojo::AssociatedRemote<mojom::JsToJavaMessaging> js_to_java_messaging_;
 
   DISALLOW_COPY_AND_ASSIGN(JsBinding);
 };
diff --git a/android_webview/renderer/js_java_interaction/js_java_configurator.cc b/android_webview/renderer/js_java_interaction/js_java_configurator.cc
index af93a7f..b3154d5 100644
--- a/android_webview/renderer/js_java_interaction/js_java_configurator.cc
+++ b/android_webview/renderer/js_java_interaction/js_java_configurator.cc
@@ -15,6 +15,11 @@
 
 namespace android_webview {
 
+struct JsJavaConfigurator::JsObjectInfo {
+  net::ProxyBypassRules allowed_origin_rules;
+  mojo::AssociatedRemote<mojom::JsToJavaMessaging> js_to_java_messaging;
+};
+
 JsJavaConfigurator::JsJavaConfigurator(content::RenderFrame* render_frame)
     : RenderFrameObserver(render_frame) {
   render_frame->GetAssociatedInterfaceRegistry()->AddInterface(
@@ -24,24 +29,38 @@
 
 JsJavaConfigurator::~JsJavaConfigurator() = default;
 
-void JsJavaConfigurator::SetJsApiService(
-    bool need_to_inject_js_object,
-    const base::string16& js_object_name,
-    const net::ProxyBypassRules& allowed_origin_rules) {
-  need_to_inject_js_object_ = need_to_inject_js_object;
-  js_object_name_ = js_object_name;
-  js_object_allowed_origin_rules_ = allowed_origin_rules;
+void JsJavaConfigurator::SetJsObjects(
+    std::vector<mojom::JsObjectPtr> js_object_ptrs) {
+  JsObjectMap js_objects;
+  for (const auto& js_object : js_object_ptrs) {
+    const auto& js_object_info_pair = js_objects.insert(
+        {js_object->js_object_name, std::make_unique<JsObjectInfo>()});
+    JsObjectInfo* js_object_info = js_object_info_pair.first->second.get();
+    js_object_info->allowed_origin_rules = js_object->allowed_origin_rules;
+    js_object_info->js_to_java_messaging =
+        std::move(mojo::AssociatedRemote<mojom::JsToJavaMessaging>(
+            std::move(js_object->js_to_java_messaging)));
+  }
+  js_objects_.swap(js_objects);
 }
 
 void JsJavaConfigurator::DidClearWindowObject() {
-  if (!need_to_inject_js_object_ || !IsOriginMatch())
-    return;
-
   if (inside_did_clear_window_object_)
     return;
 
   base::AutoReset<bool> flag_entry(&inside_did_clear_window_object_, true);
-  js_binding_ = JsBinding::Install(render_frame(), js_object_name_);
+
+  url::Origin frame_origin =
+      url::Origin(render_frame()->GetWebFrame()->GetSecurityOrigin());
+  std::vector<std::unique_ptr<JsBinding>> js_bindings;
+  js_bindings.reserve(js_objects_.size());
+  for (const auto& js_object : js_objects_) {
+    if (!js_object.second->allowed_origin_rules.Matches(frame_origin.GetURL()))
+      continue;
+    js_bindings.push_back(
+        JsBinding::Install(render_frame(), js_object.first, this));
+  }
+  js_bindings_.swap(js_bindings);
 }
 
 void JsJavaConfigurator::WillReleaseScriptContext(
@@ -52,20 +71,14 @@
   if (world_id != content::ISOLATED_WORLD_ID_GLOBAL)
     return;
 
-  if (js_binding_)
-    js_binding_->ReleaseV8GlobalObjects();
+  for (const auto& js_binding : js_bindings_)
+    js_binding->ReleaseV8GlobalObjects();
 }
 
 void JsJavaConfigurator::OnDestruct() {
   delete this;
 }
 
-inline bool JsJavaConfigurator::IsOriginMatch() {
-  url::Origin frame_origin =
-      url::Origin(render_frame()->GetWebFrame()->GetSecurityOrigin());
-  return js_object_allowed_origin_rules_.Matches(frame_origin.GetURL());
-}
-
 void JsJavaConfigurator::BindPendingReceiver(
     mojo::PendingAssociatedReceiver<mojom::JsJavaConfigurator>
         pending_receiver) {
@@ -73,4 +86,12 @@
                  render_frame()->GetTaskRunner(blink::TaskType::kInternalIPC));
 }
 
+mojom::JsToJavaMessaging* JsJavaConfigurator::GetJsToJavaMessage(
+    const base::string16& js_object_name) {
+  auto iterator = js_objects_.find(js_object_name);
+  if (iterator == js_objects_.end())
+    return nullptr;
+  return iterator->second->js_to_java_messaging.get();
+}
+
 }  // namespace android_webview
diff --git a/android_webview/renderer/js_java_interaction/js_java_configurator.h b/android_webview/renderer/js_java_interaction/js_java_configurator.h
index 6c45545..28e4116 100644
--- a/android_webview/renderer/js_java_interaction/js_java_configurator.h
+++ b/android_webview/renderer/js_java_interaction/js_java_configurator.h
@@ -5,10 +5,13 @@
 #ifndef ANDROID_WEBVIEW_RENDERER_JS_JAVA_INTERACTION_JS_JAVA_CONFIGURATOR_H_
 #define ANDROID_WEBVIEW_RENDERER_JS_JAVA_INTERACTION_JS_JAVA_CONFIGURATOR_H_
 
+#include <vector>
+
 #include "android_webview/common/js_java_interaction/interfaces.mojom.h"
 #include "base/strings/string16.h"
 #include "content/public/renderer/render_frame_observer.h"
 #include "mojo/public/cpp/bindings/associated_receiver.h"
+#include "mojo/public/cpp/bindings/associated_remote.h"
 #include "net/proxy_resolution/proxy_bypass_rules.h"
 
 namespace content {
@@ -26,10 +29,7 @@
   ~JsJavaConfigurator() override;
 
   // mojom::Configurator implementation
-  void SetJsApiService(
-      bool inject_js_object,
-      const base::string16& js_object_name,
-      const net::ProxyBypassRules& allowed_origin_rules) override;
+  void SetJsObjects(std::vector<mojom::JsObjectPtr> js_object_ptrs) override;
 
   // RenderFrameObserver implementation
   void DidClearWindowObject() override;
@@ -37,24 +37,24 @@
                                 int32_t world_id) override;
   void OnDestruct() override;
 
+  mojom::JsToJavaMessaging* GetJsToJavaMessage(
+      const base::string16& js_object_name);
+
  private:
+  struct JsObjectInfo;
+
   void BindPendingReceiver(
       mojo::PendingAssociatedReceiver<mojom::JsJavaConfigurator>
           pending_receiver);
 
-  bool IsOriginMatch();
-
-  bool need_to_inject_js_object_ = false;
-  base::string16 js_object_name_;
-  // We use ProxyBypassRules because it has the functionality that suitable
-  // here, but it is not for proxy bypass.
-  net::ProxyBypassRules js_object_allowed_origin_rules_;
+  using JsObjectMap = std::map<base::string16, std::unique_ptr<JsObjectInfo>>;
+  JsObjectMap js_objects_;
 
   // In some cases DidClearWindowObject will be called twice in a row, we need
   // to prevent doing multiple injection in that case.
   bool inside_did_clear_window_object_ = false;
 
-  std::unique_ptr<JsBinding> js_binding_;
+  std::vector<std::unique_ptr<JsBinding>> js_bindings_;
 
   // Associated with legacy IPC channel.
   mojo::AssociatedReceiver<mojom::JsJavaConfigurator> receiver_{this};
diff --git a/ash/app_list/app_list_controller_impl.cc b/ash/app_list/app_list_controller_impl.cc
index bb7059f..1f41bcb 100644
--- a/ash/app_list/app_list_controller_impl.cc
+++ b/ash/app_list/app_list_controller_impl.cc
@@ -32,6 +32,7 @@
 #include "ash/public/cpp/app_list/app_list_metrics.h"
 #include "ash/public/cpp/app_list/app_list_types.h"
 #include "ash/public/cpp/ash_pref_names.h"
+#include "ash/public/cpp/shelf_config.h"
 #include "ash/public/cpp/shell_window_ids.h"
 #include "ash/root_window_controller.h"
 #include "ash/screen_util.h"
@@ -1223,6 +1224,10 @@
   return ash::screen_util::SnapBoundsToDisplayEdge(bounds, window);
 }
 
+int AppListControllerImpl::GetShelfHeight() {
+  return ShelfConfig::Get()->shelf_size();
+}
+
 void AppListControllerImpl::RecordAppLaunched(
     AppListLaunchedFrom launched_from) {
   app_list::RecordAppListAppLaunched(launched_from, GetAppListViewState(),
diff --git a/ash/app_list/app_list_controller_impl.h b/ash/app_list/app_list_controller_impl.h
index 0ab8289e..9a20a747 100644
--- a/ash/app_list/app_list_controller_impl.h
+++ b/ash/app_list/app_list_controller_impl.h
@@ -211,6 +211,7 @@
   void GetAppLaunchedMetricParams(
       app_list::AppLaunchedMetricParams* metric_params) override;
   gfx::Rect SnapBoundsToDisplayEdge(const gfx::Rect& bounds) override;
+  int GetShelfHeight() override;
 
   void AddObserver(AppListControllerObserver* observer);
   void RemoveObserver(AppListControllerObserver* obsever);
diff --git a/ash/app_list/app_list_controller_impl_unittest.cc b/ash/app_list/app_list_controller_impl_unittest.cc
index 1ec031ae..5696b99 100644
--- a/ash/app_list/app_list_controller_impl_unittest.cc
+++ b/ash/app_list/app_list_controller_impl_unittest.cc
@@ -17,6 +17,7 @@
 #include "ash/keyboard/keyboard_controller_impl.h"
 #include "ash/public/cpp/app_list/app_list_config.h"
 #include "ash/public/cpp/presentation_time_recorder.h"
+#include "ash/public/cpp/shelf_config.h"
 #include "ash/public/cpp/shelf_types.h"
 #include "ash/shelf/shelf.h"
 #include "ash/shelf/shelf_layout_manager.h"
@@ -185,8 +186,7 @@
   EXPECT_EQ(0, app_list_screen_bounds.y());
   EXPECT_EQ(ash::AppListViewState::kHalf, GetAppListView()->app_list_state());
   gfx::Transform expected_transform;
-  expected_transform.Translate(
-      0, -app_list::AppListConfig::instance().background_radius());
+  expected_transform.Translate(0, -(ShelfConfig::Get()->shelf_size() / 2));
   EXPECT_EQ(
       expected_transform,
       GetAppListView()->GetAppListBackgroundShieldForTest()->GetTransform());
@@ -236,8 +236,7 @@
 
   // AppListBackgroundShield is translated to hide the rounded corners.
   gfx::Transform expected_transform;
-  expected_transform.Translate(
-      0, -app_list::AppListConfig::instance().background_radius());
+  expected_transform.Translate(0, -(ShelfConfig::Get()->shelf_size() / 2));
   EXPECT_EQ(
       expected_transform,
       GetAppListView()->GetAppListBackgroundShieldForTest()->GetTransform());
diff --git a/ash/app_list/app_list_presenter_delegate_unittest.cc b/ash/app_list/app_list_presenter_delegate_unittest.cc
index 861f0d2..32b9a403 100644
--- a/ash/app_list/app_list_presenter_delegate_unittest.cc
+++ b/ash/app_list/app_list_presenter_delegate_unittest.cc
@@ -30,6 +30,7 @@
 #include "ash/public/cpp/ash_features.h"
 #include "ash/public/cpp/ash_switches.h"
 #include "ash/public/cpp/keyboard/keyboard_switches.h"
+#include "ash/public/cpp/shelf_config.h"
 #include "ash/public/cpp/shelf_item_delegate.h"
 #include "ash/public/cpp/shelf_model.h"
 #include "ash/public/cpp/shelf_types.h"
@@ -1746,8 +1747,7 @@
                                    ->GetShelfViewForTesting()
                                    ->GetBoundsInScreen()
                                    .top_center();
-  const int background_radius =
-      app_list::AppListConfig::instance().background_radius();
+  const int background_radius = ShelfConfig::Get()->shelf_size() / 2;
 
   app_list::AppListView* view = GetAppListView();
   const views::View* const background_shield =
@@ -1825,8 +1825,7 @@
                                    ->GetShelfViewForTesting()
                                    ->GetBoundsInScreen()
                                    .top_center();
-  const int background_radius =
-      app_list::AppListConfig::instance().background_radius();
+  const int background_radius = ShelfConfig::Get()->shelf_size() / 2;
 
   app_list::AppListView* view = GetAppListView();
   const views::View* const background_shield =
@@ -1949,7 +1948,7 @@
         ExpectedSuggestionChipContainerTop(search_box_bounds) +
         32 /*suggestion chip container height*/;
     const int available_height =
-        display_height - config.shelf_height() -
+        display_height - ShelfConfig::Get()->shelf_size() -
         config.search_box_fullscreen_top_padding() -
         search_box_bounds.height() - 32 /*suggestion chip container height*/
         - 24 /*margin between suggestion chip and search box*/;
@@ -2015,7 +2014,7 @@
 TEST_P(AppListPresenterDelegateScalableAppListTest,
        AppsPagePositionDuringDrag) {
   const app_list::AppListConfig& config = GetAppListView()->GetAppListConfig();
-  const int shelf_height = config.shelf_height();
+  const int shelf_height = ShelfConfig::Get()->shelf_size();
   const int fullscreen_y = 0;
   const int closed_y = 900 - shelf_height;
   const int fullscreen_search_box_padding =
@@ -2128,7 +2127,7 @@
   GetAppListTestHelper()->CheckState(AppListViewState::kHalf);
 
   const app_list::AppListConfig& config = GetAppListView()->GetAppListConfig();
-  const int shelf_height = config.shelf_height();
+  const int shelf_height = ShelfConfig::Get()->shelf_size();
   const int search_results_height = 440;
   const int fullscreen_y = 0;
   const int closed_y = 900 - shelf_height;
@@ -2274,7 +2273,7 @@
       GetAppListView()->GetBoundsInScreen().top_center();
 
   const app_list::AppListConfig& config = GetAppListView()->GetAppListConfig();
-  const int shelf_height = config.shelf_height();
+  const int shelf_height = ShelfConfig::Get()->shelf_size();
   const int search_results_height = 440;
   const int fullscreen_y = 0;
   const int fullscreen_search_box_padding =
@@ -2381,7 +2380,7 @@
   GetAppListTestHelper()->CheckState(AppListViewState::kFullscreenAllApps);
 
   const app_list::AppListConfig& config = GetAppListView()->GetAppListConfig();
-  const int shelf_height = config.shelf_height();
+  const int shelf_height = ShelfConfig::Get()->shelf_size();
   const int search_results_height = 440;
   const int fullscreen_y = 0;
   const int fullscreen_search_box_padding =
diff --git a/ash/app_list/app_list_view_delegate.h b/ash/app_list/app_list_view_delegate.h
index c5f3590..d48f606 100644
--- a/ash/app_list/app_list_view_delegate.h
+++ b/ash/app_list/app_list_view_delegate.h
@@ -209,6 +209,9 @@
   // Adjusts the bounds by snapping it to the edge of the display in pixel
   // space. This prevents 1px gaps on displays with non-integer scale factors.
   virtual gfx::Rect SnapBoundsToDisplayEdge(const gfx::Rect& bounds) = 0;
+
+  // Gets the current shelf height from the ShelfConfig.
+  virtual int GetShelfHeight() = 0;
 };
 
 }  // namespace app_list
diff --git a/ash/app_list/test/app_list_test_view_delegate.cc b/ash/app_list/test/app_list_test_view_delegate.cc
index ad7ba9f..8059566 100644
--- a/ash/app_list/test/app_list_test_view_delegate.cc
+++ b/ash/app_list/test/app_list_test_view_delegate.cc
@@ -195,6 +195,12 @@
   return bounds;
 }
 
+int AppListTestViewDelegate::GetShelfHeight() {
+  // TODO(mmourgos): change this to 48 once shelf-hotseat flag is enabled.
+  // Return the height of the shelf when clamshell mode is active.
+  return 56;
+}
+
 void AppListTestViewDelegate::RecordAppLaunched(
     ash::AppListLaunchedFrom launched_from) {
   app_list::RecordAppListAppLaunched(launched_from, model_->state_fullscreen(),
diff --git a/ash/app_list/test/app_list_test_view_delegate.h b/ash/app_list/test/app_list_test_view_delegate.h
index f6a0c9c..fdedd28e 100644
--- a/ash/app_list/test/app_list_test_view_delegate.h
+++ b/ash/app_list/test/app_list_test_view_delegate.h
@@ -115,6 +115,7 @@
   void GetAppLaunchedMetricParams(
       app_list::AppLaunchedMetricParams* metric_params) override;
   gfx::Rect SnapBoundsToDisplayEdge(const gfx::Rect& bounds) override;
+  int GetShelfHeight() override;
 
   // Do a bulk replacement of the items in the model.
   void ReplaceTestModel(int item_count);
diff --git a/ash/app_list/views/app_list_view.cc b/ash/app_list/views/app_list_view.cc
index 1cb43b5..924eba2 100644
--- a/ash/app_list/views/app_list_view.cc
+++ b/ash/app_list/views/app_list_view.cc
@@ -188,10 +188,10 @@
 // state).
 // |height|: App list view height, relative to the shelf top (i.e. distance
 //           between app list top and shelf top edge).
-double GetBackgroundRadiusForAppListHeight(double height) {
-  return std::min(
-      static_cast<double>(AppListConfig::instance().background_radius()),
-      std::max(height, 0.));
+double GetBackgroundRadiusForAppListHeight(double height,
+                                           int shelf_background_corner_radius) {
+  return std::min(static_cast<double>(shelf_background_corner_radius),
+                  std::max(height, 0.));
 }
 
 // This targeter prevents routing events to sub-windows, such as
@@ -422,10 +422,12 @@
 // The view for the app list background shield which changes color and radius.
 class AppListBackgroundShieldView : public views::View {
  public:
-  AppListBackgroundShieldView() : color_(AppListView::kDefaultBackgroundColor) {
+  explicit AppListBackgroundShieldView(int shelf_background_corner_radius)
+      : color_(AppListView::kDefaultBackgroundColor),
+        shelf_background_corner_radius_(shelf_background_corner_radius) {
     SetPaintToLayer(ui::LAYER_SOLID_COLOR);
     layer()->SetFillsBoundsOpaquely(false);
-    SetBackgroundRadius(AppListConfig::instance().background_radius());
+    SetBackgroundRadius(shelf_background_corner_radius_);
     layer()->SetColor(color_);
   }
 
@@ -451,7 +453,7 @@
     const double target_corner_radius =
         (state == ash::AppListViewState::kClosed && !shelf_has_rounded_corners)
             ? 0
-            : AppListConfig::instance().background_radius();
+            : shelf_background_corner_radius_;
     if (radius_ == target_corner_radius)
       return;
 
@@ -490,8 +492,7 @@
     // add the inset to the bottom to keep padding at the top of the AppList the
     // same.
     gfx::Rect new_bounds = bounds;
-    new_bounds.Inset(0, 0, 0,
-                     -AppListConfig::instance().background_radius() * 2);
+    new_bounds.Inset(0, 0, 0, -shelf_background_corner_radius_ * 2);
     SetBoundsRect(new_bounds);
   }
 
@@ -509,6 +510,8 @@
 
   SkColor color_;
 
+  int shelf_background_corner_radius_ = 0;
+
   DISALLOW_COPY_AND_ASSIGN(AppListBackgroundShieldView);
 };
 
@@ -592,7 +595,8 @@
   DCHECK(!search_box_view_);
   DCHECK(!announcement_view_);
 
-  app_list_background_shield_ = new AppListBackgroundShieldView();
+  app_list_background_shield_ =
+      new AppListBackgroundShieldView(delegate_->GetShelfHeight() / 2);
   app_list_background_shield_->UpdateBackground(/*use_blur*/ !is_tablet_mode &&
                                                 is_background_blur_enabled_);
   AddChildView(app_list_background_shield_);
@@ -778,7 +782,7 @@
   // Exclude the shelf height from the contents bounds to avoid apps grid from
   // overlapping with shelf.
   gfx::Rect main_bounds = contents_bounds;
-  main_bounds.Inset(0, 0, 0, AppListConfig::instance().shelf_height());
+  main_bounds.Inset(0, 0, 0, delegate_->GetShelfHeight());
 
   app_list_main_view_->SetBoundsRect(main_bounds);
 
@@ -811,7 +815,7 @@
   gfx::Size available_apps_grid_size = parent_window->bounds().size();
   available_apps_grid_size.Enlarge(
       -non_apps_grid_size.width(),
-      -non_apps_grid_size.height() - AppListConfig::instance().shelf_height());
+      -non_apps_grid_size.height() - delegate_->GetShelfHeight());
 
   // Create the app list configuration override if it's needed for the current
   // display bounds and the available apps grid size.
@@ -1663,8 +1667,7 @@
 
   gfx::Transform shield_transform;
   if (ShouldHideRoundedCorners(target_state, target_bounds)) {
-    shield_transform.Translate(0,
-                               -AppListConfig::instance().background_radius());
+    shield_transform.Translate(0, -(delegate_->GetShelfHeight() / 2));
   }
   app_list_background_shield_->SetTransform(shield_transform);
 
@@ -1747,7 +1750,7 @@
   gfx::Rect new_widget_bounds = GetWidget()->GetWindowBoundsInScreen();
   app_list_y_position_in_screen_ = std::min(
       std::max(y_position_in_screen, GetDisplayNearestView().work_area().y()),
-      GetScreenBottom() - AppListConfig::instance().shelf_height());
+      GetScreenBottom() - delegate_->GetShelfHeight());
   new_widget_bounds.set_y(app_list_y_position_in_screen_);
   gfx::NativeView native_view = GetWidget()->GetNativeView();
   ::wm::ConvertRectFromScreen(native_view->parent(), &new_widget_bounds);
@@ -1817,7 +1820,7 @@
 
 int AppListView::GetCurrentAppListHeight() const {
   if (!GetWidget())
-    return AppListConfig::instance().shelf_height();
+    return delegate_->GetShelfHeight();
   return GetScreenBottom() - GetWidget()->GetWindowBoundsInScreen().y();
 }
 
@@ -1958,7 +1961,7 @@
 
   gfx::Transform transform;
   if (ShouldHideRoundedCorners(app_list_state_, new_bounds))
-    transform.Translate(0, -AppListConfig::instance().background_radius());
+    transform.Translate(0, -(delegate_->GetShelfHeight() / 2));
 
   // Avoid setting new transform if the shield is animating to (or already has)
   // the target value.
@@ -2116,7 +2119,7 @@
 
 float AppListView::GetAppListBackgroundOpacityDuringDragging() {
   float top_of_applist = GetWidget()->GetWindowBoundsInScreen().y();
-  const int shelf_height = AppListConfig::instance().shelf_height();
+  const int shelf_height = delegate_->GetShelfHeight();
   float dragging_height =
       std::max((GetScreenBottom() - shelf_height - top_of_applist), 0.f);
   float coefficient =
@@ -2225,6 +2228,8 @@
 
 void AppListView::UpdateAppListBackgroundYPosition(
     ash::AppListViewState state) {
+  const int app_list_background_corner_radius = delegate_->GetShelfHeight() / 2;
+
   // Update the y position of the background shield.
   gfx::Transform transform;
   if (is_in_drag_) {
@@ -2234,17 +2239,18 @@
       const float shelf_height =
           GetScreenBottom() - GetDisplayNearestView().work_area().bottom();
       app_list_background_shield_->SetBackgroundRadius(
-          GetBackgroundRadiusForAppListHeight(GetCurrentAppListHeight() -
-                                              shelf_height));
+          GetBackgroundRadiusForAppListHeight(
+              GetCurrentAppListHeight() - shelf_height,
+              app_list_background_corner_radius));
     } else if (app_list_transition_progress >= 1 &&
                app_list_transition_progress <= 2) {
       // Translate background shield so that it ends drag at a y position
       // according to the background radius in peeking and fullscreen.
-      transform.Translate(0, -AppListConfig::instance().background_radius() *
+      transform.Translate(0, -app_list_background_corner_radius *
                                  (app_list_transition_progress - 1));
     }
   } else if (ShouldHideRoundedCorners(state, GetBoundsInScreen())) {
-    transform.Translate(0, -AppListConfig::instance().background_radius());
+    transform.Translate(0, -app_list_background_corner_radius);
   }
 
   // Avoid setting new transform if the shield is animating to (or already has)
diff --git a/ash/app_list/views/app_list_view_unittest.cc b/ash/app_list/views/app_list_view_unittest.cc
index b743224..bcb123f 100644
--- a/ash/app_list/views/app_list_view_unittest.cc
+++ b/ash/app_list/views/app_list_view_unittest.cc
@@ -252,7 +252,7 @@
            view_->search_box_view()->GetWidget()->GetWindowBoundsInScreen();
   }
 
-  int ShelfHeight() const { return AppListConfig::instance().shelf_height(); }
+  int ShelfHeight() const { return delegate_->GetShelfHeight(); }
 
   // Gets the PaginationModel owned by |view_|.
   ash::PaginationModel* GetPaginationModel() const {
@@ -1893,7 +1893,7 @@
   EXPECT_EQ(ash::AppListViewState::kFullscreenAllApps, view_->app_list_state());
   // The rounded corners should be off screen in side shelf.
   gfx::Transform translation;
-  translation.Translate(0, -AppListConfig::instance().background_radius());
+  translation.Translate(0, -(delegate_->GetShelfHeight() / 2));
   // The rounded corners should be off screen in side shelf.
   EXPECT_EQ(translation,
             view_->GetAppListBackgroundShieldForTest()->GetTransform());
diff --git a/ash/app_list/views/search_box_view.cc b/ash/app_list/views/search_box_view.cc
index e436046..3714735 100644
--- a/ash/app_list/views/search_box_view.cc
+++ b/ash/app_list/views/search_box_view.cc
@@ -380,7 +380,7 @@
            ->ShouldShowSearchBox()) {
     return;
   }
-  const int shelf_height = AppListConfig::instance().shelf_height();
+  const int shelf_height = view_delegate_->GetShelfHeight();
   float fraction =
       std::max<float>(
           0, contents_view_->app_list_view()->GetCurrentAppListHeight() -
diff --git a/ash/assistant/assistant_setup_controller.cc b/ash/assistant/assistant_setup_controller.cc
index 52a5f91..f050feca 100644
--- a/ash/assistant/assistant_setup_controller.cc
+++ b/ash/assistant/assistant_setup_controller.cc
@@ -44,13 +44,11 @@
 void AssistantSetupController::OnDeepLinkReceived(
     assistant::util::DeepLinkType type,
     const std::map<std::string, std::string>& params) {
-  using namespace assistant::util;
-
-  if (type != DeepLinkType::kOnboarding)
+  if (type != assistant::util::DeepLinkType::kOnboarding)
     return;
 
-  base::Optional<bool> relaunch =
-      GetDeepLinkParamAsBool(params, DeepLinkParam::kRelaunch);
+  base::Optional<bool> relaunch = assistant::util::GetDeepLinkParamAsBool(
+      params, assistant::util::DeepLinkParam::kRelaunch);
 
   StartOnboarding(relaunch.value_or(false));
 }
diff --git a/ash/display/touch_calibrator_controller_unittest.cc b/ash/display/touch_calibrator_controller_unittest.cc
index 36c441b2..0b6d274 100644
--- a/ash/display/touch_calibrator_controller_unittest.cc
+++ b/ash/display/touch_calibrator_controller_unittest.cc
@@ -25,8 +25,6 @@
 #include "ui/events/test/event_generator.h"
 #include "ui/events/test/events_test_utils.h"
 
-using namespace display;
-
 namespace ash {
 namespace {
 
@@ -50,10 +48,10 @@
 
   void TearDown() override {
     // Reset all touch device and touch association.
-    test::TouchDeviceManagerTestApi(touch_device_manager())
+    display::test::TouchDeviceManagerTestApi(touch_device_manager())
         .ResetTouchDeviceManager();
     ui::DeviceDataManagerTestApi().SetTouchscreenDevices({});
-    test::TouchTransformControllerTestApi(
+    display::test::TouchTransformControllerTestApi(
         Shell::Get()->touch_transformer_controller())
         .touch_transform_setter()
         ->ConfigureTouchDevices(std::vector<ui::TouchDeviceTransform>());
@@ -78,22 +76,22 @@
     return ctrl->touch_calibrator_views_;
   }
 
-  const Display& InitDisplays() {
+  const display::Display& InitDisplays() {
     // Initialize 2 displays each with resolution 500x500.
     UpdateDisplay("500x500,500x500");
     // Assuming index 0 points to the native display, we will calibrate the
     // touch display at index 1.
     const int kTargetDisplayIndex = 1;
-    DisplayIdList display_id_list =
+    display::DisplayIdList display_id_list =
         display_manager()->GetCurrentDisplayIdList();
     int64_t target_display_id = display_id_list[kTargetDisplayIndex];
-    const Display& touch_display =
+    const display::Display& touch_display =
         display_manager()->GetDisplayForId(target_display_id);
     return touch_display;
   }
 
   void StartCalibrationChecks(TouchCalibratorController* ctrl,
-                              const Display& target_display) {
+                              const display::Display& target_display) {
     EXPECT_FALSE(ctrl->IsCalibrating());
     EXPECT_FALSE(!!ctrl->touch_calibrator_views_.size());
 
@@ -131,14 +129,14 @@
 
   // Generates a touch press and release event in the |display| with source
   // device id as |touch_device_id|.
-  void GenerateTouchEvent(const Display& display,
+  void GenerateTouchEvent(const display::Display& display,
                           int touch_device_id,
                           const gfx::Point& location = gfx::Point(20, 20)) {
     // Get the correct EventTarget for the given |display|.
     aura::Window::Windows root_windows = Shell::GetAllRootWindows();
     ui::EventTarget* event_target = nullptr;
     for (auto* window : root_windows) {
-      if (Screen::GetScreen()->GetDisplayNearestWindow(window).id() ==
+      if (display::Screen::GetScreen()->GetDisplayNearestWindow(window).id() ==
           display.id()) {
         event_target = window;
         break;
@@ -178,7 +176,7 @@
     transforms.push_back(touch_device_transform);
 
     // This makes touchscreen target displays valid for ui::DeviceDataManager.
-    test::TouchTransformControllerTestApi(
+    display::test::TouchTransformControllerTestApi(
         Shell::Get()->touch_transformer_controller())
         .touch_transform_setter()
         ->ConfigureTouchDevices(transforms);
@@ -190,7 +188,7 @@
 };
 
 TEST_F(TouchCalibratorControllerTest, StartCalibration) {
-  const Display& touch_display = InitDisplays();
+  const display::Display& touch_display = InitDisplays();
   TouchCalibratorController touch_calibrator_controller;
   StartCalibrationChecks(&touch_calibrator_controller, touch_display);
 
@@ -200,7 +198,7 @@
 }
 
 TEST_F(TouchCalibratorControllerTest, KeyEventIntercept) {
-  const Display& touch_display = InitDisplays();
+  const display::Display& touch_display = InitDisplays();
   TouchCalibratorController touch_calibrator_controller;
   StartCalibrationChecks(&touch_calibrator_controller, touch_display);
 
@@ -211,7 +209,7 @@
 }
 
 TEST_F(TouchCalibratorControllerTest, TouchThreshold) {
-  const Display& touch_display = InitDisplays();
+  const display::Display& touch_display = InitDisplays();
   TouchCalibratorController touch_calibrator_controller;
   StartCalibrationChecks(&touch_calibrator_controller, touch_display);
 
@@ -239,7 +237,7 @@
 }
 
 TEST_F(TouchCalibratorControllerTest, TouchDeviceIdIsSet) {
-  const Display& touch_display = InitDisplays();
+  const display::Display& touch_display = InitDisplays();
 
   TouchCalibratorController touch_calibrator_controller;
   StartCalibrationChecks(&touch_calibrator_controller, touch_display);
@@ -256,7 +254,7 @@
 }
 
 TEST_F(TouchCalibratorControllerTest, CustomCalibration) {
-  const Display& touch_display = InitDisplays();
+  const display::Display& touch_display = InitDisplays();
 
   TouchCalibratorController touch_calibrator_controller;
   EXPECT_FALSE(touch_calibrator_controller.IsCalibrating());
@@ -296,14 +294,14 @@
   const display::ManagedDisplayInfo& info =
       display_manager()->GetDisplayInfo(touch_display.id());
 
-  test::TouchDeviceManagerTestApi tdm_test_api(touch_device_manager());
+  display::test::TouchDeviceManagerTestApi tdm_test_api(touch_device_manager());
   EXPECT_TRUE(tdm_test_api.AreAssociated(info, touchdevice));
   EXPECT_EQ(calibration_data,
             touch_device_manager()->GetCalibrationData(touchdevice, info.id()));
 }
 
 TEST_F(TouchCalibratorControllerTest, CustomCalibrationInvalidTouchId) {
-  const Display& touch_display = InitDisplays();
+  const display::Display& touch_display = InitDisplays();
 
   TouchCalibratorController touch_calibrator_controller;
   EXPECT_FALSE(touch_calibrator_controller.IsCalibrating());
@@ -345,7 +343,7 @@
 }
 
 TEST_F(TouchCalibratorControllerTest, IgnoreInternalTouchDevices) {
-  const Display& touch_display = InitDisplays();
+  const display::Display& touch_display = InitDisplays();
 
   // We need to initialize a touch device before starting calibration so that
   // the set |internal_touch_device_ids_| can be initialized.
@@ -382,14 +380,15 @@
   // Index 0 points to the native internal display, we will calibrate the touch
   // display at index 2.
   const int kTargetDisplayIndex = 2;
-  DisplayIdList display_id_list = display_manager()->GetCurrentDisplayIdList();
+  display::DisplayIdList display_id_list =
+      display_manager()->GetCurrentDisplayIdList();
 
   int64_t internal_display_id = display_id_list[1];
-  test::ScopedSetInternalDisplayId set_internal(display_manager(),
-                                                internal_display_id);
+  display::test::ScopedSetInternalDisplayId set_internal(display_manager(),
+                                                         internal_display_id);
 
   int64_t target_display_id = display_id_list[kTargetDisplayIndex];
-  const Display& touch_display =
+  const display::Display& touch_display =
       display_manager()->GetDisplayForId(target_display_id);
 
   // We create 2 touch devices.
@@ -416,7 +415,7 @@
   touch_device_transform.device_id = external_touchdevice.id;
   transforms.push_back(touch_device_transform);
 
-  test::TouchTransformControllerTestApi(
+  display::test::TouchTransformControllerTestApi(
       Shell::Get()->touch_transformer_controller())
       .touch_transform_setter()
       ->ConfigureTouchDevices(transforms);
@@ -438,7 +437,7 @@
       ->SkipCurrentAnimation();
 
   // Reinitialize the transforms, as starting calibration resets them.
-  test::TouchTransformControllerTestApi(
+  display::test::TouchTransformControllerTestApi(
       Shell::Get()->touch_transformer_controller())
       .touch_transform_setter()
       ->ConfigureTouchDevices(transforms);
@@ -474,14 +473,15 @@
   // Index 0 points to the native internal display, we will calibrate the touch
   // display at index 1.
   const int kTargetDisplayIndex = 1;
-  DisplayIdList display_id_list = display_manager()->GetCurrentDisplayIdList();
+  display::DisplayIdList display_id_list =
+      display_manager()->GetCurrentDisplayIdList();
 
   int64_t internal_display_id = display_id_list[0];
-  test::ScopedSetInternalDisplayId set_internal(display_manager(),
-                                                internal_display_id);
+  display::test::ScopedSetInternalDisplayId set_internal(display_manager(),
+                                                         internal_display_id);
 
   int64_t target_display_id = display_id_list[kTargetDisplayIndex];
-  const Display& touch_display =
+  const display::Display& touch_display =
       display_manager()->GetDisplayForId(target_display_id);
 
   // We create 2 touch devices.
@@ -508,7 +508,7 @@
   touch_device_transform.device_id = external_touchdevice.id;
   transforms.push_back(touch_device_transform);
 
-  test::TouchTransformControllerTestApi(
+  display::test::TouchTransformControllerTestApi(
       Shell::Get()->touch_transformer_controller())
       .touch_transform_setter()
       ->ConfigureTouchDevices(transforms);
@@ -530,7 +530,7 @@
       ->SkipCurrentAnimation();
 
   // Reinitialize the transforms, as starting calibration resets them.
-  test::TouchTransformControllerTestApi(
+  display::test::TouchTransformControllerTestApi(
       Shell::Get()->touch_transformer_controller())
       .touch_transform_setter()
       ->ConfigureTouchDevices(transforms);
@@ -563,7 +563,7 @@
 }
 
 TEST_F(TouchCalibratorControllerTest, InternalTouchDeviceIsRejected) {
-  const Display& touch_display = InitDisplays();
+  const display::Display& touch_display = InitDisplays();
 
   // We need to initialize a touch device before starting calibration so that
   // the set |internal_touch_device_ids_| can be initialized.
diff --git a/ash/login/login_screen_controller_unittest.cc b/ash/login/login_screen_controller_unittest.cc
index e60beff..d0320cf 100644
--- a/ash/login/login_screen_controller_unittest.cc
+++ b/ash/login/login_screen_controller_unittest.cc
@@ -22,8 +22,8 @@
 #include "components/prefs/pref_service.h"
 #include "components/session_manager/session_manager_types.h"
 
+using session_manager::SessionState;
 using ::testing::_;
-using namespace session_manager;
 
 namespace ash {
 
diff --git a/ash/public/cpp/app_list/app_list_config.cc b/ash/public/cpp/app_list/app_list_config.cc
index bc282cb..187426a 100644
--- a/ash/public/cpp/app_list/app_list_config.cc
+++ b/ash/public/cpp/app_list/app_list_config.cc
@@ -307,10 +307,6 @@
       folder_background_color_(gfx::kGoogleGrey100),
       page_flip_zone_size_(20),
       grid_tile_spacing_in_folder_(8),
-      // TODO(manucornet): Share the value with ShelfConstants and use
-      // 48 when the new shelf UI is turned off.
-      shelf_height_(chromeos::switches::ShouldShowShelfHotseat() ? 48 : 56),
-      background_radius_(shelf_height_ / 2),
       blur_radius_(30),
       contents_background_color_(SkColorSetRGB(0xF2, 0xF2, 0xF2)),
       grid_selected_color_(gfx::kGoogleBlue300),
@@ -445,8 +441,6 @@
           MinScale(base_config.grid_tile_spacing_in_folder_,
                    scale_x,
                    inner_tile_scale_y)),
-      shelf_height_(base_config.shelf_height_),
-      background_radius_(base_config.background_radius_),
       blur_radius_(base_config.blur_radius_),
       contents_background_color_(base_config.contents_background_color_),
       grid_selected_color_(base_config.grid_selected_color_),
diff --git a/ash/public/cpp/app_list/app_list_config.h b/ash/public/cpp/app_list/app_list_config.h
index 8c2d87c..289121c 100644
--- a/ash/public/cpp/app_list/app_list_config.h
+++ b/ash/public/cpp/app_list/app_list_config.h
@@ -127,8 +127,6 @@
   int grid_tile_spacing_in_folder() const {
     return grid_tile_spacing_in_folder_;
   }
-  int shelf_height() const { return shelf_height_; }
-  int background_radius() const { return background_radius_; }
   int blur_radius() const { return blur_radius_; }
   SkColor contents_background_color() const {
     return contents_background_color_;
@@ -366,12 +364,6 @@
   // The spacing between tile views in folder.
   const int grid_tile_spacing_in_folder_;
 
-  // The height/width of the shelf from the bottom/side of the screen.
-  const int shelf_height_;
-
-  // The background corner radius used for the app list.
-  const int background_radius_;
-
   // The blur radius used in the app list.
   const int blur_radius_;
 
diff --git a/ash/system/network/tray_network_state_model.cc b/ash/system/network/tray_network_state_model.cc
index adeebb3..6eeb76e 100644
--- a/ash/system/network/tray_network_state_model.cc
+++ b/ash/system/network/tray_network_state_model.cc
@@ -116,6 +116,8 @@
 
 void TrayNetworkStateModel::OnVpnProvidersChanged() {}
 
+void TrayNetworkStateModel::OnNetworkCertificatesChanged() {}
+
 void TrayNetworkStateModel::GetDeviceStateList() {
   DCHECK(remote_cros_network_config_);
   remote_cros_network_config_->GetDeviceStateList(base::BindOnce(
diff --git a/ash/system/network/tray_network_state_model.h b/ash/system/network/tray_network_state_model.h
index df15261..9c20bf9 100644
--- a/ash/system/network/tray_network_state_model.h
+++ b/ash/system/network/tray_network_state_model.h
@@ -77,6 +77,7 @@
   void OnNetworkStateListChanged() override;
   void OnDeviceStateListChanged() override;
   void OnVpnProvidersChanged() override;
+  void OnNetworkCertificatesChanged() override;
 
   void GetDeviceStateList();
   void OnGetDeviceStateList(
diff --git a/ash/system/network/vpn_list.cc b/ash/system/network/vpn_list.cc
index 8de0437..5c0b80f 100644
--- a/ash/system/network/vpn_list.cc
+++ b/ash/system/network/vpn_list.cc
@@ -58,6 +58,8 @@
       base::BindOnce(&VpnList::OnGetVpnProviders, base::Unretained(this)));
 }
 
+void VpnList::OnNetworkCertificatesChanged() {}
+
 void VpnList::SetVpnProvidersForTest(std::vector<VpnProviderPtr> providers) {
   OnGetVpnProviders(std::move(providers));
 }
diff --git a/ash/system/network/vpn_list.h b/ash/system/network/vpn_list.h
index aae73b8..41df362 100644
--- a/ash/system/network/vpn_list.h
+++ b/ash/system/network/vpn_list.h
@@ -70,6 +70,7 @@
   void OnNetworkStateListChanged() override;
   void OnDeviceStateListChanged() override;
   void OnVpnProvidersChanged() override;
+  void OnNetworkCertificatesChanged() override;
 
   void SetVpnProvidersForTest(std::vector<VpnProviderPtr> providers);
 
diff --git a/ash/system/palette/palette_tool_manager_unittest.cc b/ash/system/palette/palette_tool_manager_unittest.cc
index 83c4ff2..9ee6119f 100644
--- a/ash/system/palette/palette_tool_manager_unittest.cc
+++ b/ash/system/palette/palette_tool_manager_unittest.cc
@@ -12,7 +12,7 @@
 #include "base/memory/ptr_util.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
-using namespace ash;
+namespace ash {
 
 namespace {
 
@@ -141,3 +141,5 @@
   EXPECT_FALSE(action_2->enabled());
   palette_tool_manager_->DeactivateTool(action_1->GetToolId());
 }
+
+}  // namespace ash
\ No newline at end of file
diff --git a/base/time/time.h b/base/time/time.h
index 3e1000c..4689935 100644
--- a/base/time/time.h
+++ b/base/time/time.h
@@ -121,6 +121,9 @@
   constexpr TimeDelta() : delta_(0) {}
 
   // Converts units of time to TimeDeltas.
+  // WARNING: Floating point arithmetic is such that FromXXXD(t.InXXXF()) may
+  // not precisely equal |t|. Hence, floating point values should not be used
+  // for storage.
   static constexpr TimeDelta FromDays(int days);
   static constexpr TimeDelta FromHours(int hours);
   static constexpr TimeDelta FromMinutes(int minutes);
@@ -208,6 +211,9 @@
   // towards zero, std::trunc() behavior). The InXYZFloored() versions round to
   // lesser integers (std::floor() behavior). The XYZRoundedUp() versions round
   // up to greater integers (std::ceil() behavior).
+  // WARNING: Floating point arithmetic is such that FromXXXD(t.InXXXF()) may
+  // not precisely equal |t|. Hence, floating point values should not be used
+  // for storage.
   int InDays() const;
   int InDaysFloored() const;
   int InHours() const;
diff --git a/build/fuchsia/linux.sdk.sha1 b/build/fuchsia/linux.sdk.sha1
index 25944cf..156843d7 100644
--- a/build/fuchsia/linux.sdk.sha1
+++ b/build/fuchsia/linux.sdk.sha1
@@ -1 +1 @@
-8901447639518533984
\ No newline at end of file
+8901420124367166160
\ No newline at end of file
diff --git a/build/fuchsia/mac.sdk.sha1 b/build/fuchsia/mac.sdk.sha1
index 0d6122f..abc6db2 100644
--- a/build/fuchsia/mac.sdk.sha1
+++ b/build/fuchsia/mac.sdk.sha1
@@ -1 +1 @@
-8901448833775125712
\ No newline at end of file
+8901421552206782336
\ No newline at end of file
diff --git a/build/util/lib/common/chrome_test_server_spawner.py b/build/util/lib/common/chrome_test_server_spawner.py
index 93ecb71..103f7c0 100644
--- a/build/util/lib/common/chrome_test_server_spawner.py
+++ b/build/util/lib/common/chrome_test_server_spawner.py
@@ -355,7 +355,7 @@
       _logger.info('Test server is running on port %d forwarded to %d.' %
               (new_server.forwarder_device_port, new_server.host_port))
       port = new_server.forwarder_device_port
-      assert not self.server.test_servers.has_key(port)
+      assert port not in self.server.test_servers
       self.server.test_servers[port] = new_server
     else:
       new_server.Stop()
@@ -372,7 +372,7 @@
       self._SendResponse(400, 'Invalid request.', {}, 'port must be specified')
       return
 
-    if not self.server.test_servers.has_key(port):
+    if port not in self.server.test_servers:
       self._SendResponse(400, 'Invalid request.', {},
                          "testserver isn't running on port %d" % port)
       return
diff --git a/build/win/gn_meta_sln.py b/build/win/gn_meta_sln.py
index f97f47b..862d278 100644
--- a/build/win/gn_meta_sln.py
+++ b/build/win/gn_meta_sln.py
@@ -90,7 +90,7 @@
         match_obj = re.match(project_pattern, sln_line)
         if match_obj:
             proj_name = match_obj.group(1)
-            if not all_projects.has_key(proj_name):
+            if proj_name not in all_projects:
                 all_projects[proj_name] = []
             all_projects[proj_name].append((config[0], match_obj.group(2),
                                             match_obj.group(3)))
diff --git a/cc/raster/raster_buffer_provider.cc b/cc/raster/raster_buffer_provider.cc
index c4fa5a9b..86bd980 100644
--- a/cc/raster/raster_buffer_provider.cc
+++ b/cc/raster/raster_buffer_provider.cc
@@ -45,7 +45,6 @@
     case viz::BGRX_1010102:
     case viz::YVU_420:
     case viz::YUV_420_BIPLANAR:
-    case viz::UYVY_422:
     case viz::P010:
       return false;
   }
@@ -145,7 +144,6 @@
     case viz::BGRX_1010102:
     case viz::YVU_420:
     case viz::YUV_420_BIPLANAR:
-    case viz::UYVY_422:
     case viz::P010:
       NOTREACHED();
       return;
diff --git a/cc/trees/layer_tree_host_pixeltest_filters.cc b/cc/trees/layer_tree_host_pixeltest_filters.cc
index b43b7336..ad0e12be 100644
--- a/cc/trees/layer_tree_host_pixeltest_filters.cc
+++ b/cc/trees/layer_tree_host_pixeltest_filters.cc
@@ -138,6 +138,26 @@
           : base::FilePath(FILE_PATH_LITERAL("backdrop_filter_blur.png")));
 }
 
+TEST_P(LayerTreeHostFiltersPixelTest, BackdropFilterInvalid) {
+  scoped_refptr<SolidColorLayer> background =
+      CreateSolidColorLayer(gfx::Rect(200, 200), SK_ColorWHITE);
+  scoped_refptr<SolidColorLayer> green =
+      CreateSolidColorLayer(gfx::Rect(50, 50, 100, 100), kCSSGreen);
+  scoped_refptr<SolidColorLayer> blur =
+      CreateSolidColorLayer(gfx::Rect(30, 30, 140, 140), SK_ColorTRANSPARENT);
+  background->AddChild(green);
+  background->AddChild(blur);
+
+  // This should be an invalid filter, and result in just the original green.
+  FilterOperations filters;
+  filters.Append(FilterOperation::CreateHueRotateFilter(9e99));
+  blur->SetBackdropFilters(filters);
+
+  RunPixelTest(
+      renderer_type(), background,
+      base::FilePath(FILE_PATH_LITERAL("backdrop_filter_invalid.png")));
+}
+
 TEST_P(LayerTreeHostFiltersPixelTest, BackdropFilterBlurRadius) {
   if (renderer_type() == RENDERER_SOFTWARE) {
     // TODO(989238): Software renderer does not support/implement
diff --git a/chrome/android/chrome_junit_test_java_sources.gni b/chrome/android/chrome_junit_test_java_sources.gni
index 59a03e5..98e3127 100644
--- a/chrome/android/chrome_junit_test_java_sources.gni
+++ b/chrome/android/chrome_junit_test_java_sources.gni
@@ -41,7 +41,6 @@
   "junit/src/org/chromium/chrome/browser/compositor/overlays/strip/StripLayoutHelperTest.java",
   "junit/src/org/chromium/chrome/browser/contextmenu/ChromeContextMenuPopulatorTest.java",
   "junit/src/org/chromium/chrome/browser/contextmenu/RevampedContextMenuCoordinatorTest.java",
-  "junit/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchContextForTest.java",
   "junit/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchContextTest.java",
   "junit/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchEntityHeuristicTest.java",
   "junit/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchInternalStateTest.java",
diff --git a/chrome/android/features/tab_ui/BUILD.gn b/chrome/android/features/tab_ui/BUILD.gn
index a163a61..4a3b277 100644
--- a/chrome/android/features/tab_ui/BUILD.gn
+++ b/chrome/android/features/tab_ui/BUILD.gn
@@ -78,6 +78,9 @@
 android_library("java") {
   java_files = [
     "java/src/org/chromium/chrome/browser/tasks/MostVisitedListCoordinator.java",
+    "java/src/org/chromium/chrome/browser/tasks/MostVisitedListMediator.java",
+    "java/src/org/chromium/chrome/browser/tasks/MostVisitedListViewBinder.java",
+    "java/src/org/chromium/chrome/browser/tasks/MostVisitedListProperties.java",
     "java/src/org/chromium/chrome/browser/tasks/TasksSurfaceMediator.java",
     "java/src/org/chromium/chrome/browser/tasks/TasksSurfaceCoordinator.java",
     "java/src/org/chromium/chrome/browser/tasks/TasksView.java",
diff --git a/chrome/android/features/tab_ui/java/res/layout/tasks_view_layout.xml b/chrome/android/features/tab_ui/java/res/layout/tasks_view_layout.xml
index 1f87ec2a..fb0b8eb 100644
--- a/chrome/android/features/tab_ui/java/res/layout/tasks_view_layout.xml
+++ b/chrome/android/features/tab_ui/java/res/layout/tasks_view_layout.xml
@@ -15,12 +15,12 @@
         android:layout_height="wrap_content"
         android:background="@color/modern_primary_color"
         android:visibility="gone"
-        android:scrollbars="none"
-        android:paddingBottom="@dimen/tasks_view_items_vertical_spacing">
+        android:scrollbars="none">
         <LinearLayout android:id="@+id/mv_tiles_layout"
             android:layout_width="wrap_content"
             android:layout_height="match_parent"
-            android:orientation="horizontal" />
+            android:orientation="horizontal"
+            android:paddingBottom="@dimen/tasks_view_items_vertical_spacing" />
     </HorizontalScrollView>
     <LinearLayout
         android:id="@+id/tab_switcher_title"
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/MostVisitedListCoordinator.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/MostVisitedListCoordinator.java
index b3e580b..dc4a3db 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/MostVisitedListCoordinator.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/MostVisitedListCoordinator.java
@@ -20,7 +20,6 @@
 import org.chromium.chrome.browser.suggestions.SuggestionsConfig;
 import org.chromium.chrome.browser.suggestions.SuggestionsDependencyFactory;
 import org.chromium.chrome.browser.suggestions.SuggestionsEventReporter;
-import org.chromium.chrome.browser.suggestions.SuggestionsRanker;
 import org.chromium.chrome.browser.suggestions.SuggestionsUiDelegate;
 import org.chromium.chrome.browser.suggestions.SuggestionsUiDelegateImpl;
 import org.chromium.chrome.browser.suggestions.tile.SuggestionsTileView;
@@ -31,27 +30,36 @@
 import org.chromium.chrome.browser.suggestions.tile.TileRenderer;
 import org.chromium.chrome.browser.suggestions.tile.TileSectionType;
 import org.chromium.ui.base.PageTransition;
+import org.chromium.ui.modelutil.PropertyModel;
+import org.chromium.ui.modelutil.PropertyModelChangeProcessor;
 
 /**
  * Coordinator for displaying a list of {@link SuggestionsTileView} in a {@link ViewGroup}.
  *
- * TODO(mattsimmons): Finish MVC of this. The way the renderer builds the layout complicates things.
+ * TODO(mattsimmons): Move logic and view manipulation into the mediator/viewbinder. (and add tests)
  */
 class MostVisitedListCoordinator implements TileGroup.Observer, TileGroup.TileSetupDelegate {
     private static final int TITLE_LINES = 1;
 
     // There's a limit of 12 in {@link MostVisitedSitesBridge#setObserver}.
     private static final int MAX_RESULTS = 12;
+    private PropertyModelChangeProcessor mModelChangeProcessor;
+    private MostVisitedListMediator mMediator;
     private TileGroup mTileGroup;
     private TileRenderer mRenderer;
     private ViewGroup mParent;
 
     public MostVisitedListCoordinator(ChromeActivity activity, ViewGroup parent) {
+        PropertyModel propertyModel = new PropertyModel(MostVisitedListProperties.ALL_KEYS);
+        mModelChangeProcessor = PropertyModelChangeProcessor.create(
+                propertyModel, parent, MostVisitedListViewBinder::bind);
+
+        mMediator = new MostVisitedListMediator(propertyModel, activity.getTabModelSelector());
+
         mParent = parent;
         Profile profile = Profile.getLastUsedProfile();
         SuggestionsSource suggestionsSource =
                 SuggestionsDependencyFactory.getInstance().createSuggestionSource(profile);
-        SuggestionsRanker suggestionsRanker = new SuggestionsRanker();
         SuggestionsEventReporter eventReporter =
                 SuggestionsDependencyFactory.getInstance().createEventReporter();
 
@@ -74,6 +82,10 @@
         mTileGroup.startObserving(MAX_RESULTS);
     }
 
+    void destroy() {
+        mMediator.destroy();
+    }
+
     private void updateTileIcon(Tile tile) {
         for (int i = 0; i < mParent.getChildCount(); i++) {
             View tileView = mParent.getChildAt(i);
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/MostVisitedListMediator.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/MostVisitedListMediator.java
new file mode 100644
index 0000000..2c18861b
--- /dev/null
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/MostVisitedListMediator.java
@@ -0,0 +1,37 @@
+// 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.tasks;
+
+import static org.chromium.chrome.browser.tasks.MostVisitedListProperties.IS_VISIBLE;
+
+import org.chromium.chrome.browser.tabmodel.EmptyTabModelSelectorObserver;
+import org.chromium.chrome.browser.tabmodel.TabModel;
+import org.chromium.chrome.browser.tabmodel.TabModelSelector;
+import org.chromium.chrome.browser.tabmodel.TabModelSelectorObserver;
+import org.chromium.ui.modelutil.PropertyModel;
+
+/** Mediator handling logic and model operations for most visited list. */
+class MostVisitedListMediator {
+    private final PropertyModel mModel;
+    private final TabModelSelector mTabModelSelector;
+    private final TabModelSelectorObserver mTabModelSelectorObserver;
+
+    MostVisitedListMediator(PropertyModel model, TabModelSelector tabModelSelector) {
+        mModel = model;
+        mTabModelSelector = tabModelSelector;
+
+        mTabModelSelectorObserver = new EmptyTabModelSelectorObserver() {
+            @Override
+            public void onTabModelSelected(TabModel newModel, TabModel oldModel) {
+                mModel.set(IS_VISIBLE, !newModel.isIncognito());
+            }
+        };
+        mTabModelSelector.addObserver(mTabModelSelectorObserver);
+    }
+
+    void destroy() {
+        mTabModelSelector.removeObserver(mTabModelSelectorObserver);
+    }
+}
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/MostVisitedListProperties.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/MostVisitedListProperties.java
new file mode 100644
index 0000000..b5b9af5
--- /dev/null
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/MostVisitedListProperties.java
@@ -0,0 +1,18 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.chrome.browser.tasks;
+
+import org.chromium.ui.modelutil.PropertyKey;
+import org.chromium.ui.modelutil.PropertyModel;
+
+/** View Properties related to displaying a most visited list. */
+final class MostVisitedListProperties {
+    private MostVisitedListProperties() {}
+
+    public static final PropertyModel.WritableBooleanPropertyKey IS_VISIBLE =
+            new PropertyModel.WritableBooleanPropertyKey();
+
+    public static final PropertyKey[] ALL_KEYS = new PropertyKey[] {IS_VISIBLE};
+}
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/MostVisitedListViewBinder.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/MostVisitedListViewBinder.java
new file mode 100644
index 0000000..fdc2793
--- /dev/null
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/MostVisitedListViewBinder.java
@@ -0,0 +1,22 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.chrome.browser.tasks;
+
+import static org.chromium.chrome.browser.tasks.MostVisitedListProperties.IS_VISIBLE;
+
+import android.view.View;
+import android.view.ViewGroup;
+
+import org.chromium.ui.modelutil.PropertyKey;
+import org.chromium.ui.modelutil.PropertyModel;
+
+/** Model-to-View binder for most visited list. Handles view manipulations. */
+final class MostVisitedListViewBinder {
+    public static void bind(PropertyModel model, ViewGroup viewGroup, PropertyKey propertyKey) {
+        if (IS_VISIBLE == propertyKey) {
+            viewGroup.setVisibility(model.get(IS_VISIBLE) ? View.VISIBLE : View.GONE);
+        }
+    }
+}
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/TasksSurfaceCoordinator.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/TasksSurfaceCoordinator.java
index 9805cab..8dcbee4 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/TasksSurfaceCoordinator.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/TasksSurfaceCoordinator.java
@@ -42,7 +42,6 @@
         mMediator = new TasksSurfaceMediator(
                 activity, propertyModel, isTabCarousel, activity.getOverviewModeBehavior());
 
-        // TODO(mattsimmons): Handle incognito/dark theming.
         LinearLayout mvTilesLayout = mView.findViewById(R.id.mv_tiles_layout);
         mMostVisitedList = new MostVisitedListCoordinator(activity, mvTilesLayout);
     }
@@ -71,5 +70,6 @@
     @Override
     public void destroy() {
         mMediator.destroy();
+        mMostVisitedList.destroy();
     }
 }
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/TasksViewBinder.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/TasksViewBinder.java
index 5316c49a..f967595 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/TasksViewBinder.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/TasksViewBinder.java
@@ -22,7 +22,6 @@
         } else if (propertyKey == MORE_TABS_CLICK_LISTENER) {
             view.setMoreTabsOnClickListener(model.get(MORE_TABS_CLICK_LISTENER));
         } else if (propertyKey == MV_TILES_VISIBLE) {
-            // TODO(mattsimmons): Hide these when in incognito mode.
             view.setMostVisitedVisibility(model.get(MV_TILES_VISIBLE) ? View.VISIBLE : View.GONE);
         } else if (propertyKey == TOP_PADDING) {
             view.setPadding(0, model.get(TOP_PADDING), 0, 0);
diff --git a/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/MostVisitedListMediatorUnitTest.java b/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/MostVisitedListMediatorUnitTest.java
new file mode 100644
index 0000000..ffd2e9b
--- /dev/null
+++ b/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/MostVisitedListMediatorUnitTest.java
@@ -0,0 +1,74 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.chrome.browser.tasks;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.verify;
+
+import static org.chromium.chrome.browser.tasks.MostVisitedListProperties.IS_VISIBLE;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Captor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import org.chromium.chrome.browser.tabmodel.TabModel;
+import org.chromium.chrome.browser.tabmodel.TabModelSelector;
+import org.chromium.chrome.browser.tabmodel.TabModelSelectorObserver;
+import org.chromium.testing.local.LocalRobolectricTestRunner;
+import org.chromium.ui.modelutil.PropertyModel;
+
+/**
+ * Tests for {@link MostVisitedListMediator}.
+ */
+@RunWith(LocalRobolectricTestRunner.class)
+public final class MostVisitedListMediatorUnitTest {
+    private final PropertyModel mModel = new PropertyModel(MostVisitedListProperties.ALL_KEYS);
+
+    @Mock
+    private TabModel mNewTabModel;
+
+    @Mock
+    private TabModelSelector mTabModelSelector;
+
+    @Captor
+    private ArgumentCaptor<TabModelSelectorObserver> mTabModelSelectorObserverCaptor;
+
+    private MostVisitedListMediator mMediator;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+
+        mMediator = new MostVisitedListMediator(mModel, mTabModelSelector);
+
+        verify(mTabModelSelector).addObserver(mTabModelSelectorObserverCaptor.capture());
+    }
+
+    @Test
+    public void incognitoTabModel_setsNotVisible() {
+        mModel.set(IS_VISIBLE, true);
+        doReturn(true).when(mNewTabModel).isIncognito();
+
+        mTabModelSelectorObserverCaptor.getValue().onTabModelSelected(mNewTabModel, null);
+
+        assertFalse(mModel.get(IS_VISIBLE));
+    }
+
+    @Test
+    public void regularTabModel_setsVisible() {
+        mModel.set(IS_VISIBLE, false);
+        doReturn(false).when(mNewTabModel).isIncognito();
+
+        mTabModelSelectorObserverCaptor.getValue().onTabModelSelected(mNewTabModel, null);
+
+        assertTrue(mModel.get(IS_VISIBLE));
+    }
+}
diff --git a/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/MostVisitedListViewBinderUnitTest.java b/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/MostVisitedListViewBinderUnitTest.java
new file mode 100644
index 0000000..af5f43b
--- /dev/null
+++ b/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/MostVisitedListViewBinderUnitTest.java
@@ -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.
+
+package org.chromium.chrome.browser.tasks;
+
+import static org.mockito.Mockito.verify;
+
+import static org.chromium.chrome.browser.tasks.MostVisitedListProperties.IS_VISIBLE;
+
+import android.view.View;
+import android.view.ViewGroup;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.mockito.MockitoAnnotations;
+
+import org.chromium.testing.local.LocalRobolectricTestRunner;
+import org.chromium.ui.modelutil.PropertyModel;
+
+/**
+ * Tests for {@link MostVisitedListViewBinder}.
+ */
+@RunWith(LocalRobolectricTestRunner.class)
+public final class MostVisitedListViewBinderUnitTest {
+    @Mock
+    private ViewGroup mViewGroup;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+    }
+
+    @Test
+    public void bind_setsVisibilityCorrectly() {
+        PropertyModel model = new PropertyModel(MostVisitedListProperties.ALL_KEYS);
+
+        Mockito.reset(mViewGroup);
+        model.set(IS_VISIBLE, true);
+        MostVisitedListViewBinder.bind(model, mViewGroup, IS_VISIBLE);
+        verify(mViewGroup).setVisibility(View.VISIBLE);
+
+        Mockito.reset(mViewGroup);
+        model.set(IS_VISIBLE, false);
+        MostVisitedListViewBinder.bind(model, mViewGroup, IS_VISIBLE);
+        verify(mViewGroup).setVisibility(View.GONE);
+    }
+}
diff --git a/chrome/android/features/tab_ui/tab_management_java_sources.gni b/chrome/android/features/tab_ui/tab_management_java_sources.gni
index 2fa5be7..410c8c0 100644
--- a/chrome/android/features/tab_ui/tab_management_java_sources.gni
+++ b/chrome/android/features/tab_ui/tab_management_java_sources.gni
@@ -30,6 +30,8 @@
 ]
 
 tab_management_junit_java_sources = [
+  "//chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/MostVisitedListMediatorUnitTest.java",
+  "//chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/MostVisitedListViewBinderUnitTest.java",
   "//chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_groups/TabGroupModelFilterUnitTest.java",
   "//chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/TabSwitcherMediatorUnitTest.java",
   "//chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/TabGridDialogMediatorUnitTest.java",
diff --git a/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/FeedAppLifecycle.java b/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/FeedAppLifecycle.java
index 39ea8fd..4403e57 100644
--- a/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/FeedAppLifecycle.java
+++ b/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/FeedAppLifecycle.java
@@ -181,7 +181,7 @@
             mDelayedInitializeStarted = true;
             boolean initFeed = ChromeFeatureList.getFieldTrialParamByFeatureAsBoolean(
                     ChromeFeatureList.INTEREST_FEED_CONTENT_SUGGESTIONS, "init_feed_after_startup",
-                    true);
+                    false);
             if (initFeed) {
                 DeferredStartupHandler.getInstance().addDeferredTask(() -> {
                     // Since this is being run asynchronously, it's possible #destroy() is called
diff --git a/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/FeedConfiguration.java b/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/FeedConfiguration.java
index 95b1b29..f78447f 100644
--- a/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/FeedConfiguration.java
+++ b/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/FeedConfiguration.java
@@ -119,7 +119,7 @@
 
     private static final String SPINNER_DELAY_MS = "spinner_delay";
     /** Default value for delay before showing a spinner. */
-    public static final long SPINNER_DELAY_MS_DEFAULT = 0;
+    public static final long SPINNER_DELAY_MS_DEFAULT = 500;
 
     private static final String SPINNER_MINIMUM_SHOW_TIME_MS = "spinner_minimum_show_time";
     /** Default value for how long spinners must be shown for. */
@@ -127,7 +127,7 @@
 
     private static final String STORAGE_MISS_THRESHOLD = "storage_miss_threshold";
     /** Default number of items that can be missing from a call to FeedStore before failing. */
-    public static final long STORAGE_MISS_THRESHOLD_DEFAULT = 4;
+    public static final long STORAGE_MISS_THRESHOLD_DEFAULT = 100;
 
     private static final String TRIGGER_IMMEDIATE_PAGINATION = "trigger_immediate_pagination";
     /** Default value for triggering immediate pagination. */
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchContext.java b/chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchContext.java
index 73ef7b6..5e4b951 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchContext.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchContext.java
@@ -11,6 +11,7 @@
 import org.chromium.base.Log;
 import org.chromium.base.VisibleForTesting;
 import org.chromium.base.annotations.CalledByNative;
+import org.chromium.base.annotations.NativeMethods;
 import org.chromium.content_public.browser.WebContents;
 
 /**
@@ -78,7 +79,7 @@
     private int mPreviousUserInteractions;
 
     /** A {@link ContextualSearchContext} that ignores changes to the selection. */
-    private static class ChangeIgnoringContext extends ContextualSearchContext {
+    static class ChangeIgnoringContext extends ContextualSearchContext {
         @Override
         void onSelectionChanged() {}
     }
@@ -91,7 +92,7 @@
      * @return A {@link ContextualSearchContext} or {@code null} if the insertion point happens to
      *         miss a word (e.g. it has non-word characters on both sides).
      */
-    static public @Nullable ContextualSearchContext getContextForInsertionPoint(
+    public static @Nullable ContextualSearchContext getContextForInsertionPoint(
             String surroundingText, int insertionPointOffset) {
         ContextualSearchContext context = new ChangeIgnoringContext();
         context.setSurroundingText(
@@ -117,7 +118,7 @@
      * Constructs a context that tracks the selection and some amount of page content.
      */
     ContextualSearchContext() {
-        mNativePointer = nativeInit();
+        mNativePointer = ContextualSearchContextJni.get().init(this);
         mHasSetResolveProperties = false;
     }
 
@@ -137,17 +138,18 @@
         mHomeCountry = homeCountry;
         mPreviousEventId = previousEventId;
         mPreviousUserInteractions = previousUserInteractions;
-        nativeSetResolveProperties(getNativePointer(), homeCountry, maySendBasePageUrl,
-                previousEventId, previousUserInteractions);
+        ContextualSearchContextJni.get().setResolveProperties(getNativePointer(), this, homeCountry,
+                maySendBasePageUrl, previousEventId, previousUserInteractions);
     }
 
     /**
      * This method should be called to clean up storage when an instance of this class is
-     * no longer in use.  The nativeDestroy will call the destructor on the native instance.
+     * no longer in use.  The ContextualSearchContextJni.get().destroy will call the destructor on
+     * the native instance.
      */
     public void destroy() {
         assert mNativePointer != 0;
-        nativeDestroy(mNativePointer);
+        ContextualSearchContextJni.get().destroy(mNativePointer, this);
         mNativePointer = 0;
 
         // Also zero out private data that may be sizable.
@@ -191,8 +193,8 @@
             onSelectionChanged();
         }
         if (setNative) {
-            nativeSetContent(getNativePointer(), mSurroundingText, mSelectionStartOffset,
-                    mSelectionEndOffset);
+            ContextualSearchContextJni.get().setContent(getNativePointer(), this, mSurroundingText,
+                    mSelectionStartOffset, mSelectionEndOffset);
         }
     }
 
@@ -283,7 +285,7 @@
         mSurroundingText = mInitialSelectedWord;
         mSelectionStartOffset = 0;
         mSelectionEndOffset = mSurroundingText.length();
-        nativeRestrictResolve(mNativePointer);
+        ContextualSearchContextJni.get().restrictResolve(mNativePointer, this);
     }
 
     /**
@@ -298,7 +300,8 @@
         mSelectionStartOffset += startAdjust;
         mSelectionEndOffset += endAdjust;
         updateInitialSelectedWord();
-        nativeAdjustSelection(getNativePointer(), startAdjust, endAdjust);
+        ContextualSearchContextJni.get().adjustSelection(
+                getNativePointer(), this, startAdjust, endAdjust);
         // Notify of changes.
         onSelectionChanged();
     }
@@ -340,7 +343,8 @@
     String getDetectedLanguage() {
         assert mSurroundingText != null;
         if (mDetectedLanguage == null) {
-            mDetectedLanguage = nativeDetectLanguage(mNativePointer);
+            mDetectedLanguage =
+                    ContextualSearchContextJni.get().detectLanguage(mNativePointer, this);
         }
         return mDetectedLanguage;
     }
@@ -567,25 +571,18 @@
         return mNativePointer;
     }
 
-    // ============================================================================================
-    // Native methods.
-    // ============================================================================================
-    @VisibleForTesting
-    protected native long nativeInit();
-    @VisibleForTesting
-    protected native void nativeDestroy(long nativeContextualSearchContext);
-    @VisibleForTesting
-    protected native void nativeSetResolveProperties(long nativeContextualSearchContext,
-            String homeCountry, boolean maySendBasePageUrl, long previousEventId,
-            int previousEventResults);
-    @VisibleForTesting
-    protected native void nativeAdjustSelection(
-            long nativeContextualSearchContext, int startAdjust, int endAdjust);
-    @VisibleForTesting
-    protected native void nativeSetContent(long nativeContextualSearchContext, String content,
-            int selectionStart, int selectionEnd);
-    @VisibleForTesting
-    protected native String nativeDetectLanguage(long nativeContextualSearchContext);
-    @VisibleForTesting
-    protected native void nativeRestrictResolve(long nativeContextualSearchContext);
+    @NativeMethods
+    interface Natives {
+        long init(ContextualSearchContext caller);
+        void destroy(long nativeContextualSearchContext, ContextualSearchContext caller);
+        void setResolveProperties(long nativeContextualSearchContext,
+                ContextualSearchContext caller, String homeCountry, boolean maySendBasePageUrl,
+                long previousEventId, int previousEventResults);
+        void adjustSelection(long nativeContextualSearchContext, ContextualSearchContext caller,
+                int startAdjust, int endAdjust);
+        void setContent(long nativeContextualSearchContext, ContextualSearchContext caller,
+                String content, int selectionStart, int selectionEnd);
+        String detectLanguage(long nativeContextualSearchContext, ContextualSearchContext caller);
+        void restrictResolve(long nativeContextualSearchContext, ContextualSearchContext caller);
+    }
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchManager.java b/chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchManager.java
index 9f7abb0..0d068e6 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchManager.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchManager.java
@@ -19,6 +19,7 @@
 import org.chromium.base.TimeUtils;
 import org.chromium.base.VisibleForTesting;
 import org.chromium.base.annotations.CalledByNative;
+import org.chromium.base.annotations.NativeMethods;
 import org.chromium.base.metrics.RecordUserAction;
 import org.chromium.chrome.R;
 import org.chromium.chrome.browser.ChromeActivity;
@@ -250,7 +251,7 @@
      * @param parentView The parent view to attach Contextual Search UX to.
      */
     public void initialize(ViewGroup parentView) {
-        mNativeContextualSearchManagerPtr = nativeInit();
+        mNativeContextualSearchManagerPtr = ContextualSearchManagerJni.get().init(this);
 
         mParentView = parentView;
         mParentView.getViewTreeObserver().addOnGlobalFocusChangeListener(mOnFocusChangeListener);
@@ -279,7 +280,7 @@
 
         hideContextualSearch(StateChangeReason.UNKNOWN);
         mParentView.getViewTreeObserver().removeOnGlobalFocusChangeListener(mOnFocusChangeListener);
-        nativeDestroy(mNativeContextualSearchManagerPtr);
+        ContextualSearchManagerJni.get().destroy(mNativeContextualSearchManagerPtr, this);
         stopListeningForHideNotifications();
         mTabRedirectHandler.clear();
         mInternalStateController.enter(InternalState.UNDEFINED);
@@ -488,8 +489,8 @@
         WebContents baseWebContents = getBaseWebContents();
         if (baseWebContents != null && mContext != null && mContext.canResolve()) {
             if (isRestrictedResolve) mContext.setRestrictedResolve();
-            nativeStartSearchTermResolutionRequest(
-                    mNativeContextualSearchManagerPtr, mContext, getBaseWebContents());
+            ContextualSearchManagerJni.get().startSearchTermResolutionRequest(
+                    mNativeContextualSearchManagerPtr, this, mContext, getBaseWebContents());
         } else {
             // Something went wrong and we couldn't resolve.
             hideContextualSearch(StateChangeReason.UNKNOWN);
@@ -609,11 +610,10 @@
     }
 
     /**
-     * Called in response to the
-     * {@link ContextualSearchManager#nativeStartSearchTermResolutionRequest} method.
-     * If {@code nativeStartSearchTermResolutionRequest} is called with a previous request sill
-     * pending our native delegate is supposed to cancel all previous requests.  So this code
-     * should only be called with data corresponding to the most recent request.
+     * Called in response to the {@link ContextualSearchManagerJni#startSearchTermResolutionRequest}
+     * method. If {@code startSearchTermResolutionRequest} is called with a previous request sill
+     * pending our native delegate is supposed to cancel all previous requests.  So this code should
+     * only be called with data corresponding to the most recent request.
      * @param isNetworkUnavailable Indicates if the network is unavailable, in which case all other
      *        parameters should be ignored.
      * @param responseCode The HTTP response code. If the code is not OK, the query should be
@@ -787,7 +787,8 @@
         mLoadedSearchUrlTimeMs = System.currentTimeMillis();
         mLastSearchRequestLoaded = mSearchRequest;
         String searchUrl = mSearchRequest.getSearchUrl();
-        nativeWhitelistContextualSearchJsApiUrl(mNativeContextualSearchManagerPtr, searchUrl);
+        ContextualSearchManagerJni.get().whitelistContextualSearchJsApiUrl(
+                mNativeContextualSearchManagerPtr, this, searchUrl);
         mSearchPanel.loadUrlInPanel(searchUrl);
         mDidStartLoadingResolvedSearchRequest = true;
 
@@ -948,12 +949,14 @@
 
     @Override
     public String getAcceptLanguages() {
-        return nativeGetAcceptLanguages(mNativeContextualSearchManagerPtr);
+        return ContextualSearchManagerJni.get().getAcceptLanguages(
+                mNativeContextualSearchManagerPtr, this);
     }
 
     @Override
     public String getTranslateServiceTargetLanguage() {
-        return nativeGetTargetLanguage(mNativeContextualSearchManagerPtr);
+        return ContextualSearchManagerJni.get().getTargetLanguage(
+                mNativeContextualSearchManagerPtr, this);
     }
 
     // ============================================================================================
@@ -1036,8 +1039,9 @@
 
         @Override
         public void onContentViewCreated() {
-            nativeEnableContextualSearchJsApiForWebContents(
-                    mNativeContextualSearchManagerPtr, getSearchPanelWebContents());
+            ContextualSearchManagerJni.get().enableContextualSearchJsApiForWebContents(
+                    mNativeContextualSearchManagerPtr, ContextualSearchManager.this,
+                    getSearchPanelWebContents());
         }
 
         @Override
@@ -1575,8 +1579,9 @@
                 if (webContents != null) {
                     mInternalStateController.notifyStartingWorkOn(
                             InternalState.GATHERING_SURROUNDINGS);
-                    nativeGatherSurroundingText(
-                            mNativeContextualSearchManagerPtr, mContext, webContents);
+                    ContextualSearchManagerJni.get().gatherSurroundingText(
+                            mNativeContextualSearchManagerPtr, ContextualSearchManager.this,
+                            mContext, webContents);
                 } else {
                     mInternalStateController.reset(StateChangeReason.UNKNOWN);
                 }
@@ -1818,21 +1823,26 @@
         return mContext;
     }
 
-    // ============================================================================================
-    // Native calls
-    // ============================================================================================
+    @NativeMethods
+    interface Natives {
+        long init(ContextualSearchManager caller);
 
-    private native long nativeInit();
-    private native void nativeDestroy(long nativeContextualSearchManager);
-    private native void nativeStartSearchTermResolutionRequest(long nativeContextualSearchManager,
-            ContextualSearchContext contextualSearchContext, WebContents baseWebContents);
-    protected native void nativeGatherSurroundingText(long nativeContextualSearchManager,
-            ContextualSearchContext contextualSearchContext, WebContents baseWebContents);
-    private native void nativeWhitelistContextualSearchJsApiUrl(
-            long nativeContextualSearchManager, String url);
-    private native void nativeEnableContextualSearchJsApiForWebContents(
-            long nativeContextualSearchManager, WebContents overlayWebContents);
-    // Don't call these directly, instead call the private methods that cache the results.
-    private native String nativeGetTargetLanguage(long nativeContextualSearchManager);
-    private native String nativeGetAcceptLanguages(long nativeContextualSearchManager);
+        void destroy(long nativeContextualSearchManager, ContextualSearchManager caller);
+        void startSearchTermResolutionRequest(long nativeContextualSearchManager,
+                ContextualSearchManager caller, ContextualSearchContext contextualSearchContext,
+                WebContents baseWebContents);
+        void gatherSurroundingText(long nativeContextualSearchManager,
+                ContextualSearchManager caller, ContextualSearchContext contextualSearchContext,
+                WebContents baseWebContents);
+        void whitelistContextualSearchJsApiUrl(
+                long nativeContextualSearchManager, ContextualSearchManager caller, String url);
+        void enableContextualSearchJsApiForWebContents(long nativeContextualSearchManager,
+                ContextualSearchManager caller, WebContents overlayWebContents);
+        // Don't call these directly, instead call the private methods that cache the results.
+        String getTargetLanguage(
+                long nativeContextualSearchManager, ContextualSearchManager caller);
+
+        String getAcceptLanguages(
+                long nativeContextualSearchManager, ContextualSearchManager caller);
+    }
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadManagerService.java b/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadManagerService.java
index 364e26f0..bcef872 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadManagerService.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadManagerService.java
@@ -389,7 +389,10 @@
 
     @Override
     public void onDownloadCancelled(final DownloadInfo downloadInfo) {
-        DownloadItem item = new DownloadItem(false, downloadInfo);
+        DownloadInfo newInfo = DownloadInfo.Builder.fromDownloadInfo(downloadInfo)
+                                       .setState(DownloadState.CANCELLED)
+                                       .build();
+        DownloadItem item = new DownloadItem(false, newInfo);
         removeAutoResumableDownload(item.getId());
         updateDownloadProgress(new DownloadItem(false, downloadInfo), DownloadStatus.CANCELLED);
         updateDownloadInfoBar(item);
@@ -1129,6 +1132,8 @@
             removeDownloadProgress(id.id);
         } else {
             mDownloadNotifier.notifyDownloadCanceled(id);
+            DownloadInfoBarController infoBarController = getInfoBarController(isOffTheRecord);
+            if (infoBarController != null) infoBarController.onDownloadItemRemoved(id);
         }
         recordDownloadFinishedUMA(DownloadStatus.CANCELLED, id.id, 0);
         maybeRecordBackgroundDownload(UmaBackgroundDownload.CANCELLED, id.id);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/payments/PaymentRequestImpl.java b/chrome/android/java/src/org/chromium/chrome/browser/payments/PaymentRequestImpl.java
index e7ad945..368c5e7 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/payments/PaymentRequestImpl.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/payments/PaymentRequestImpl.java
@@ -194,6 +194,12 @@
          * Called when the payment response is ready.
          */
         void onPaymentResponseReady();
+
+        /**
+         * Called when the browser acknowledges the renderer's complete call, which indicates that
+         * the browser UI has closed.
+         */
+        void onCompleteReplied();
     }
 
     /**
@@ -2547,7 +2553,10 @@
 
         if (mUI != null) {
             mUI.close(immediateClose, () -> {
-                if (mClient != null) mClient.onComplete();
+                if (mClient != null) {
+                    if (sObserverForTest != null) sObserverForTest.onCompleteReplied();
+                    mClient.onComplete();
+                }
                 closeClient();
             });
             mUI = null;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/payments/ui/DimmingDialog.java b/chrome/android/java/src/org/chromium/chrome/browser/payments/ui/DimmingDialog.java
index 10a6756..fc77baf 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/payments/ui/DimmingDialog.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/payments/ui/DimmingDialog.java
@@ -31,6 +31,9 @@
 import org.chromium.chrome.browser.ui.widget.animation.Interpolators;
 import org.chromium.chrome.browser.util.ColorUtils;
 
+import java.util.ArrayList;
+import java.util.Collection;
+
 /**
  * A fullscreen semitransparent dialog used for dimming Chrome when overlaying a bottom sheet
  * dialog/CCT or an alert dialog on top of it. FLAG_DIM_BEHIND is not being used because it causes
@@ -196,25 +199,31 @@
         public DisappearingAnimator(boolean removeDialog) {
             mIsDialogClosing = removeDialog;
 
-            View child = mFullContainer.getChildAt(0);
-            assert child != null;
+            Collection<Animator> animators = new ArrayList<>();
 
-            Animator sheetFader = ObjectAnimator.ofFloat(child, View.ALPHA, child.getAlpha(), 0f);
-            Animator sheetTranslator =
-                    ObjectAnimator.ofFloat(child, View.TRANSLATION_Y, 0f, mAnimatorTranslation);
+            View child = mFullContainer.getChildAt(0);
+            if (child != null) {
+                // Sheet fader.
+                animators.add(ObjectAnimator.ofFloat(child, View.ALPHA, child.getAlpha(), 0f));
+                // Sheet translator.
+                animators.add(ObjectAnimator.ofFloat(
+                        child, View.TRANSLATION_Y, 0f, mAnimatorTranslation));
+            }
+
+            if (mIsDialogClosing) {
+                // Scrim fader.
+                animators.add(ObjectAnimator.ofInt(mFullContainer.getBackground(),
+                        AnimatorProperties.DRAWABLE_ALPHA_PROPERTY, 127, 0));
+            }
+
+            if (animators.isEmpty()) return;
+
+            mIsAnimatingDisappearance = true;
 
             AnimatorSet current = new AnimatorSet();
             current.setDuration(DIALOG_EXIT_ANIMATION_MS);
             current.setInterpolator(Interpolators.FAST_OUT_LINEAR_IN_INTERPOLATOR);
-            if (mIsDialogClosing) {
-                Animator scrimFader = ObjectAnimator.ofInt(mFullContainer.getBackground(),
-                        AnimatorProperties.DRAWABLE_ALPHA_PROPERTY, 127, 0);
-                current.playTogether(sheetFader, sheetTranslator, scrimFader);
-            } else {
-                current.playTogether(sheetFader, sheetTranslator);
-            }
-
-            mIsAnimatingDisappearance = true;
+            current.playTogether(animators);
             current.addListener(this);
             current.start();
         }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/preferences/MainPreferences.java b/chrome/android/java/src/org/chromium/chrome/browser/preferences/MainPreferences.java
index 1332f432..374daeb 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/preferences/MainPreferences.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/preferences/MainPreferences.java
@@ -272,7 +272,7 @@
     }
 
     private void setOnOffSummary(Preference pref, boolean isOn) {
-        pref.setSummary(getResources().getString(isOn ? R.string.text_on : R.string.text_off));
+        pref.setSummary(isOn ? R.string.text_on : R.string.text_off);
     }
 
     // SigninManager.SignInStateObserver implementation.
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/preferences/password/SavePasswordsPreferences.java b/chrome/android/java/src/org/chromium/chrome/browser/preferences/password/SavePasswordsPreferences.java
index 060b585..4175783 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/preferences/password/SavePasswordsPreferences.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/preferences/password/SavePasswordsPreferences.java
@@ -300,7 +300,7 @@
                 getPreferenceScreen().removePreference(passwordParent);
             } else {
                 getView().announceForAccessibility(
-                        getResources().getText(R.string.accessible_find_in_page_no_results));
+                        getString(R.string.accessible_find_in_page_no_results));
             }
         }
     }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/preferences/privacy/PrivacyPreferences.java b/chrome/android/java/src/org/chromium/chrome/browser/preferences/privacy/PrivacyPreferences.java
index f8c725ae..f3d8b29 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/preferences/privacy/PrivacyPreferences.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/preferences/privacy/PrivacyPreferences.java
@@ -99,9 +99,6 @@
     public void updateSummaries() {
         PrefServiceBridge prefServiceBridge = PrefServiceBridge.getInstance();
 
-        CharSequence textOn = getActivity().getResources().getText(R.string.text_on);
-        CharSequence textOff = getActivity().getResources().getText(R.string.text_off);
-
         CheckBoxPreference canMakePaymentPref =
                 (CheckBoxPreference) findPreference(PREF_CAN_MAKE_PAYMENT);
         if (canMakePaymentPref != null) {
@@ -111,7 +108,8 @@
 
         Preference doNotTrackPref = findPreference(PREF_DO_NOT_TRACK);
         if (doNotTrackPref != null) {
-            doNotTrackPref.setSummary(prefServiceBridge.isDoNotTrackEnabled() ? textOn : textOff);
+            doNotTrackPref.setSummary(
+                    prefServiceBridge.isDoNotTrackEnabled() ? R.string.text_on : R.string.text_off);
         }
 
         Preference usageStatsPref = findPreference(PREF_USAGE_STATS);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/preferences/sync/AccountManagementFragment.java b/chrome/android/java/src/org/chromium/chrome/browser/preferences/sync/AccountManagementFragment.java
index b65a74d..dbc720f3 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/preferences/sync/AccountManagementFragment.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/preferences/sync/AccountManagementFragment.java
@@ -10,7 +10,6 @@
 import android.app.ProgressDialog;
 import android.content.Context;
 import android.content.Intent;
-import android.content.res.Resources;
 import android.graphics.Bitmap;
 import android.graphics.BitmapFactory;
 import android.graphics.Point;
@@ -36,7 +35,6 @@
 import org.chromium.chrome.browser.preferences.PreferencesLauncher;
 import org.chromium.chrome.browser.profiles.Profile;
 import org.chromium.chrome.browser.profiles.ProfileAccountManagementMetrics;
-import org.chromium.chrome.browser.signin.ConfirmManagedSyncDataDialog;
 import org.chromium.chrome.browser.signin.IdentityServicesProvider;
 import org.chromium.chrome.browser.signin.ProfileDataCache;
 import org.chromium.chrome.browser.signin.SignOutDialogFragment;
@@ -62,8 +60,7 @@
  * Note: This can be triggered from a web page, e.g. a GAIA sign-in page.
  */
 public class AccountManagementFragment extends PreferenceFragmentCompat
-        implements SignOutDialogListener, SignInStateObserver,
-                   ConfirmManagedSyncDataDialog.Listener, ProfileDataCache.Observer {
+        implements SignOutDialogListener, SignInStateObserver, ProfileDataCache.Observer {
     private static final String TAG = "AcctManagementPref";
 
     public static final String SIGN_OUT_DIALOG_TAG = "sign_out_dialog_tag";
@@ -213,24 +210,13 @@
                     SigninUtils.logEvent(
                             ProfileAccountManagementMetrics.TOGGLE_SIGNOUT, mGaiaServiceType);
 
-                    String managementDomain =
-                            IdentityServicesProvider.getSigninManager().getManagementDomain();
-                    if (managementDomain != null) {
-                        // Show the 'You are signing out of a managed account' dialog.
+                    SignOutDialogFragment signOutFragment = new SignOutDialogFragment();
+                    Bundle args = new Bundle();
+                    args.putInt(SHOW_GAIA_SERVICE_TYPE_EXTRA, mGaiaServiceType);
+                    signOutFragment.setArguments(args);
 
-                        ConfirmManagedSyncDataDialog.showSignOutFromManagedAccountDialog(
-                                AccountManagementFragment.this, getFragmentManager(),
-                                getResources(), managementDomain);
-                    } else {
-                        // Show the 'You are signing out' dialog.
-                        SignOutDialogFragment signOutFragment = new SignOutDialogFragment();
-                        Bundle args = new Bundle();
-                        args.putInt(SHOW_GAIA_SERVICE_TYPE_EXTRA, mGaiaServiceType);
-                        signOutFragment.setArguments(args);
-
-                        signOutFragment.setTargetFragment(AccountManagementFragment.this, 0);
-                        signOutFragment.show(getFragmentManager(), SIGN_OUT_DIALOG_TAG);
-                    }
+                    signOutFragment.setTargetFragment(AccountManagementFragment.this, 0);
+                    signOutFragment.show(getFragmentManager(), SIGN_OUT_DIALOG_TAG);
 
                     return true;
                 }
@@ -244,7 +230,6 @@
         Preference parentAccounts = findPreference(PREF_PARENT_ACCOUNTS);
         Preference childContent = findPreference(PREF_CHILD_CONTENT);
         if (mProfile.isChild()) {
-            Resources res = getActivity().getResources();
             PrefServiceBridge prefService = PrefServiceBridge.getInstance();
 
             String firstParent = prefService.getSupervisedUserCustodianEmail();
@@ -252,13 +237,12 @@
             String parentText;
 
             if (!secondParent.isEmpty()) {
-                parentText = res.getString(
+                parentText = getString(
                         R.string.account_management_two_parent_names, firstParent, secondParent);
             } else if (!firstParent.isEmpty()) {
-                parentText =
-                        res.getString(R.string.account_management_one_parent_name, firstParent);
+                parentText = getString(R.string.account_management_one_parent_name, firstParent);
             } else {
-                parentText = res.getString(R.string.account_management_no_parental_data);
+                parentText = getString(R.string.account_management_no_parental_data);
             }
             parentAccounts.setSummary(parentText);
 
@@ -415,17 +399,6 @@
         }
     }
 
-    // ConfirmManagedSyncDataDialog.Listener implementation
-    @Override
-    public void onConfirm() {
-        onSignOutClicked(false);
-    }
-
-    @Override
-    public void onCancel() {
-        onSignOutDialogDismissed(false);
-    }
-
     // SignInStateObserver implementation:
 
     @Override
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 3ae9d01..a09deb9e 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
@@ -7,7 +7,6 @@
 import android.accounts.Account;
 import android.app.Dialog;
 import android.content.Intent;
-import android.content.res.Resources;
 import android.net.Uri;
 import android.os.Bundle;
 import android.provider.Settings;
@@ -461,13 +460,12 @@
      * @param error The sync error.
      */
     private String getSyncErrorTitle(@SyncError int error) {
-        Resources res = getActivity().getResources();
         switch (error) {
             case SyncError.SYNC_SETUP_INCOMPLETE:
                 assert ChromeFeatureList.isEnabled(ChromeFeatureList.SYNC_MANUAL_START_ANDROID);
-                return res.getString(R.string.sync_settings_not_confirmed_title);
+                return getString(R.string.sync_settings_not_confirmed_title);
             default:
-                return res.getString(R.string.sync_error_card_title);
+                return getString(R.string.sync_error_card_title);
         }
     }
 
@@ -476,22 +474,21 @@
      * @param error The sync error.
      */
     private String getSyncErrorHint(@SyncError int error) {
-        Resources res = getActivity().getResources();
         switch (error) {
             case SyncError.ANDROID_SYNC_DISABLED:
-                return res.getString(R.string.hint_android_sync_disabled);
+                return getString(R.string.hint_android_sync_disabled);
             case SyncError.AUTH_ERROR:
-                return res.getString(R.string.hint_sync_auth_error);
+                return getString(R.string.hint_sync_auth_error);
             case SyncError.CLIENT_OUT_OF_DATE:
-                return res.getString(
+                return getString(
                         R.string.hint_client_out_of_date, BuildInfo.getInstance().hostPackageLabel);
             case SyncError.OTHER_ERRORS:
-                return res.getString(R.string.hint_other_sync_errors);
+                return getString(R.string.hint_other_sync_errors);
             case SyncError.PASSPHRASE_REQUIRED:
-                return res.getString(R.string.hint_passphrase_required);
+                return getString(R.string.hint_passphrase_required);
             case SyncError.SYNC_SETUP_INCOMPLETE:
                 assert ChromeFeatureList.isEnabled(ChromeFeatureList.SYNC_MANUAL_START_ANDROID);
-                return res.getString(R.string.hint_sync_settings_not_confirmed_description);
+                return getString(R.string.hint_sync_settings_not_confirmed_description);
             case SyncError.NO_ERROR:
             default:
                 return null;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/preferences/themes/ThemePreferences.java b/chrome/android/java/src/org/chromium/chrome/browser/preferences/themes/ThemePreferences.java
index 51dc3a7a..c9612a2 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/preferences/themes/ThemePreferences.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/preferences/themes/ThemePreferences.java
@@ -48,7 +48,7 @@
     @Override
     public void onCreatePreferences(@Nullable Bundle savedInstanceState, String rootKey) {
         PreferenceUtils.addPreferencesFromResource(this, R.xml.theme_preferences);
-        getActivity().setTitle(getResources().getString(R.string.prefs_themes));
+        getActivity().setTitle(R.string.prefs_themes);
 
         ChromePreferenceManager chromePreferenceManager = ChromePreferenceManager.getInstance();
         RadioButtonGroupThemePreference radioButtonGroupThemePreference =
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/preferences/website/SingleCategoryPreferences.java b/chrome/android/java/src/org/chromium/chrome/browser/preferences/website/SingleCategoryPreferences.java
index 25774286..1d5d833b 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/preferences/website/SingleCategoryPreferences.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/preferences/website/SingleCategoryPreferences.java
@@ -8,7 +8,6 @@
 
 import android.content.Context;
 import android.content.DialogInterface;
-import android.content.res.Resources;
 import android.os.Build;
 import android.os.Bundle;
 import android.support.graphics.drawable.VectorDrawableCompat;
@@ -254,8 +253,7 @@
     }
 
     private CharSequence getHeaderTitle(int resourceId, int count) {
-        SpannableStringBuilder spannable =
-                new SpannableStringBuilder(getResources().getString(resourceId));
+        SpannableStringBuilder spannable = new SpannableStringBuilder(getString(resourceId));
         String prefCount = String.format(Locale.getDefault(), " - %d", count);
         spannable.append(prefCount);
 
@@ -452,8 +450,7 @@
                 });
         builder.setNegativeButton(R.string.cancel, null);
         builder.setTitle(R.string.storage_clear_site_storage_title);
-        Resources res = getResources();
-        String dialogFormattedText = res.getString(R.string.storage_clear_dialog_text,
+        String dialogFormattedText = getString(R.string.storage_clear_dialog_text,
                 Formatter.formatShortFileSize(getActivity(), totalUsage));
         builder.setMessage(dialogFormattedText);
         builder.create().show();
@@ -552,7 +549,7 @@
                     : R.string.website_settings_add_site_description_cookies_allow;
         }
         assert resource > 0;
-        return getResources().getString(resource);
+        return getString(resource);
     }
 
     // OnPreferenceClickListener:
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/preferences/website/SingleWebsitePreferences.java b/chrome/android/java/src/org/chromium/chrome/browser/preferences/website/SingleWebsitePreferences.java
index ec245ea..71a4007 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/preferences/website/SingleWebsitePreferences.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/preferences/website/SingleWebsitePreferences.java
@@ -8,7 +8,6 @@
 import android.content.Context;
 import android.content.DialogInterface;
 import android.content.Intent;
-import android.content.res.Resources;
 import android.net.Uri;
 import android.os.Build;
 import android.os.Bundle;
@@ -434,9 +433,7 @@
         if (managedBy != null) {
             final Intent notificationSettingsIntent =
                     getNotificationSettingsIntent(manager.getDelegatePackageName(origin));
-            String summaryText = String.format(
-                    getResources().getString(R.string.website_notification_managed_by_app),
-                    managedBy);
+            String summaryText = getString(R.string.website_notification_managed_by_app, managedBy);
             ChromeImageViewPreference newPreference =
                     replaceWithReadOnlyCopyOf(preference, summaryText);
             setupNotificationManagedByPreference(newPreference, notificationSettingsIntent);
@@ -459,13 +456,11 @@
             String overrideSummary;
             if (isPermissionControlledByDSE(
                         ContentSettingsType.CONTENT_SETTINGS_TYPE_NOTIFICATIONS)) {
-                overrideSummary = getResources().getString(
-                        value != null && value == ContentSettingValues.ALLOW
+                overrideSummary = getString(value != null && value == ContentSettingValues.ALLOW
                                 ? R.string.website_settings_permissions_allow_dse
                                 : R.string.website_settings_permissions_block_dse);
             } else {
-                overrideSummary =
-                        getResources().getString(ContentSettingsResources.getSiteSummary(value));
+                overrideSummary = getString(ContentSettingsResources.getSiteSummary(value));
             }
 
             // On Android O this preference is read-only, so we replace the existing pref with a
@@ -707,10 +702,10 @@
         CharSequence[] descriptions = new String[2];
         keys[0] = ContentSetting.toString(ContentSettingValues.ALLOW);
         keys[1] = ContentSetting.toString(ContentSettingValues.BLOCK);
-        descriptions[0] = getResources().getString(
-                ContentSettingsResources.getSiteSummary(ContentSettingValues.ALLOW));
-        descriptions[1] = getResources().getString(
-                ContentSettingsResources.getSiteSummary(ContentSettingValues.BLOCK));
+        descriptions[0] =
+                getString(ContentSettingsResources.getSiteSummary(ContentSettingValues.ALLOW));
+        descriptions[1] =
+                getString(ContentSettingsResources.getSiteSummary(ContentSettingValues.BLOCK));
         listPreference.setEntryValues(keys);
         listPreference.setEntries(descriptions);
         // TODO(crbug.com/735110): Figure out if this is the correct thing to do - here we are
@@ -815,10 +810,9 @@
 
         // The subresource filter permission has a custom BLOCK string.
         ListPreference listPreference = (ListPreference) preference;
-        Resources res = getResources();
         listPreference.setEntries(
-                new String[] {res.getString(R.string.website_settings_permissions_allow),
-                        res.getString(R.string.website_settings_permissions_ads_block)});
+                new String[] {getString(R.string.website_settings_permissions_allow),
+                        getString(R.string.website_settings_permissions_ads_block)});
         listPreference.setValueIndex(permission == ContentSettingValues.ALLOW ? 0 : 1);
     }
 
@@ -838,10 +832,9 @@
      */
     private void updatePreferenceForDSESetting(Preference preference) {
         ListPreference listPreference = (ListPreference) preference;
-        Resources res = getResources();
         listPreference.setEntries(new String[] {
-                res.getString(R.string.website_settings_permissions_allow_dse),
-                res.getString(R.string.website_settings_permissions_block_dse),
+                getString(R.string.website_settings_permissions_allow_dse),
+                getString(R.string.website_settings_permissions_block_dse),
         });
     }
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/signin/ConfirmManagedSyncDataDialog.java b/chrome/android/java/src/org/chromium/chrome/browser/signin/ConfirmManagedSyncDataDialog.java
index 305c3a586..f7d4134 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/signin/ConfirmManagedSyncDataDialog.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/signin/ConfirmManagedSyncDataDialog.java
@@ -66,22 +66,6 @@
         String desc = resources.getString(R.string.sign_in_managed_account_description, domain);
         showNewInstance(title, desc, positive, negative, fragmentManager, callback);
     }
-    /**
-     * Create the dialog to show when signing out of a managed account (but not switching from a
-     * managed account).
-     * @param callback Callback for result.
-     * @param fragmentManager FragmentManaged to display the dialog.
-     * @param resources Resources to load the strings.
-     * @param domain The domain of the managed account.
-     */
-    public static void showSignOutFromManagedAccountDialog(Listener callback,
-            FragmentManager fragmentManager, Resources resources, String domain) {
-        String title = resources.getString(R.string.sign_out_managed_account);
-        String positive = resources.getString(R.string.accept_and_sign_out);
-        String negative = resources.getString(R.string.cancel);
-        String desc = resources.getString(R.string.sign_out_managed_account_description, domain);
-        showNewInstance(title, desc, positive, negative, fragmentManager, callback);
-    }
 
     /**
      * Create the dialog to show when switching from a managed account.
diff --git a/chrome/android/java/strings/android_chrome_strings.grd b/chrome/android/java/strings/android_chrome_strings.grd
index aa9e21f..ac3f993 100644
--- a/chrome/android/java/strings/android_chrome_strings.grd
+++ b/chrome/android/java/strings/android_chrome_strings.grd
@@ -1636,15 +1636,9 @@
       <message name="IDS_SIGN_IN_MANAGED_ACCOUNT_DESCRIPTION" desc="Description for signing in to managed accounts">
         You are signing in with an account managed by <ph name="MANAGED_DOMAIN">%1$s<ex>Google</ex></ph> and giving its administrator control over your Chrome data. Your data will become permanently tied to this account. Signing out of Chrome will delete your data from this device, but it will remain stored in your Google Account.
       </message>
-      <message name="IDS_SIGN_OUT_MANAGED_ACCOUNT_DESCRIPTION" desc="Description for signing out of a managed account">
-        You are signing out of an account managed by <ph name="MANAGED_DOMAIN">%1$s<ex>Google</ex></ph>. This will delete your Chrome data from this device, but your data will remain in your Google Account.
-      </message>
       <message name="IDS_SWITCH_FROM_MANAGED_ACCOUNT_DESCRIPTION" desc="Description for switching from a managed account">
         You are switching sync accounts from <ph name="ACCOUNT_EMAIL_OLD">%1$s<ex>user@example.com</ex></ph> to <ph name="ACCOUNT_EMAIL_NEW">%2$s<ex>user@example.com</ex></ph>. Your existing Chrome data is managed by <ph name="MANAGED_DOMAIN">%3$s<ex>Google</ex></ph>. This will delete your data from this device, but your data will remain in <ph name="ACCOUNT_EMAIL_OLD">%1$s<ex>user@example.com</ex></ph>.
       </message>
-      <message name="IDS_ACCEPT_AND_SIGN_OUT" desc="Button displayed for user to confirm and sign out of a managed account">
-        Accept and sign out
-      </message>
       <message name="IDS_ACCEPT_AND_SWITCH_ACCOUNTS" desc="Button displayed for user to confirm and switch from a managed account">
         Accept and switch accounts
       </message>
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchTapEventTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchTapEventTest.java
index afe89ac..83549c8d 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchTapEventTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchTapEventTest.java
@@ -13,9 +13,12 @@
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
 
 import org.chromium.base.test.util.CommandLineFlags;
 import org.chromium.base.test.util.Feature;
+import org.chromium.base.test.util.JniMocker;
 import org.chromium.base.test.util.Restriction;
 import org.chromium.chrome.browser.ChromeActivity;
 import org.chromium.chrome.browser.ChromeSwitches;
@@ -49,6 +52,12 @@
     public ChromeActivityTestRule<ChromeActivity> mActivityTestRule =
             new ChromeActivityTestRule<>(ChromeActivity.class);
 
+    @Rule
+    public JniMocker mocker = new JniMocker();
+
+    @Mock
+    ContextualSearchManager.Natives mContextualSearchManagerJniMock;
+
     private ContextualSearchManagerWrapper mContextualSearchManager;
     private ContextualSearchPanel mPanel;
     private OverlayPanelManagerWrapper mPanelManager;
@@ -105,10 +114,6 @@
                     "", "", "", "", QuickActionCategory.NONE, 0, "", "", 0);
         }
 
-        @Override
-        protected void nativeGatherSurroundingText(long nativeContextualSearchManager,
-                ContextualSearchContext contextualSearchContext, WebContents baseWebContents) {}
-
         /**
          * @return A stubbed SelectionPopupController for mocking text selection.
          */
@@ -247,6 +252,8 @@
     public void setUp() throws Exception {
         mActivityTestRule.startMainActivityOnBlankPage();
         final ChromeActivity activity = mActivityTestRule.getActivity();
+        MockitoAnnotations.initMocks(this);
+        mocker.mock(ContextualSearchManagerJni.TEST_HOOKS, mContextualSearchManagerJniMock);
 
         TestThreadUtils.runOnUiThreadBlocking(() -> {
             mPanelManager = new OverlayPanelManagerWrapper();
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/feed/FeedAppLifecycleTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/feed/FeedAppLifecycleTest.java
index 641295c..d04b275 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/feed/FeedAppLifecycleTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/feed/FeedAppLifecycleTest.java
@@ -327,7 +327,7 @@
     public void testDelayedInitNoParam() {
         verify(mAppLifecycleListener, times(1)).onEnterForeground();
         mTestDeferredStartupHandler.runAllTasks();
-        verify(mAppLifecycleListener, times(1)).initialize();
+        verify(mAppLifecycleListener, times(0)).initialize();
     }
 
     @Test
@@ -367,7 +367,7 @@
     testDelayedInitZeroParamNotBoolean() {
         verify(mAppLifecycleListener, times(1)).onEnterForeground();
         mTestDeferredStartupHandler.runAllTasks();
-        verify(mAppLifecycleListener, times(1)).initialize();
+        verify(mAppLifecycleListener, times(0)).initialize();
     }
 
     private void signalActivityStart(Activity activity)
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/offlinepages/prefetch/PrefetchFlowTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/offlinepages/prefetch/PrefetchFlowTest.java
index 5e78c68..b804f4f1 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/offlinepages/prefetch/PrefetchFlowTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/offlinepages/prefetch/PrefetchFlowTest.java
@@ -22,6 +22,7 @@
 import org.chromium.base.test.util.Feature;
 import org.chromium.base.test.util.RetryOnFailure;
 import org.chromium.chrome.browser.ChromeActivity;
+import org.chromium.chrome.browser.ChromeFeatureList;
 import org.chromium.chrome.browser.ChromeSwitches;
 import org.chromium.chrome.browser.offlinepages.OfflinePageBridge;
 import org.chromium.chrome.browser.offlinepages.OfflinePageItem;
@@ -108,6 +109,8 @@
         final String suggestionsBackend = Uri.encode(mServer.getBaseUrl() + "suggestions/");
         CommandLine.getInstance().appendSwitchWithValue("enable-features",
                 "OfflinePagesPrefetching<Trial,DownloadService<Trial,NTPArticleSuggestions<Trial");
+        CommandLine.getInstance().appendSwitchWithValue(
+                "disable-features", ChromeFeatureList.INTEREST_FEED_CONTENT_SUGGESTIONS + "<Trial");
         CommandLine.getInstance().appendSwitchWithValue("force-fieldtrials", "Trial/Group");
         CommandLine.getInstance().appendSwitchWithValue("force-fieldtrial-params",
                 "Trial.Group:start_up_delay_ms/100/offline_pages_backend/" + offlinePagesBackend
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentRequestPaymentAppUiSkipTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentRequestPaymentAppUiSkipTest.java
index 0a08ad47..2585ecc4 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentRequestPaymentAppUiSkipTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentRequestPaymentAppUiSkipTest.java
@@ -47,6 +47,24 @@
     public PaymentRequestTestRule mPaymentRequestTestRule =
             new PaymentRequestTestRule("payment_request_bobpay_ui_skip_test.html");
 
+    /** If the transaction fails, the browser shows an error message. */
+    @Test
+    @MediumTest
+    @Feature({"Payments"})
+    public void testFail() throws InterruptedException, TimeoutException {
+        mPaymentRequestTestRule.installPaymentApp(HAVE_INSTRUMENTS, IMMEDIATE_RESPONSE);
+
+        // Wait for the error message overlay.
+        mPaymentRequestTestRule.triggerUIAndWait(
+                "buyFail", mPaymentRequestTestRule.getResultReady());
+
+        // Dismiss the error message overlay.
+        mPaymentRequestTestRule.clickErrorOverlayAndWait(
+                R.id.ok_button, mPaymentRequestTestRule.getCompleteReplied());
+
+        mPaymentRequestTestRule.expectResultContains(new String[] {"Transaction failed"});
+    }
+
     /**
      * If Bob Pay is supported and installed, user should be able to pay with it. Here Bob Pay
      * responds to Chrome immediately.
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentRequestTestRule.java b/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentRequestTestRule.java
index 54ec404..8b46bb02 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentRequestTestRule.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentRequestTestRule.java
@@ -117,6 +117,7 @@
     final CallbackHelper mHasEnrolledInstrumentQueryResponded;
     final CallbackHelper mExpirationMonthChange;
     final CallbackHelper mPaymentResponseReady;
+    final CallbackHelper mCompleteReplied;
     PaymentRequestImpl mPaymentRequest;
     PaymentRequestUI mUI;
 
@@ -156,6 +157,7 @@
         mShowFailed = new CallbackHelper();
         mCanMakePaymentQueryResponded = new CallbackHelper();
         mHasEnrolledInstrumentQueryResponded = new CallbackHelper();
+        mCompleteReplied = new CallbackHelper();
         mWebContentsRef = new AtomicReference<>();
         mTestFilePath = testFileName.equals("about:blank") || testFileName.startsWith("data:")
                 ? testFileName
@@ -242,6 +244,9 @@
     public CallbackHelper getPaymentResponseReady() {
         return mPaymentResponseReady;
     }
+    public CallbackHelper getCompleteReplied() {
+        return mCompleteReplied;
+    }
     public PaymentRequestUI getPaymentRequestUI() {
         return mUI;
     }
@@ -323,6 +328,18 @@
         helper.waitForCallback(callCount);
     }
 
+    /** Clicks on an element in the error overlay. */
+    protected void clickErrorOverlayAndWait(int resourceId, CallbackHelper helper)
+            throws InterruptedException, TimeoutException {
+        int callCount = helper.getCallCount();
+        ThreadUtils.runOnUiThreadBlocking(() -> {
+            // Error overlay always allows clicks and is not taken into account in
+            // isAcceptingUserInput().
+            mUI.getDialogForTest().findViewById(resourceId).performClick();
+        });
+        helper.waitForCallback(callCount);
+    }
+
     /** Clicks on an element in the "Order summary" section of the payments UI. */
     protected void clickInOrderSummaryAndWait(CallbackHelper helper)
             throws InterruptedException, TimeoutException {
@@ -1081,6 +1098,12 @@
         mPaymentResponseReady.notifyCalled();
     }
 
+    @Override
+    public void onCompleteReplied() {
+        ThreadUtils.assertOnUiThread();
+        mCompleteReplied.notifyCalled();
+    }
+
     /**
      * Listens for UI notifications.
      */
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchContextForTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchContextForTest.java
deleted file mode 100644
index 8b38b7f7..0000000
--- a/chrome/android/junit/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchContextForTest.java
+++ /dev/null
@@ -1,52 +0,0 @@
-// Copyright 2017 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-package org.chromium.chrome.browser.contextualsearch;
-
-/**
- * Provides a {@link ContextualSearchContext} suitable for injection into a class for testing.  This
- * has native calls stubbed out and is set up to provide some simple test feedback.
- */
-public class ContextualSearchContextForTest extends ContextualSearchContext {
-    private boolean mDidSelectionChange;
-    private String mDetectedLanguage;
-
-    /**
-     * @return Whether {@link #onSelectionChanged} was called.
-     */
-    boolean getDidSelectionChange() {
-        return mDidSelectionChange;
-    }
-
-    void setLanguageToDetect(String language) {
-        mDetectedLanguage = language;
-    }
-
-    @Override
-    void onSelectionChanged() {
-        mDidSelectionChange = true;
-    }
-
-    @Override
-    protected long nativeInit() {
-        return -1;
-    }
-
-    @Override
-    protected void nativeDestroy(long nativeContextualSearchContext) {}
-
-    @Override
-    protected void nativeSetResolveProperties(long nativeContextualSearchContext,
-            String homeCountry, boolean maySendBasePageUrl, long previousEventId,
-            int previousEventResults) {}
-
-    @Override
-    protected void nativeAdjustSelection(
-            long nativeContextualSearchContext, int startAdjust, int endAdjust) {}
-
-    @Override
-    protected String nativeDetectLanguage(long nativeContextualSearchContext) {
-        return mDetectedLanguage;
-    }
-}
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchContextTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchContextTest.java
index 0b530e89..b519dfb1 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchContextTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchContextTest.java
@@ -9,13 +9,19 @@
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.when;
 
 import org.junit.Before;
+import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.junit.runners.BlockJUnit4ClassRunner;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
 
 import org.chromium.base.test.util.Feature;
+import org.chromium.base.test.util.JniMocker;
 
 /**
  * Tests parts of the ContextualSearchContext class.
@@ -27,11 +33,30 @@
     private static final String SAMPLE_TEXT =
             "Now Barack Obama is not the best example.  And Clinton is ambiguous.";
     private static final String HOME_COUNTRY = "unused";
+    private static final long NATIVE_PTR = 1;
 
-    private ContextualSearchContextForTest mContext;
+    private ContextualSearchContext mContext;
+    private boolean mDidSelectionChange;
+
+    private class ContextualSearchContextForTest extends ContextualSearchContext {
+        @Override
+        void onSelectionChanged() {
+            mDidSelectionChange = true;
+        }
+    }
+
+    @Rule
+    public JniMocker mocker = new JniMocker();
+
+    @Mock
+    private ContextualSearchContext.Natives mContextJniMock;
 
     @Before
     public void setup() {
+        MockitoAnnotations.initMocks(this);
+        mocker.mock(ContextualSearchContextJni.TEST_HOOKS, mContextJniMock);
+        when(mContextJniMock.init(any())).thenReturn(NATIVE_PTR);
+        mDidSelectionChange = false;
         mContext = new ContextualSearchContextForTest();
     }
 
@@ -98,7 +123,7 @@
         assertTrue(mContext.getSelectionStartOffset() >= 0);
         assertTrue(mContext.getSelectionEndOffset() >= 0);
         assertNotNull(mContext.getEncoding());
-        assertTrue(mContext.getDidSelectionChange());
+        assertTrue(mDidSelectionChange);
     }
 
     @Test
@@ -114,13 +139,13 @@
         assertTrue(mContext.getSelectionEndOffset() >= 0);
         assertNotNull(mContext.getEncoding());
         assertNull(mContext.getInitialSelectedWord());
-        assertFalse(mContext.getDidSelectionChange());
+        assertFalse(mDidSelectionChange);
 
         simulateSelectWordAroundCaret(-"Ba".length(), "rack".length());
         assertEquals("Barack", mContext.getInitialSelectedWord());
         assertEquals("Barack".length(),
                 mContext.getSelectionEndOffset() - mContext.getSelectionStartOffset());
-        assertTrue(mContext.getDidSelectionChange());
+        assertTrue(mDidSelectionChange);
         assertTrue(mContext.hasValidSelection());
     }
 
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchEntityHeuristicTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchEntityHeuristicTest.java
index 7067496..c8ab0e8 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchEntityHeuristicTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchEntityHeuristicTest.java
@@ -6,12 +6,20 @@
 
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.anyLong;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.when;
 
+import org.junit.Before;
+import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.junit.runners.BlockJUnit4ClassRunner;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
 
 import org.chromium.base.test.util.Feature;
+import org.chromium.base.test.util.JniMocker;
 
 import java.util.Locale;
 
@@ -24,13 +32,26 @@
             "Now Barack Obama, Michelle are not the best examples.  And Clinton is ambiguous.";
     private static final String UTF_8 = "UTF-8";
 
-    private ContextualSearchContextForTest mContext;
+    @Rule
+    public JniMocker mocker = new JniMocker();
+
+    @Mock
+    private ContextualSearchContext.Natives mContextJniMock;
+
+    private ContextualSearchContext mContext;
     private ContextualSearchEntityHeuristic mEntityHeuristic;
 
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+        mocker.mock(ContextualSearchContextJni.TEST_HOOKS, mContextJniMock);
+    }
+
     private void setupInstanceToTest(Locale locale, int tapOffset) {
-        mContext = new ContextualSearchContextForTest();
+        mContext = new ContextualSearchContext.ChangeIgnoringContext();
         mContext.setSurroundingText(UTF_8, SAMPLE_TEXT, tapOffset, tapOffset);
-        mContext.setLanguageToDetect(locale.getLanguage());
+        when(mContextJniMock.detectLanguage(anyLong(), eq(mContext)))
+                .thenReturn(locale.getLanguage());
         mEntityHeuristic = ContextualSearchEntityHeuristic.testInstance(mContext, true);
     }
 
@@ -88,8 +109,8 @@
 
     private ContextualSearchEntityHeuristic setupHeuristic(
             String language, String start, String text) {
-        ContextualSearchContextForTest context = new ContextualSearchContextForTest();
-        context.setLanguageToDetect(language);
+        ContextualSearchContext context = new ContextualSearchContext.ChangeIgnoringContext();
+        when(mContextJniMock.detectLanguage(anyLong(), eq(context))).thenReturn(language);
         assert text.startsWith(start);
         int tapOffset = start.length();
         context.setSurroundingText(UTF_8, text, tapOffset, tapOffset);
diff --git a/chrome/browser/BUILD.gn b/chrome/browser/BUILD.gn
index 1bfb5916..4b3c71c9 100644
--- a/chrome/browser/BUILD.gn
+++ b/chrome/browser/BUILD.gn
@@ -964,6 +964,10 @@
     "optimization_guide/optimization_guide_top_host_provider.h",
     "optimization_guide/optimization_guide_web_contents_observer.cc",
     "optimization_guide/optimization_guide_web_contents_observer.h",
+    "optimization_guide/prediction/decision_tree_prediction_model.cc",
+    "optimization_guide/prediction/decision_tree_prediction_model.h",
+    "optimization_guide/prediction/prediction_model.cc",
+    "optimization_guide/prediction/prediction_model.h",
     "page_load_metrics/observers/aborts_page_load_metrics_observer.cc",
     "page_load_metrics/observers/aborts_page_load_metrics_observer.h",
     "page_load_metrics/observers/ad_metrics/ads_page_load_metrics_observer.cc",
@@ -1723,6 +1727,8 @@
     "ssl/ssl_error_handler.h",
     "ssl/ssl_error_navigation_throttle.cc",
     "ssl/ssl_error_navigation_throttle.h",
+    "ssl/tls_deprecation_config.cc",
+    "ssl/tls_deprecation_config.h",
     "ssl/typed_navigation_timing_throttle.cc",
     "ssl/typed_navigation_timing_throttle.h",
     "startup_data.cc",
@@ -3009,6 +3015,8 @@
       "chrome_process_singleton.h",
       "component_updater/intervention_policy_database_component_installer.cc",
       "component_updater/intervention_policy_database_component_installer.h",
+      "component_updater/tls_deprecation_config_component_installer.cc",
+      "component_updater/tls_deprecation_config_component_installer.h",
       "custom_handlers/register_protocol_handler_permission_request.cc",
       "custom_handlers/register_protocol_handler_permission_request.h",
       "diagnostics/diagnostics_controller.cc",
diff --git a/chrome/browser/about_flags.cc b/chrome/browser/about_flags.cc
index 52aee33..4cbceba7 100644
--- a/chrome/browser/about_flags.cc
+++ b/chrome/browser/about_flags.cc
@@ -2429,12 +2429,6 @@
                                     "AndroidNightMode")},
 #endif  // BUILDFLAG(ENABLE_ANDROID_NIGHT_MODE)
 #endif  // OS_ANDROID
-#if defined(OS_LINUX) && !defined(OS_CHROMEOS)
-    {"enable-dbus-and-x11-status-icons",
-     flag_descriptions::kEnableDbusAndX11StatusIconsName,
-     flag_descriptions::kEnableDbusAndX11StatusIconsDescription, kOsLinux,
-     FEATURE_VALUE_TYPE(features::kEnableDbusAndX11StatusIcons)},
-#endif  // defined(OS_LINUX) && !defined(OS_CHROMEOS)
     {"enable-experimental-accessibility-features",
      flag_descriptions::kExperimentalAccessibilityFeaturesName,
      flag_descriptions::kExperimentalAccessibilityFeaturesDescription, kOsCrOS,
diff --git a/chrome/browser/accessibility/accessibility_ui.cc b/chrome/browser/accessibility/accessibility_ui.cc
index 8165c1d..60a9afec 100644
--- a/chrome/browser/accessibility/accessibility_ui.cc
+++ b/chrome/browser/accessibility/accessibility_ui.cc
@@ -13,6 +13,7 @@
 #include "base/bind_helpers.h"
 #include "base/command_line.h"
 #include "base/json/json_writer.h"
+#include "base/strings/pattern.h"
 #include "base/strings/string_number_conversions.h"
 #include "base/strings/utf_string_conversions.h"
 #include "base/values.h"
@@ -78,7 +79,7 @@
 static const char kNative[] = "native";
 static const char kPage[] = "page";
 static const char kScreenReader[] = "screenreader";
-static const char kShowTree[] = "showTree";
+static const char kShowOrRefreshTree[] = "showOrRefreshTree";
 static const char kText[] = "text";
 static const char kWeb[] = "web";
 
@@ -253,17 +254,53 @@
   callback.Run(base::RefCountedString::TakeString(&json_string));
 }
 
-std::string RecursiveDumpAXPlatformNodeAsString(ui::AXPlatformNode* node,
-                                                int indent) {
+bool MatchesPropertyFilters(
+    const std::vector<content::AccessibilityTreeFormatter::PropertyFilter>&
+        property_filters,
+    const base::string16& text) {
+  bool allow = false;
+  for (const auto& filter : property_filters) {
+    if (base::MatchPattern(text, filter.match_str)) {
+      switch (filter.type) {
+        case content::AccessibilityTreeFormatter::PropertyFilter::ALLOW_EMPTY:
+          allow = true;
+          break;
+        case content::AccessibilityTreeFormatter::PropertyFilter::ALLOW:
+          allow = (!base::MatchPattern(text, base::UTF8ToUTF16("*=''")));
+          break;
+        case content::AccessibilityTreeFormatter::PropertyFilter::DENY:
+          allow = false;
+          break;
+      }
+    }
+  }
+  return allow;
+}
+
+std::string RecursiveDumpAXPlatformNodeAsString(
+    ui::AXPlatformNode* node,
+    int indent,
+    const std::vector<content::AccessibilityTreeFormatter::PropertyFilter>&
+        property_filters) {
   if (!node)
     return "";
   std::string str(2 * indent, '+');
-  str += node->GetDelegate()->GetData().ToString() + "\n";
+  std::string line = node->GetDelegate()->GetData().ToString();
+  std::vector<std::string> attributes = base::SplitString(
+      line, " ", base::KEEP_WHITESPACE, base::SPLIT_WANT_NONEMPTY);
+  for (std::string attribute : attributes) {
+    if (MatchesPropertyFilters(property_filters,
+                               base::UTF8ToUTF16(attribute))) {
+      str += attribute + " ";
+    }
+  }
+  str += "\n";
   for (int i = 0; i < node->GetDelegate()->GetChildCount(); i++) {
     gfx::NativeViewAccessible child = node->GetDelegate()->ChildAtIndex(i);
     ui::AXPlatformNode* child_node =
         ui::AXPlatformNode::FromNativeViewAccessible(child);
-    str += RecursiveDumpAXPlatformNodeAsString(child_node, indent + 1);
+    str += RecursiveDumpAXPlatformNodeAsString(child_node, indent + 1,
+                                               property_filters);
   }
   return str;
 }
@@ -393,16 +430,17 @@
     base::DictionaryValue request_data;
     request_data.SetIntPath(kProcessIdField, process_id);
     request_data.SetIntPath(kRouteIdField, route_id);
-    request_data.SetStringPath(kRequestTypeField, kShowTree);
+    request_data.SetStringPath(kRequestTypeField, kShowOrRefreshTree);
     base::ListValue request_args;
     request_args.Append(std::move(request_data));
     RequestWebContentsTree(&request_args);
   } else {
-    // Call accessibility.showTree without a 'tree' field so the row's
+    // Call accessibility.showOrRefreshTree without a 'tree' field so the row's
     // accessibility mode buttons are updated.
     AllowJavascript();
     std::unique_ptr<base::DictionaryValue> new_mode(BuildTargetDescriptor(rvh));
-    CallJavascriptFunction("accessibility.showTree", *(new_mode.get()));
+    CallJavascriptFunction("accessibility.showOrRefreshTree",
+                           *(new_mode.get()));
   }
 }
 
@@ -472,7 +510,7 @@
   int route_id = *data->FindIntPath(kRouteIdField);
 
   std::string request_type = Validate(data->FindStringPath(kRequestTypeField));
-  CHECK(request_type == kShowTree || request_type == kCopyTree);
+  CHECK(request_type == kShowOrRefreshTree || request_type == kCopyTree);
   request_type = "accessibility." + request_type;
 
   std::string allow = Validate(data->FindStringPath("filters.allow"));
@@ -528,12 +566,28 @@
 
   int session_id = *data->FindIntPath(kSessionIdField);
   std::string request_type = Validate(data->FindStringPath(kRequestTypeField));
-  CHECK(request_type == kShowTree || request_type == kCopyTree);
+  CHECK(request_type == kShowOrRefreshTree || request_type == kCopyTree);
   request_type = "accessibility." + request_type;
 
+  std::string allow = Validate(data->FindStringPath("filters.allow"));
+  std::string allow_empty =
+      Validate(data->FindStringPath("filters.allowEmpty"));
+  std::string deny = Validate(data->FindStringPath("filters.deny"));
+
   AllowJavascript();
 
 #if !defined(OS_ANDROID)
+  std::vector<content::AccessibilityTreeFormatter::PropertyFilter>
+      property_filters;
+  AddPropertyFilters(
+      property_filters, allow,
+      content::AccessibilityTreeFormatter::PropertyFilter::ALLOW);
+  AddPropertyFilters(
+      property_filters, allow_empty,
+      content::AccessibilityTreeFormatter::PropertyFilter::ALLOW_EMPTY);
+  AddPropertyFilters(property_filters, deny,
+                     content::AccessibilityTreeFormatter::PropertyFilter::DENY);
+
   for (Browser* browser : *BrowserList::GetInstance()) {
     if (browser->session_id().id() == session_id) {
       std::unique_ptr<base::DictionaryValue> result(
@@ -542,7 +596,9 @@
       ui::AXPlatformNode* node =
           ui::AXPlatformNode::FromNativeWindow(native_window);
       result->SetKey(kTreeField,
-                     base::Value(RecursiveDumpAXPlatformNodeAsString(node, 0)));
+                     base::Value(RecursiveDumpAXPlatformNodeAsString(
+                         node, 0, property_filters)));
+      result->SetBoolean(kImprovementsEnabledField, improvements_enabled_);
       CallJavascriptFunction(request_type, *(result.get()));
       return;
     }
diff --git a/chrome/browser/android/feed/feed_host_service_factory.cc b/chrome/browser/android/feed/feed_host_service_factory.cc
index 73aa242..f9910db 100644
--- a/chrome/browser/android/feed/feed_host_service_factory.cc
+++ b/chrome/browser/android/feed/feed_host_service_factory.cc
@@ -138,4 +138,8 @@
   return context->IsOffTheRecord() ? nullptr : context;
 }
 
+bool FeedHostServiceFactory::ServiceIsNULLWhileTesting() const {
+  return true;
+}
+
 }  // namespace feed
diff --git a/chrome/browser/android/feed/feed_host_service_factory.h b/chrome/browser/android/feed/feed_host_service_factory.h
index 3597f24f..03c014c 100644
--- a/chrome/browser/android/feed/feed_host_service_factory.h
+++ b/chrome/browser/android/feed/feed_host_service_factory.h
@@ -37,6 +37,7 @@
       content::BrowserContext* context) const override;
   content::BrowserContext* GetBrowserContextToUse(
       content::BrowserContext* context) const override;
+  bool ServiceIsNULLWhileTesting() const override;
 
   DISALLOW_COPY_AND_ASSIGN(FeedHostServiceFactory);
 };
diff --git a/chrome/browser/browser_commands_unittest.cc b/chrome/browser/browser_commands_unittest.cc
index f77b4f3..e6ac4c8 100644
--- a/chrome/browser/browser_commands_unittest.cc
+++ b/chrome/browser/browser_commands_unittest.cc
@@ -21,9 +21,11 @@
 #include "content/public/browser/navigation_controller.h"
 #include "content/public/browser/navigation_entry.h"
 #include "content/public/browser/web_contents.h"
+#include "content/public/common/page_zoom.h"
 #include "content/public/test/navigation_simulator.h"
 #include "content/public/test/test_renderer_host.h"
 #include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/public/common/page/page_zoom.h"
 
 typedef BrowserWithTestWindowTest BrowserCommandsTest;
 
@@ -373,7 +375,7 @@
 
   // Set the default zoom level to 125.
   profile()->GetZoomLevelPrefs()->SetDefaultZoomLevelPref(
-        content::ZoomFactorToZoomLevel(1.25));
+      blink::PageZoomFactorToZoomLevel(1.25));
   EXPECT_FLOAT_EQ(125.0f, zoom_controller->GetZoomPercent());
 
   // Actual Size from context menu should be disabled now.
diff --git a/chrome/browser/browsing_data/chrome_browsing_data_remover_delegate.cc b/chrome/browser/browsing_data/chrome_browsing_data_remover_delegate.cc
index df11622..1fa141a 100644
--- a/chrome/browser/browsing_data/chrome_browsing_data_remover_delegate.cc
+++ b/chrome/browser/browsing_data/chrome_browsing_data_remover_delegate.cc
@@ -932,7 +932,11 @@
 #if defined(OS_ANDROID)
 #if BUILDFLAG(ENABLE_FEED_IN_CHROME)
     if (base::FeatureList::IsEnabled(feed::kInterestFeedContentSuggestions))
-      feed::FeedLifecycleBridge::ClearCachedData();
+      // Don't bridge through if the service isn't present, which means we're
+      // probably running in a native unit test.
+      if (feed::FeedHostServiceFactory::GetForBrowserContext(profile_)) {
+        feed::FeedLifecycleBridge::ClearCachedData();
+      }
 #endif  // BUILDFLAG(ENABLE_FEED_IN_CHROME)
 #endif  // defined(OS_ANDROID)
 
diff --git a/chrome/browser/browsing_data/chrome_browsing_data_remover_delegate_factory.cc b/chrome/browser/browsing_data/chrome_browsing_data_remover_delegate_factory.cc
index 89522eca..5737f32 100644
--- a/chrome/browser/browsing_data/chrome_browsing_data_remover_delegate_factory.cc
+++ b/chrome/browser/browsing_data/chrome_browsing_data_remover_delegate_factory.cc
@@ -5,6 +5,8 @@
 #include "chrome/browser/browsing_data/chrome_browsing_data_remover_delegate_factory.h"
 
 #include "base/memory/singleton.h"
+#include "build/build_config.h"
+#include "chrome/browser/android/feed/feed_host_service_factory.h"
 #include "chrome/browser/autofill/personal_data_manager_factory.h"
 #include "chrome/browser/browsing_data/chrome_browsing_data_remover_delegate.h"
 #include "chrome/browser/content_settings/host_content_settings_map_factory.h"
@@ -31,6 +33,11 @@
 #include "chrome/browser/sessions/session_service_factory.h"
 #endif
 
+#if defined(OS_ANDROID)
+#include "components/feed/buildflags.h"
+#include "components/feed/feed_feature_list.h"
+#endif  // defined(OS_ANDROID)
+
 // static
 ChromeBrowsingDataRemoverDelegateFactory*
 ChromeBrowsingDataRemoverDelegateFactory::GetInstance() {
@@ -51,6 +58,13 @@
           BrowserContextDependencyManager::GetInstance()) {
   DependsOn(autofill::PersonalDataManagerFactory::GetInstance());
   DependsOn(DataReductionProxyChromeSettingsFactory::GetInstance());
+#if defined(OS_ANDROID)
+#if BUILDFLAG(ENABLE_FEED_IN_CHROME)
+  if (base::FeatureList::IsEnabled(feed::kInterestFeedContentSuggestions)) {
+    DependsOn(feed::FeedHostServiceFactory::GetInstance());
+  }
+#endif  // BUILDFLAG(ENABLE_FEED_IN_CHROME)
+#endif  // defined(OS_ANDROID)
   DependsOn(HistoryServiceFactory::GetInstance());
   DependsOn(HostContentSettingsMapFactory::GetInstance());
   DependsOn(PasswordStoreFactory::GetInstance());
diff --git a/chrome/browser/chrome_browser_main.cc b/chrome/browser/chrome_browser_main.cc
index 33a17d7..511c013 100644
--- a/chrome/browser/chrome_browser_main.cc
+++ b/chrome/browser/chrome_browser_main.cc
@@ -330,6 +330,7 @@
 
 #if !defined(OS_ANDROID)
 #include "chrome/browser/component_updater/intervention_policy_database_component_installer.h"
+#include "chrome/browser/component_updater/tls_deprecation_config_component_installer.h"
 #include "chrome/browser/resource_coordinator/tab_manager.h"
 #endif
 
@@ -536,6 +537,7 @@
 #if !defined(OS_ANDROID)
   RegisterInterventionPolicyDatabaseComponent(
       cus, g_browser_process->GetTabManager()->intervention_policy_database());
+  RegisterTLSDeprecationConfigComponent(cus, path);
 #endif
 
 #if BUILDFLAG(ENABLE_VR)
diff --git a/chrome/browser/chromeos/arc/intent_helper/arc_settings_service.cc b/chrome/browser/chromeos/arc/intent_helper/arc_settings_service.cc
index 4b0d328..ca3d4a2e 100644
--- a/chrome/browser/chromeos/arc/intent_helper/arc_settings_service.cc
+++ b/chrome/browser/chromeos/arc/intent_helper/arc_settings_service.cc
@@ -51,9 +51,9 @@
 #include "components/proxy_config/pref_proxy_config_tracker_impl.h"
 #include "components/proxy_config/proxy_config_dictionary.h"
 #include "components/proxy_config/proxy_config_pref_names.h"
-#include "content/public/common/page_zoom.h"
 #include "net/proxy_resolution/proxy_bypass_rules.h"
 #include "net/proxy_resolution/proxy_config.h"
+#include "third_party/blink/public/common/page/page_zoom.h"
 
 using ::chromeos::system::TimezoneSettings;
 
@@ -481,7 +481,7 @@
     return;
 
   double zoom_level = profile_->GetZoomLevelPrefs()->GetDefaultZoomLevelPref();
-  double zoom_factor = content::ZoomLevelToZoomFactor(zoom_level);
+  double zoom_factor = blink::PageZoomLevelToZoomFactor(zoom_level);
 
   base::DictionaryValue extras;
   extras.SetDouble("zoomFactor", zoom_factor);
diff --git a/chrome/browser/chromeos/file_manager/file_manager_browsertest.cc b/chrome/browser/chromeos/file_manager/file_manager_browsertest.cc
index 83d37e1..17a97c0 100644
--- a/chrome/browser/chromeos/file_manager/file_manager_browsertest.cc
+++ b/chrome/browser/chromeos/file_manager/file_manager_browsertest.cc
@@ -386,6 +386,8 @@
         TestCase("checkPasteIntoFolderDisabledForReadOnlyFolder"),
         TestCase("checkInstallWithLinuxDisabledForDebianFile"),
         TestCase("checkInstallWithLinuxEnabledForDebianFile"),
+        TestCase("checkImportCrostiniImageEnabled"),
+        TestCase("checkImportCrostiniImageDisabled"),
         TestCase("checkNewFolderEnabledInsideReadWriteFolder"),
         TestCase("checkNewFolderDisabledInsideReadOnlyFolder"),
         TestCase("checkPasteEnabledInsideReadWriteFolder"),
diff --git a/chrome/browser/chromeos/file_manager/file_manager_browsertest_base.cc b/chrome/browser/chromeos/file_manager/file_manager_browsertest_base.cc
index 0cede84..414caae 100644
--- a/chrome/browser/chromeos/file_manager/file_manager_browsertest_base.cc
+++ b/chrome/browser/chromeos/file_manager/file_manager_browsertest_base.cc
@@ -1522,6 +1522,7 @@
   std::vector<base::Feature> disabled_features;
 
   enabled_features.emplace_back(features::kCrostiniAdvancedAccessControls);
+  enabled_features.emplace_back(chromeos::features::kCrostiniBackup);
 
   if (!IsGuestModeTest()) {
     enabled_features.emplace_back(features::kCrostini);
@@ -1616,6 +1617,8 @@
     profile()->GetPrefs()->SetBoolean(crostini::prefs::kCrostiniEnabled, true);
     profile()->GetPrefs()->SetBoolean(
         crostini::prefs::kUserCrostiniRootAccessAllowedByPolicy, true);
+    profile()->GetPrefs()->SetBoolean(
+        crostini::prefs::kUserCrostiniExportImportUIAllowedByPolicy, true);
     crostini::CrostiniManager* crostini_manager =
         crostini::CrostiniManager::GetForProfile(
             profile()->GetOriginalProfile());
@@ -2104,6 +2107,14 @@
     return;
   }
 
+  if (name == "setCrostiniExportImportAllowed") {
+    bool enabled;
+    ASSERT_TRUE(value.GetBoolean("enabled", &enabled));
+    profile()->GetPrefs()->SetBoolean(
+        crostini::prefs::kUserCrostiniExportImportUIAllowedByPolicy, enabled);
+    return;
+  }
+
   if (name == "useCellularNetwork") {
     net::NetworkChangeNotifier::NotifyObserversOfMaxBandwidthChangeForTests(
         net::NetworkChangeNotifier::GetMaxBandwidthMbpsForConnectionSubtype(
diff --git a/chrome/browser/chromeos/file_manager/file_tasks.cc b/chrome/browser/chromeos/file_manager/file_tasks.cc
index 2a3b6f4..1874391 100644
--- a/chrome/browser/chromeos/file_manager/file_tasks.cc
+++ b/chrome/browser/chromeos/file_manager/file_tasks.cc
@@ -77,6 +77,7 @@
 const char kArcAppTaskType[] = "arc";
 const char kCrostiniAppTaskType[] = "crostini";
 const char kWebAppTaskType[] = "web";
+const char kImportCrostiniImageHandlerId[] = "import-crostini-image";
 
 // Converts a TaskType to a string.
 std::string TaskTypeToString(TaskType task_type) {
@@ -470,6 +471,15 @@
   return false;
 }
 
+bool IsFileHandlerEnabled(Profile* profile,
+                          const apps::FileHandlerInfo& file_handler_info) {
+  // Crostini backup files can be disabled by policy.
+  if (file_handler_info.id == kImportCrostiniImageHandlerId) {
+    return crostini::IsCrostiniExportImportUIAllowedForProfile(profile);
+  }
+  return true;
+}
+
 bool IsGoodMatchFileHandler(const apps::FileHandlerInfo& file_handler_info,
                             const std::vector<extensions::EntryInfo>& entries) {
   if (file_handler_info.extensions.count("*") > 0 ||
@@ -535,6 +545,9 @@
     // show the first matching handler of the verb.
     for (const auto& handler_match : file_handlers) {
       const apps::FileHandlerInfo* handler = handler_match.handler;
+      if (!IsFileHandlerEnabled(profile, *handler)) {
+        continue;
+      }
       bool good_match = IsGoodMatchFileHandler(*handler, entries);
       auto it = handlers_for_entries.find(handler->verb);
       if (it == handlers_for_entries.end() ||
diff --git a/chrome/browser/chromeos/file_manager/file_tasks.h b/chrome/browser/chromeos/file_manager/file_tasks.h
index 5e6f5c2..a498f9be 100644
--- a/chrome/browser/chromeos/file_manager/file_tasks.h
+++ b/chrome/browser/chromeos/file_manager/file_tasks.h
@@ -270,6 +270,11 @@
                      const std::vector<storage::FileSystemURL>& file_urls,
                      FileTaskFinishedCallback done);
 
+// Returns true if a file handler is enabled. Some handlers such as
+// import-crostini-image can be disabled at runtime by enterprise policy.
+bool IsFileHandlerEnabled(Profile* profile,
+                          const apps::FileHandlerInfo& file_handler_info);
+
 // Returns true if a file handler matches with entries as good match.
 bool IsGoodMatchFileHandler(const apps::FileHandlerInfo& file_handler_info,
                             const std::vector<extensions::EntryInfo>& entries);
diff --git a/chrome/browser/chromeos/file_manager/file_tasks_unittest.cc b/chrome/browser/chromeos/file_manager/file_tasks_unittest.cc
index 1dd38e9..bf75fe6 100644
--- a/chrome/browser/chromeos/file_manager/file_tasks_unittest.cc
+++ b/chrome/browser/chromeos/file_manager/file_tasks_unittest.cc
@@ -14,6 +14,7 @@
 #include "base/values.h"
 #include "chrome/browser/chromeos/crostini/crostini_mime_types_service.h"
 #include "chrome/browser/chromeos/crostini/crostini_mime_types_service_factory.h"
+#include "chrome/browser/chromeos/crostini/crostini_pref_names.h"
 #include "chrome/browser/chromeos/crostini/crostini_test_helper.h"
 #include "chrome/browser/chromeos/drive/file_system_util.h"
 #include "chrome/browser/chromeos/file_manager/app_id.h"
@@ -27,6 +28,7 @@
 #include "chromeos/dbus/dbus_thread_manager.h"
 #include "chromeos/dbus/fake_concierge_client.h"
 #include "components/prefs/pref_registry_simple.h"
+#include "components/prefs/pref_service.h"
 #include "components/prefs/testing_pref_service.h"
 #include "components/services/app_service/public/cpp/file_handler_info.h"
 #include "content/public/test/browser_task_environment.h"
@@ -304,6 +306,29 @@
   EXPECT_TRUE(tasks[0].is_default());
 }
 
+// Test IsFileHandlerEnabled which returns whether a file handler should be
+// used.
+TEST(FileManagerFileTasksTest, IsFileHandlerEnabled) {
+  content::BrowserTaskEnvironment task_environment;
+  TestingProfile test_profile;
+  crostini::CrostiniTestHelper helper(&test_profile);
+
+  apps::FileHandlerInfo crostini_import_handler;
+  crostini_import_handler.id = "import-crostini-image";
+  apps::FileHandlerInfo test_handler;
+  test_handler.id = "test";
+
+  test_profile.GetPrefs()->SetBoolean(
+      crostini::prefs::kUserCrostiniExportImportUIAllowedByPolicy, true);
+  EXPECT_TRUE(IsFileHandlerEnabled(&test_profile, crostini_import_handler));
+  EXPECT_TRUE(IsFileHandlerEnabled(&test_profile, test_handler));
+
+  test_profile.GetPrefs()->SetBoolean(
+      crostini::prefs::kUserCrostiniExportImportUIAllowedByPolicy, false);
+  EXPECT_FALSE(IsFileHandlerEnabled(&test_profile, crostini_import_handler));
+  EXPECT_TRUE(IsFileHandlerEnabled(&test_profile, test_handler));
+}
+
 // Test IsGoodMatchFileHandler which returns whether a file handle info matches
 // with files as good match or not.
 TEST(FileManagerFileTasksTest, IsGoodMatchFileHandler) {
diff --git a/chrome/browser/chromeos/login/enrollment/enrollment_screen.cc b/chrome/browser/chromeos/login/enrollment/enrollment_screen.cc
index 388994e0..21158ba 100644
--- a/chrome/browser/chromeos/login/enrollment/enrollment_screen.cc
+++ b/chrome/browser/chromeos/login/enrollment/enrollment_screen.cc
@@ -27,6 +27,7 @@
 #include "chromeos/dbus/dbus_method_call_status.h"
 #include "chromeos/dbus/dbus_thread_manager.h"
 #include "components/policy/core/common/cloud/cloud_policy_constants.h"
+#include "components/user_manager/user_manager.h"
 #include "google_apis/gaia/gaia_auth_util.h"
 
 using policy::EnrollmentConfig;
@@ -83,6 +84,40 @@
   }
 }
 
+bool HasPublicUser() {
+  // Some tests don't initialize the UserManager.
+  if (!user_manager::UserManager::IsInitialized())
+    return false;
+
+  for (const user_manager::User* user :
+       user_manager::UserManager::Get()->GetUsers()) {
+    if (user->GetType() == user_manager::USER_TYPE_PUBLIC_ACCOUNT)
+      return true;
+  }
+  return false;
+}
+
+bool ShouldAttemptRestart() {
+  // Restart browser to switch from DeviceCloudPolicyManagerChromeOS to
+  // DeviceActiveDirectoryPolicyManager.
+  if (g_browser_process->platform_part()
+          ->browser_policy_connector_chromeos()
+          ->IsActiveDirectoryManaged()) {
+    // TODO(tnagel): Refactor BrowserPolicyConnectorChromeOS so that device
+    // policy providers are only registered after enrollment has finished and
+    // thus the correct one can be picked without restarting the browser.
+    return true;
+  }
+
+  // Restart browser to switch to Views account picker if we have public
+  // accounts (which have user pods on the login screen).
+  // TODO(crbug.com/943720): Switch to Views account without Chrome restart.
+  if (HasPublicUser())
+    return true;
+
+  return false;
+}
+
 }  // namespace
 
 namespace chromeos {
@@ -320,16 +355,8 @@
   // either case, passing exit_callback_ directly should be safe.
   ClearAuth(base::BindRepeating(exit_callback_, Result::COMPLETED));
 
-  // Restart browser to switch from DeviceCloudPolicyManagerChromeOS to
-  // DeviceActiveDirectoryPolicyManager.
-  if (g_browser_process->platform_part()
-          ->browser_policy_connector_chromeos()
-          ->IsActiveDirectoryManaged()) {
-    // TODO(tnagel): Refactor BrowserPolicyConnectorChromeOS so that device
-    // policy providers are only registered after enrollment has finished and
-    // thus the correct one can be picked without restarting the browser.
+  if (ShouldAttemptRestart())
     chrome::AttemptRestart();
-  }
 }
 
 void EnrollmentScreen::OnAuthError(const GoogleServiceAuthError& error) {
diff --git a/chrome/browser/chromeos/login/saml/password_expiry_notification.cc b/chrome/browser/chromeos/login/saml/password_expiry_notification.cc
index ebaa421..3607d1f 100644
--- a/chrome/browser/chromeos/login/saml/password_expiry_notification.cc
+++ b/chrome/browser/chromeos/login/saml/password_expiry_notification.cc
@@ -4,6 +4,9 @@
 
 #include "chrome/browser/chromeos/login/saml/password_expiry_notification.h"
 
+#include <memory>
+#include <vector>
+
 #include "ash/public/cpp/notification_utils.h"
 #include "ash/public/cpp/session/session_activation_observer.h"
 #include "ash/public/cpp/session/session_controller.h"
@@ -53,11 +56,6 @@
 // Unique ID for this notification.
 const char kNotificationId[] = "saml.password-expiry-notification";
 
-// NotifierId for histogram reporting.
-const base::NoDestructor<NotifierId> kNotifierId(
-    message_center::NotifierType::SYSTEM_COMPONENT,
-    kNotificationId);
-
 // Simplest type of notification UI - no progress bars, images etc.
 const NotificationType kNotificationType =
     message_center::NOTIFICATION_TYPE_SIMPLE;
@@ -69,19 +67,12 @@
 // The icon to use for this notification - looks like an office building.
 const gfx::VectorIcon& kIcon = vector_icons::kBusinessIcon;
 
-// Leaving this empty means the notification is attributed to the system -
-// ie "Chromium OS" or similar.
-const base::NoDestructor<base::string16> kEmptyDisplaySource;
-
-// No origin URL is needed since the notification comes from the system.
-const base::NoDestructor<GURL> kEmptyOriginUrl;
-
-// Warning level of WARNING makes the title  orange.
-const SystemNotificationWarningLevel kWarningLevel =
+// Warning level of WARNING makes the title orange.
+constexpr SystemNotificationWarningLevel kWarningLevel =
     SystemNotificationWarningLevel::WARNING;
 
 // A time-delta of length one minute.
-const base::TimeDelta kOneMinute = base::TimeDelta::FromMinutes(1);
+constexpr base::TimeDelta kOneMinute = base::TimeDelta::FromMinutes(1);
 
 base::string16 GetBodyText() {
   return l10n_util::GetStringUTF16(IDS_PASSWORD_EXPIRY_CALL_TO_ACTION);
@@ -136,6 +127,17 @@
                                       base::TimeDelta time_until_expiry) {
   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
 
+  // NotifierId for histogram reporting.
+  static const base::NoDestructor<NotifierId> kNotifierId(
+      message_center::NotifierType::SYSTEM_COMPONENT, kNotificationId);
+
+  // Leaving this empty means the notification is attributed to the system -
+  // ie "Chromium OS" or similar.
+  static const base::NoDestructor<base::string16> kEmptyDisplaySource;
+
+  // No origin URL is needed since the notification comes from the system.
+  static const base::NoDestructor<GURL> kEmptyOriginUrl;
+
   const base::string16 title = GetTitleText(time_until_expiry);
   const base::string16 body = GetBodyText();
   const RichNotificationData rich_notification_data = GetRichNotificationData();
diff --git a/chrome/browser/chromeos/login/session/user_session_manager.cc b/chrome/browser/chromeos/login/session/user_session_manager.cc
index 4270a80..dd890c0 100644
--- a/chrome/browser/chromeos/login/session/user_session_manager.cc
+++ b/chrome/browser/chromeos/login/session/user_session_manager.cc
@@ -158,9 +158,9 @@
 #include "content/public/browser/notification_service.h"
 #include "content/public/browser/storage_partition.h"
 #include "content/public/common/content_switches.h"
-#include "content/public/common/page_zoom.h"
 #include "extensions/common/features/feature_session_type.h"
 #include "rlz/buildflags/buildflags.h"
+#include "third_party/blink/public/common/page/page_zoom.h"
 #include "third_party/cros_system_api/switches/chrome_switches.h"
 #include "ui/base/ime/chromeos/input_method_descriptor.h"
 #include "ui/base/ime/chromeos/input_method_manager.h"
@@ -1626,7 +1626,7 @@
   // Record each user's "Page zoom" setting for https://crbug.com/955071.
   // This can be removed after M79.
   double zoom_level = profile->GetZoomLevelPrefs()->GetDefaultZoomLevelPref();
-  double zoom_factor = content::ZoomLevelToZoomFactor(zoom_level);
+  double zoom_factor = blink::PageZoomLevelToZoomFactor(zoom_level);
   int zoom_percent = std::floor(zoom_factor * 100);
   // Zoom can be greater than 100%.
   UMA_HISTOGRAM_COUNTS_1000("Login.DefaultPageZoom", zoom_percent);
diff --git a/chrome/browser/chromeos/printing/cups_printers_manager.cc b/chrome/browser/chromeos/printing/cups_printers_manager.cc
index d7aa100..67f4ab77 100644
--- a/chrome/browser/chromeos/printing/cups_printers_manager.cc
+++ b/chrome/browser/chromeos/printing/cups_printers_manager.cc
@@ -248,20 +248,13 @@
     // detected printers.
     ClearNetworkDetectedPrinters();
   }
-
-  // mojom::CrosNetworkConfigObserver implementation.
   void OnNetworkStateChanged(
       chromeos::network_config::mojom::NetworkStatePropertiesPtr /* network */)
       override {}
-
-  // mojom::CrosNetworkConfigObserver implementation.
   void OnNetworkStateListChanged() override {}
-
-  // mojom::CrosNetworkConfigObserver implementation.
   void OnDeviceStateListChanged() override {}
-
-  // mojom::CrosNetworkConfigObserver implementation.
   void OnVpnProvidersChanged() override {}
+  void OnNetworkCertificatesChanged() override {}
 
   // Callback for PrinterDetectors.
   void OnPrintersFound(
diff --git a/chrome/browser/component_updater/tls_deprecation_config_component_installer.cc b/chrome/browser/component_updater/tls_deprecation_config_component_installer.cc
new file mode 100644
index 0000000..21904f6
--- /dev/null
+++ b/chrome/browser/component_updater/tls_deprecation_config_component_installer.cc
@@ -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.
+
+#include "chrome/browser/component_updater/tls_deprecation_config_component_installer.h"
+
+#include <memory>
+#include <utility>
+
+#include "base/bind.h"
+#include "base/files/file_util.h"
+#include "base/logging.h"
+#include "base/memory/ref_counted.h"
+#include "base/stl_util.h"
+#include "base/task/post_task.h"
+#include "base/task/task_traits.h"
+#include "chrome/browser/ssl/tls_deprecation_config.h"
+#include "chrome/browser/ssl/tls_deprecation_config.pb.h"
+#include "content/public/browser/browser_task_traits.h"
+#include "content/public/browser/browser_thread.h"
+
+using component_updater::ComponentUpdateService;
+
+namespace {
+
+const base::FilePath::CharType kTLSDeprecationConfigBinaryPbFileName[] =
+    FILE_PATH_LITERAL("tls_deprecation_config.pb");
+
+// The SHA256 of the SubjectPublicKeyInfo used to sign the extension.
+// The extension id is: bklopemakmnopmghhmccadeonafabnal
+const uint8_t kTLSDeprecationConfigPublicKeySHA256[32] = {
+    0x1a, 0xbe, 0xf4, 0xc0, 0xac, 0xde, 0xfc, 0x67, 0x7c, 0x22, 0x03,
+    0x4e, 0xd0, 0x50, 0x1d, 0x0b, 0xed, 0x45, 0x0f, 0xcb, 0x0b, 0x7f,
+    0xad, 0x4f, 0xb6, 0x7b, 0x7c, 0x8f, 0xbf, 0xda, 0xa8, 0xe3};
+
+std::unique_ptr<chrome_browser_ssl::LegacyTLSExperimentConfig>
+LoadTLSDeprecationConfigProtoFromDisk(const base::FilePath& pb_path) {
+  std::string binary_pb;
+  if (!base::ReadFileToString(pb_path, &binary_pb)) {
+    // The file won't exist on new installations, so this is not always an
+    // error.
+    DVLOG(1) << "Failed reading from " << pb_path.value();
+    return nullptr;
+  }
+  auto proto =
+      std::make_unique<chrome_browser_ssl::LegacyTLSExperimentConfig>();
+  if (!proto->ParseFromString(binary_pb)) {
+    DVLOG(1) << "Failed parsing proto " << pb_path.value();
+    return nullptr;
+  }
+  return proto;
+}
+
+base::FilePath GetInstalledPath(const base::FilePath& base) {
+  return base.Append(kTLSDeprecationConfigBinaryPbFileName);
+}
+
+}  // namespace
+
+namespace component_updater {
+
+TLSDeprecationConfigComponentInstallerPolicy::
+    TLSDeprecationConfigComponentInstallerPolicy() = default;
+
+TLSDeprecationConfigComponentInstallerPolicy::
+    ~TLSDeprecationConfigComponentInstallerPolicy() = default;
+
+bool TLSDeprecationConfigComponentInstallerPolicy::
+    SupportsGroupPolicyEnabledComponentUpdates() const {
+  return false;
+}
+
+bool TLSDeprecationConfigComponentInstallerPolicy::RequiresNetworkEncryption()
+    const {
+  return false;
+}
+
+update_client::CrxInstaller::Result
+TLSDeprecationConfigComponentInstallerPolicy::OnCustomInstall(
+    const base::DictionaryValue& /* manifest */,
+    const base::FilePath& /* install_dir */) {
+  return update_client::CrxInstaller::Result(0);  // Nothing custom here.
+}
+
+void TLSDeprecationConfigComponentInstallerPolicy::OnCustomUninstall() {}
+
+void TLSDeprecationConfigComponentInstallerPolicy::ComponentReady(
+    const base::Version& version,
+    const base::FilePath& install_dir,
+    std::unique_ptr<base::DictionaryValue> manifest) {
+  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+  DVLOG(1) << "Component ready, version " << version.GetString() << " in "
+           << install_dir.value();
+
+  const base::FilePath pb_path = GetInstalledPath(install_dir);
+  if (pb_path.empty())
+    return;
+
+  // The default proto will always be a placeholder since the updated versions
+  // are not checked into the repo. Simply load whatever the component updater
+  // gave us without checking the default proto from the resource bundle.
+  base::PostTaskAndReplyWithResult(
+      FROM_HERE,
+      {base::ThreadPool(), base::MayBlock(), base::TaskPriority::BEST_EFFORT},
+      base::BindOnce(&LoadTLSDeprecationConfigProtoFromDisk, pb_path),
+      base::BindOnce(&SetRemoteTLSDeprecationConfigProto));
+}
+
+// Called during startup and installation before ComponentReady().
+bool TLSDeprecationConfigComponentInstallerPolicy::VerifyInstallation(
+    const base::DictionaryValue& /* manifest */,
+    const base::FilePath& install_dir) const {
+  // No need to actually validate the proto here, since we'll do the checking
+  // in PopulateFromDynamicUpdate().
+  return base::PathExists(GetInstalledPath(install_dir));
+}
+
+base::FilePath
+TLSDeprecationConfigComponentInstallerPolicy::GetRelativeInstallDir() const {
+  return base::FilePath(FILE_PATH_LITERAL("TLSDeprecationConfig"));
+}
+
+void TLSDeprecationConfigComponentInstallerPolicy::GetHash(
+    std::vector<uint8_t>* hash) const {
+  hash->assign(kTLSDeprecationConfigPublicKeySHA256,
+               kTLSDeprecationConfigPublicKeySHA256 +
+                   base::size(kTLSDeprecationConfigPublicKeySHA256));
+}
+
+std::string TLSDeprecationConfigComponentInstallerPolicy::GetName() const {
+  return "Legacy TLS Deprecation Configuration";
+}
+
+update_client::InstallerAttributes
+TLSDeprecationConfigComponentInstallerPolicy::GetInstallerAttributes() const {
+  return update_client::InstallerAttributes();
+}
+
+std::vector<std::string>
+TLSDeprecationConfigComponentInstallerPolicy::GetMimeTypes() const {
+  return std::vector<std::string>();
+}
+
+void RegisterTLSDeprecationConfigComponent(
+    ComponentUpdateService* cus,
+    const base::FilePath& user_data_dir) {
+  DVLOG(1) << "Registering TLS Deprecation Config component.";
+
+  auto installer = base::MakeRefCounted<ComponentInstaller>(
+      std::make_unique<TLSDeprecationConfigComponentInstallerPolicy>());
+  installer->Register(cus, base::OnceClosure());
+}
+
+}  // namespace component_updater
diff --git a/chrome/browser/component_updater/tls_deprecation_config_component_installer.h b/chrome/browser/component_updater/tls_deprecation_config_component_installer.h
new file mode 100644
index 0000000..23f3e5b
--- /dev/null
+++ b/chrome/browser/component_updater/tls_deprecation_config_component_installer.h
@@ -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.
+
+#ifndef CHROME_BROWSER_COMPONENT_UPDATER_TLS_DEPRECATION_CONFIG_COMPONENT_INSTALLER_H_
+#define CHROME_BROWSER_COMPONENT_UPDATER_TLS_DEPRECATION_CONFIG_COMPONENT_INSTALLER_H_
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "base/macros.h"
+#include "base/values.h"
+#include "components/component_updater/component_installer.h"
+
+namespace base {
+class FilePath;
+}  // namespace base
+
+namespace component_updater {
+
+class TLSDeprecationConfigComponentInstallerPolicy
+    : public ComponentInstallerPolicy {
+ public:
+  TLSDeprecationConfigComponentInstallerPolicy();
+  ~TLSDeprecationConfigComponentInstallerPolicy() override;
+
+ private:
+  // ComponentInstallerPolicy methods:
+  bool SupportsGroupPolicyEnabledComponentUpdates() const override;
+  bool RequiresNetworkEncryption() const override;
+  update_client::CrxInstaller::Result OnCustomInstall(
+      const base::DictionaryValue& manifest,
+      const base::FilePath& install_dir) override;
+  void OnCustomUninstall() override;
+  bool VerifyInstallation(const base::DictionaryValue& manifest,
+                          const base::FilePath& install_dir) const override;
+  void ComponentReady(const base::Version& version,
+                      const base::FilePath& install_dir,
+                      std::unique_ptr<base::DictionaryValue> manifest) override;
+  base::FilePath GetRelativeInstallDir() const override;
+  void GetHash(std::vector<uint8_t>* hash) const override;
+  std::string GetName() const override;
+  update_client::InstallerAttributes GetInstallerAttributes() const override;
+  std::vector<std::string> GetMimeTypes() const override;
+
+  DISALLOW_COPY_AND_ASSIGN(TLSDeprecationConfigComponentInstallerPolicy);
+};
+
+void RegisterTLSDeprecationConfigComponent(ComponentUpdateService* cus,
+                                           const base::FilePath& user_data_dir);
+
+}  // namespace component_updater
+
+#endif  // CHROME_BROWSER_COMPONENT_UPDATER_TLS_DEPRECATION_CONFIG_COMPONENT_INSTALLER_H_
diff --git a/chrome/browser/crash_upload_list/crash_upload_list.cc b/chrome/browser/crash_upload_list/crash_upload_list.cc
index 6df26e5..9643397 100644
--- a/chrome/browser/crash_upload_list/crash_upload_list.cc
+++ b/chrome/browser/crash_upload_list/crash_upload_list.cc
@@ -34,9 +34,16 @@
           .AppendASCII(CrashUploadList::kReporterLogFilename);
   return new CrashUploadListAndroid(upload_log_path);
 #else
+
+// ChromeOS uses crash_sender as its uploader even when Crashpad is enabled,
+// which isn't compatible with CrashUploadListCrashpad. crash_sender continues
+// to log uploads in CrashUploadList::kReporterLogFilename.
+#if !defined(OS_CHROMEOS)
   if (crash_reporter::IsCrashpadEnabled()) {
     return new CrashUploadListCrashpad();
   }
+#endif
+
   base::FilePath crash_dir_path;
   base::PathService::Get(chrome::DIR_CRASH_DUMPS, &crash_dir_path);
   base::FilePath upload_log_path =
diff --git a/chrome/browser/devtools/frontend/devtools_discovery_page.html b/chrome/browser/devtools/frontend/devtools_discovery_page.html
index 88291aa..52880365 100644
--- a/chrome/browser/devtools/frontend/devtools_discovery_page.html
+++ b/chrome/browser/devtools/frontend/devtools_discovery_page.html
@@ -1,5 +1,5 @@
-<html>
-<head>
+<!doctype html>
+<html lang="en">
 <title>Inspectable pages</title>
 <meta name="referrer" content="no-referrer">
 <style>
@@ -102,6 +102,19 @@
   }
 }
 
+function onBlur(event) {
+  const selection = window.getSelection();
+  selection.removeAllRanges();
+  event.stopPropagation();
+  event.preventDefault();
+}
+
+function onFocus(selectElement, event) {
+  selectNodeText(selectElement, event);
+  event.stopPropagation();
+  event.preventDefault();
+}
+
 function customFrontendURL(url) {
   if (!url || !window.location.hash)
     return null;
@@ -164,6 +177,9 @@
     var urlValue = document.createElement('div');
     urlValue.classList.add("custom-url-value");
     urlValue.textContent = customURL;
+    urlValue.tabIndex = 0;
+    urlValue.addEventListener('blur', onBlur);
+    urlValue.addEventListener('focus', event => onFocus(urlValue, event));
     urlContainer.appendChild(urlValue);
     description.appendChild(urlContainer);
     item_element.addEventListener('click', selectNodeText.bind(null, urlValue));
@@ -189,9 +205,11 @@
 </script>
 </head>
 <body onload='onLoad()'>
-  <div id='caption'>Inspectable pages</div>
-  <hr>
-  <div id='items'>
+  <div role='main'>
+    <div id='caption' role='heading' aria-level='1'>Inspectable pages</div>
+    <hr>
+    <div id='items'>
+    </div>
   </div>
 </body>
 </html>
diff --git a/chrome/browser/extensions/api/extension_action/browser_action_interactive_test.cc b/chrome/browser/extensions/api/extension_action/browser_action_interactive_test.cc
index 8fd11ee..5d765101 100644
--- a/chrome/browser/extensions/api/extension_action/browser_action_interactive_test.cc
+++ b/chrome/browser/extensions/api/extension_action/browser_action_interactive_test.cc
@@ -26,7 +26,6 @@
 #include "content/public/browser/host_zoom_map.h"
 #include "content/public/browser/notification_service.h"
 #include "content/public/browser/notification_types.h"
-#include "content/public/common/page_zoom.h"
 #include "extensions/browser/extension_registry.h"
 #include "extensions/browser/notification_types.h"
 #include "extensions/common/extension.h"
@@ -34,6 +33,7 @@
 #include "extensions/common/permissions/permissions_data.h"
 #include "extensions/test/extension_test_message_listener.h"
 #include "extensions/test/result_catcher.h"
+#include "third_party/blink/public/common/page/page_zoom.h"
 #include "ui/base/buildflags.h"
 
 #if defined(OS_WIN)
@@ -465,13 +465,13 @@
       content::HostZoomMap::GetForWebContents(popup_contents)
           ->GetDefaultZoomLevel();
   double popup_zoom_level = content::HostZoomMap::GetZoomLevel(popup_contents);
-  EXPECT_TRUE(content::ZoomValuesEqual(popup_zoom_level, default_zoom_level))
+  EXPECT_TRUE(blink::PageZoomValuesEqual(popup_zoom_level, default_zoom_level))
       << popup_zoom_level << " vs " << default_zoom_level;
 
   // Preventing the use of the per-origin zoom level in the popup should not
   // affect the zoom of the tab.
-  EXPECT_TRUE(content::ZoomValuesEqual(zoom_controller->GetZoomLevel(),
-                                       tab_new_zoom_level))
+  EXPECT_TRUE(blink::PageZoomValuesEqual(zoom_controller->GetZoomLevel(),
+                                         tab_new_zoom_level))
       << zoom_controller->GetZoomLevel() << " vs " << tab_new_zoom_level;
 
   // Subsequent zooming in the tab should also be done independently of the
@@ -487,7 +487,7 @@
   zoom_change_watcher2.Wait();
 
   popup_zoom_level = content::HostZoomMap::GetZoomLevel(popup_contents);
-  EXPECT_TRUE(content::ZoomValuesEqual(popup_zoom_level, default_zoom_level))
+  EXPECT_TRUE(blink::PageZoomValuesEqual(popup_zoom_level, default_zoom_level))
       << popup_zoom_level << " vs " << default_zoom_level;
 
   ClosePopup();
diff --git a/chrome/browser/extensions/api/settings_private/settings_private_delegate.cc b/chrome/browser/extensions/api/settings_private/settings_private_delegate.cc
index 9eb36246..1c6eddf0 100644
--- a/chrome/browser/extensions/api/settings_private/settings_private_delegate.cc
+++ b/chrome/browser/extensions/api/settings_private/settings_private_delegate.cc
@@ -15,9 +15,9 @@
 #include "chrome/browser/ui/zoom/chrome_zoom_level_prefs.h"
 #include "chrome/common/pref_names.h"
 #include "components/prefs/pref_service.h"
-#include "content/public/common/page_zoom.h"
 #include "extensions/browser/extension_registry.h"
 #include "extensions/common/extension.h"
+#include "third_party/blink/public/common/page/page_zoom.h"
 #include "url/gurl.h"
 
 namespace extensions {
@@ -64,7 +64,7 @@
   // default value.
   if (profile_->IsOffTheRecord())
     return std::make_unique<base::Value>(0.0);
-  double zoom = content::ZoomLevelToZoomFactor(
+  double zoom = blink::PageZoomLevelToZoomFactor(
       profile_->GetZoomLevelPrefs()->GetDefaultZoomLevelPref());
   return std::make_unique<base::Value>(zoom);
 }
@@ -74,7 +74,7 @@
   // See comment in GetDefaultZoom().
   if (profile_->IsOffTheRecord())
     return settings_private::SetPrefResult::PREF_NOT_MODIFIABLE;
-  double zoom_factor = content::ZoomFactorToZoomLevel(zoom);
+  double zoom_factor = blink::PageZoomFactorToZoomLevel(zoom);
   profile_->GetZoomLevelPrefs()->SetDefaultZoomLevelPref(zoom_factor);
   return settings_private::SetPrefResult::SUCCESS;
 }
diff --git a/chrome/browser/extensions/api/tabs/tabs_api.cc b/chrome/browser/extensions/api/tabs/tabs_api.cc
index 3ca92c0c..07871d2 100644
--- a/chrome/browser/extensions/api/tabs/tabs_api.cc
+++ b/chrome/browser/extensions/api/tabs/tabs_api.cc
@@ -92,6 +92,7 @@
 #include "net/base/escape.h"
 #include "skia/ext/image_operations.h"
 #include "skia/ext/platform_canvas.h"
+#include "third_party/blink/public/common/page/page_zoom.h"
 #include "third_party/skia/include/core/SkBitmap.h"
 #include "ui/base/models/list_selection_model.h"
 #include "ui/base/ui_base_types.h"
@@ -1997,9 +1998,10 @@
 
   ZoomController* zoom_controller =
       ZoomController::FromWebContents(web_contents);
-  double zoom_level = params->zoom_factor > 0
-                          ? content::ZoomFactorToZoomLevel(params->zoom_factor)
-                          : zoom_controller->GetDefaultZoomLevel();
+  double zoom_level =
+      params->zoom_factor > 0
+          ? blink::PageZoomFactorToZoomLevel(params->zoom_factor)
+          : zoom_controller->GetDefaultZoomLevel();
 
   auto client = base::MakeRefCounted<ExtensionZoomRequestClient>(extension());
   if (!zoom_controller->SetZoomLevelByClient(zoom_level, client)) {
@@ -2024,7 +2026,7 @@
 
   double zoom_level =
       ZoomController::FromWebContents(web_contents)->GetZoomLevel();
-  double zoom_factor = content::ZoomLevelToZoomFactor(zoom_level);
+  double zoom_factor = blink::PageZoomLevelToZoomFactor(zoom_level);
 
   return RespondNow(ArgumentList(tabs::GetZoom::Results::Create(zoom_factor)));
 }
@@ -2098,8 +2100,9 @@
   ZoomController::ZoomMode zoom_mode = zoom_controller->zoom_mode();
   api::tabs::ZoomSettings zoom_settings;
   ZoomModeToZoomSettings(zoom_mode, &zoom_settings);
-  zoom_settings.default_zoom_factor.reset(new double(
-      content::ZoomLevelToZoomFactor(zoom_controller->GetDefaultZoomLevel())));
+  zoom_settings.default_zoom_factor.reset(
+      new double(blink::PageZoomLevelToZoomFactor(
+          zoom_controller->GetDefaultZoomLevel())));
 
   return RespondNow(
       ArgumentList(api::tabs::GetZoomSettings::Results::Create(zoom_settings)));
diff --git a/chrome/browser/extensions/api/tabs/tabs_event_router.cc b/chrome/browser/extensions/api/tabs/tabs_event_router.cc
index 5459c09..402d3de 100644
--- a/chrome/browser/extensions/api/tabs/tabs_event_router.cc
+++ b/chrome/browser/extensions/api/tabs/tabs_event_router.cc
@@ -28,6 +28,7 @@
 #include "content/public/browser/navigation_entry.h"
 #include "content/public/browser/web_contents.h"
 #include "extensions/common/features/feature.h"
+#include "third_party/blink/public/common/page/page_zoom.h"
 
 using base::DictionaryValue;
 using base::ListValue;
@@ -272,9 +273,9 @@
   api::tabs::OnZoomChange::ZoomChangeInfo zoom_change_info;
   zoom_change_info.tab_id = tab_id;
   zoom_change_info.old_zoom_factor =
-      content::ZoomLevelToZoomFactor(data.old_zoom_level);
+      blink::PageZoomLevelToZoomFactor(data.old_zoom_level);
   zoom_change_info.new_zoom_factor =
-      content::ZoomLevelToZoomFactor(data.new_zoom_level);
+      blink::PageZoomLevelToZoomFactor(data.new_zoom_level);
   ZoomModeToZoomSettings(data.zoom_mode, &zoom_change_info.zoom_settings);
 
   // Dispatch the |onZoomChange| event.
diff --git a/chrome/browser/extensions/api/tabs/tabs_test.cc b/chrome/browser/extensions/api/tabs/tabs_test.cc
index eea7cbf..e908335 100644
--- a/chrome/browser/extensions/api/tabs/tabs_test.cc
+++ b/chrome/browser/extensions/api/tabs/tabs_test.cc
@@ -48,7 +48,6 @@
 #include "content/public/browser/render_frame_host.h"
 #include "content/public/browser/storage_partition.h"
 #include "content/public/common/mime_handler_view_mode.h"
-#include "content/public/common/page_zoom.h"
 #include "content/public/common/url_constants.h"
 #include "content/public/test/browser_test_utils.h"
 #include "content/public/test/test_navigation_observer.h"
@@ -61,6 +60,7 @@
 #include "extensions/test/extension_test_message_listener.h"
 #include "extensions/test/result_catcher.h"
 #include "net/test/embedded_test_server/embedded_test_server.h"
+#include "third_party/blink/public/common/page/page_zoom.h"
 #include "ui/base/window_open_disposition.h"
 #include "ui/gfx/geometry/rect.h"
 #include "ui/views/widget/widget.h"
@@ -1846,7 +1846,7 @@
   const double kZoomLevel = 0.8;
   EXPECT_TRUE(RunSetZoom(tab_id, kZoomLevel));
   EXPECT_EQ(kZoomLevel,
-            content::ZoomLevelToZoomFactor(GetZoomLevel(web_contents)));
+            blink::PageZoomLevelToZoomFactor(GetZoomLevel(web_contents)));
 
   // Test chrome.tabs.getZoom().
   zoom_factor = -1;
@@ -1863,9 +1863,9 @@
       zoom::ZoomController::FromWebContents(web_contents);
   double default_zoom_factor = -1.0;
   EXPECT_TRUE(RunGetDefaultZoom(tab_id, &default_zoom_factor));
-  EXPECT_TRUE(content::ZoomValuesEqual(
+  EXPECT_TRUE(blink::PageZoomValuesEqual(
       zoom_controller->GetDefaultZoomLevel(),
-      content::ZoomFactorToZoomLevel(default_zoom_factor)));
+      blink::PageZoomFactorToZoomLevel(default_zoom_factor)));
 
   // Change the default zoom level and verify GetDefaultZoom returns the
   // correct value.
@@ -1879,9 +1879,9 @@
   zoom_prefs->SetDefaultZoomLevelPref(default_zoom_level + 0.5);
   default_zoom_factor = -1.0;
   EXPECT_TRUE(RunGetDefaultZoom(tab_id, &default_zoom_factor));
-  EXPECT_TRUE(content::ZoomValuesEqual(
+  EXPECT_TRUE(blink::PageZoomValuesEqual(
       default_zoom_level + 0.5,
-      content::ZoomFactorToZoomLevel(default_zoom_factor)));
+      blink::PageZoomFactorToZoomLevel(default_zoom_factor)));
 }
 
 IN_PROC_BROWSER_TEST_F(ExtensionTabsZoomTest, SetToDefaultZoom) {
@@ -1905,9 +1905,9 @@
   double observed_zoom_factor = -1.0;
   EXPECT_TRUE(RunSetZoom(tab_id, 0.0));
   EXPECT_TRUE(RunGetZoom(tab_id, &observed_zoom_factor));
-  EXPECT_TRUE(content::ZoomValuesEqual(
+  EXPECT_TRUE(blink::PageZoomValuesEqual(
       new_default_zoom_level,
-      content::ZoomFactorToZoomLevel(observed_zoom_factor)));
+      blink::PageZoomFactorToZoomLevel(observed_zoom_factor)));
 }
 
 IN_PROC_BROWSER_TEST_F(ExtensionTabsZoomTest, ZoomSettings) {
@@ -1935,48 +1935,48 @@
   int tab_id_B = ExtensionTabUtil::GetTabId(web_contents_B);
 
   ASSERT_FLOAT_EQ(
-      1.f, content::ZoomLevelToZoomFactor(GetZoomLevel(web_contents_A1)));
+      1.f, blink::PageZoomLevelToZoomFactor(GetZoomLevel(web_contents_A1)));
   ASSERT_FLOAT_EQ(
-      1.f, content::ZoomLevelToZoomFactor(GetZoomLevel(web_contents_A2)));
+      1.f, blink::PageZoomLevelToZoomFactor(GetZoomLevel(web_contents_A2)));
   ASSERT_FLOAT_EQ(
-      1.f, content::ZoomLevelToZoomFactor(GetZoomLevel(web_contents_B)));
+      1.f, blink::PageZoomLevelToZoomFactor(GetZoomLevel(web_contents_B)));
 
   // Test per-origin automatic zoom settings.
   EXPECT_TRUE(RunSetZoom(tab_id_B, 1.f));
   EXPECT_TRUE(RunSetZoom(tab_id_A2, 1.1f));
   EXPECT_FLOAT_EQ(
-      1.1f, content::ZoomLevelToZoomFactor(GetZoomLevel(web_contents_A1)));
+      1.1f, blink::PageZoomLevelToZoomFactor(GetZoomLevel(web_contents_A1)));
   EXPECT_FLOAT_EQ(
-      1.1f, content::ZoomLevelToZoomFactor(GetZoomLevel(web_contents_A2)));
-  EXPECT_FLOAT_EQ(1.f,
-                  content::ZoomLevelToZoomFactor(GetZoomLevel(web_contents_B)));
+      1.1f, blink::PageZoomLevelToZoomFactor(GetZoomLevel(web_contents_A2)));
+  EXPECT_FLOAT_EQ(
+      1.f, blink::PageZoomLevelToZoomFactor(GetZoomLevel(web_contents_B)));
 
   // Test per-tab automatic zoom settings.
   EXPECT_TRUE(RunSetZoomSettings(tab_id_A1, "automatic", "per-tab"));
   EXPECT_TRUE(RunSetZoom(tab_id_A1, 1.2f));
   EXPECT_FLOAT_EQ(
-      1.2f, content::ZoomLevelToZoomFactor(GetZoomLevel(web_contents_A1)));
+      1.2f, blink::PageZoomLevelToZoomFactor(GetZoomLevel(web_contents_A1)));
   EXPECT_FLOAT_EQ(
-      1.1f, content::ZoomLevelToZoomFactor(GetZoomLevel(web_contents_A2)));
+      1.1f, blink::PageZoomLevelToZoomFactor(GetZoomLevel(web_contents_A2)));
 
   // Test 'manual' mode.
   EXPECT_TRUE(RunSetZoomSettings(tab_id_A1, "manual", nullptr));
   EXPECT_TRUE(RunSetZoom(tab_id_A1, 1.3f));
   EXPECT_FLOAT_EQ(
-      1.3f, content::ZoomLevelToZoomFactor(GetZoomLevel(web_contents_A1)));
+      1.3f, blink::PageZoomLevelToZoomFactor(GetZoomLevel(web_contents_A1)));
   EXPECT_FLOAT_EQ(
-      1.1f, content::ZoomLevelToZoomFactor(GetZoomLevel(web_contents_A2)));
+      1.1f, blink::PageZoomLevelToZoomFactor(GetZoomLevel(web_contents_A2)));
 
   // Test 'disabled' mode, which will reset A1's zoom to 1.f.
   EXPECT_TRUE(RunSetZoomSettings(tab_id_A1, "disabled", nullptr));
   std::string error = RunSetZoomExpectError(tab_id_A1, 1.4f);
   EXPECT_TRUE(base::MatchPattern(error, keys::kCannotZoomDisabledTabError));
   EXPECT_FLOAT_EQ(
-      1.f, content::ZoomLevelToZoomFactor(GetZoomLevel(web_contents_A1)));
+      1.f, blink::PageZoomLevelToZoomFactor(GetZoomLevel(web_contents_A1)));
   // We should still be able to zoom A2 though.
   EXPECT_TRUE(RunSetZoom(tab_id_A2, 1.4f));
   EXPECT_FLOAT_EQ(
-      1.4f, content::ZoomLevelToZoomFactor(GetZoomLevel(web_contents_A2)));
+      1.4f, blink::PageZoomLevelToZoomFactor(GetZoomLevel(web_contents_A2)));
 }
 
 IN_PROC_BROWSER_TEST_F(ExtensionTabsZoomTest, PerTabResetsOnNavigation) {
diff --git a/chrome/browser/flag_descriptions.cc b/chrome/browser/flag_descriptions.cc
index 95fc4cf0..686c556 100644
--- a/chrome/browser/flag_descriptions.cc
+++ b/chrome/browser/flag_descriptions.cc
@@ -3848,17 +3848,6 @@
 
 #endif  // #if defined(WEBRTC_USE_PIPEWIRE)
 
-#if defined(OS_LINUX) && !defined(OS_CHROMEOS)
-
-const char kEnableDbusAndX11StatusIconsName[] =
-    "Enable DBus and X11 status icons";
-const char kEnableDbusAndX11StatusIconsDescription[] =
-    "If enabled, uses Chromium's StatusNotifierItem (DBus) and system tray "
-    "(X11) implementations of status icons.  Otherwise, uses libappindicator's "
-    "and GTK's implementations.";
-
-#endif  // defined(OS_LINUX) && !defined(OS_CHROMEOS)
-
 const char kAvoidFlashBetweenNavigationName[] =
     "Enable flash avoidance between same-origin navigations";
 const char kAvoidFlahsBetweenNavigationDescription[] =
diff --git a/chrome/browser/flag_descriptions.h b/chrome/browser/flag_descriptions.h
index 845cdbd..4dffcef 100644
--- a/chrome/browser/flag_descriptions.h
+++ b/chrome/browser/flag_descriptions.h
@@ -2337,13 +2337,6 @@
 
 #endif  // #if defined(WEBRTC_USE_PIPEWIRE)
 
-#if defined(OS_LINUX) && !defined(OS_CHROMEOS)
-
-extern const char kEnableDbusAndX11StatusIconsName[];
-extern const char kEnableDbusAndX11StatusIconsDescription[];
-
-#endif  // defined(OS_LINUX) && !defined(OS_CHROMEOS)
-
 // ============================================================================
 // Don't just add flags to the end, put them in the right section in
 // alphabetical order. See top instructions for more.
diff --git a/chrome/browser/image_fetcher/image_fetcher_service_factory.cc b/chrome/browser/image_fetcher/image_fetcher_service_factory.cc
index 51db76c2..8812e23a 100644
--- a/chrome/browser/image_fetcher/image_fetcher_service_factory.cc
+++ b/chrome/browser/image_fetcher/image_fetcher_service_factory.cc
@@ -84,8 +84,13 @@
           std::move(data_store), std::move(metadata_store),
           profile_key->GetPrefs(), clock, task_runner);
 
-  scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory =
-      SystemNetworkContextManager::GetInstance()->GetSharedURLLoaderFactory();
+  scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory = nullptr;
+  // Network is null for some tests, may be removable after
+  // https://crbug.com/981057.
+  if (SystemNetworkContextManager::GetInstance()) {
+    url_loader_factory =
+        SystemNetworkContextManager::GetInstance()->GetSharedURLLoaderFactory();
+  }
 
   auto cached_image_fetcher_service =
       std::make_unique<image_fetcher::ImageFetcherService>(
diff --git a/chrome/browser/mac/install_from_dmg.mm b/chrome/browser/mac/install_from_dmg.mm
index bf53a40..3e39893 100644
--- a/chrome/browser/mac/install_from_dmg.mm
+++ b/chrome/browser/mac/install_from_dmg.mm
@@ -357,8 +357,11 @@
                         const std::string& dmg_bsd_device_name) {
   base::FilePath browser_path([installed_path fileSystemRepresentation]);
 
-  base::FilePath helper_path = browser_path.Append("Contents/Versions");
+  base::FilePath helper_path = browser_path.Append("Contents/Frameworks");
+  helper_path = helper_path.Append(chrome::kFrameworkName);
+  helper_path = helper_path.Append("Versions");
   helper_path = helper_path.Append(chrome::kChromeVersion);
+  helper_path = helper_path.Append("Helpers");
   helper_path = helper_path.Append(chrome::kHelperProcessExecutablePath);
 
   std::vector<std::string> args =
diff --git a/chrome/browser/no_best_effort_tasks_browsertest.cc b/chrome/browser/no_best_effort_tasks_browsertest.cc
index 91972d32..029e8884 100644
--- a/chrome/browser/no_best_effort_tasks_browsertest.cc
+++ b/chrome/browser/no_best_effort_tasks_browsertest.cc
@@ -12,6 +12,7 @@
 #include "base/strings/stringprintf.h"
 #include "build/build_config.h"
 #include "build/buildflag.h"
+#include "chrome/browser/chrome_content_browser_client.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/tabs/tab_strip_model.h"
@@ -247,3 +248,53 @@
             content::EvalJs(
                 browser()->tab_strip_model()->GetActiveWebContents(), kScript));
 }
+
+// A test specialization for verifying quota storage related operations do not
+// use BEST_EFFORT tasks.
+class NoBestEffortTasksTestWithQuota : public NoBestEffortTasksTest {
+ protected:
+  std::unique_ptr<storage::QuotaSettings> CreateQuotaSettings() override {
+    // Return nullptr to use the real quota subsystem.
+    return nullptr;
+  }
+};
+
+// Verify that cache_storage finishes without running BEST_EFFORT tasks.
+// Regression test for https://crbug.com/1006546.
+IN_PROC_BROWSER_TEST_F(NoBestEffortTasksTestWithQuota, CacheStorage) {
+  ASSERT_TRUE(embedded_test_server()->Start());
+  ui_test_utils::NavigateToURL(browser(),
+                               embedded_test_server()->GetURL("/empty.html"));
+  const char kScript[] = R"(
+      (async function() {
+        const name = 'foo';
+        const url = '/';
+        const body = 'hello world';
+        let c = await caches.open(name);
+        await c.put(url, new Response(body));
+        let r = await c.match(url);
+        await r.text();
+        return 'DONE';
+      })();
+  )";
+  EXPECT_EQ("DONE",
+            content::EvalJs(
+                browser()->tab_strip_model()->GetActiveWebContents(), kScript));
+}
+
+// Verify that quota estimate() finishes without running BEST_EFFORT tasks.
+// Regression test for https://crbug.com/1006546.
+IN_PROC_BROWSER_TEST_F(NoBestEffortTasksTestWithQuota, QuotaEstimate) {
+  ASSERT_TRUE(embedded_test_server()->Start());
+  ui_test_utils::NavigateToURL(browser(),
+                               embedded_test_server()->GetURL("/empty.html"));
+  const char kScript[] = R"(
+      (async function() {
+        await navigator.storage.estimate();
+        return 'DONE';
+      })();
+  )";
+  EXPECT_EQ("DONE",
+            content::EvalJs(
+                browser()->tab_strip_model()->GetActiveWebContents(), kScript));
+}
diff --git a/chrome/browser/optimization_guide/prediction/decision_tree_prediction_model.cc b/chrome/browser/optimization_guide/prediction/decision_tree_prediction_model.cc
new file mode 100644
index 0000000..4e5febe8
--- /dev/null
+++ b/chrome/browser/optimization_guide/prediction/decision_tree_prediction_model.cc
@@ -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.
+
+#include "chrome/browser/optimization_guide/prediction/decision_tree_prediction_model.h"
+#include "chrome/browser/optimization_guide/prediction/prediction_model.h"
+
+namespace optimization_guide {
+
+DecisionTreePredictionModel::DecisionTreePredictionModel(
+    std::unique_ptr<optimization_guide::proto::PredictionModel>
+        prediction_model,
+    const base::flat_set<std::string>& host_model_features)
+    : PredictionModel(std::move(prediction_model), host_model_features) {}
+
+DecisionTreePredictionModel::~DecisionTreePredictionModel() = default;
+
+optimization_guide::OptimizationTargetDecision
+DecisionTreePredictionModel::Predict(
+    const base::flat_map<std::string, float>& model_features) {
+  SEQUENCE_CHECKER(sequence_checker_);
+  // TODO(crbug/1001194): Add decision tree evaluation implementation.
+  return optimization_guide::OptimizationTargetDecision::kUnknown;
+}
+
+}  // namespace optimization_guide
diff --git a/chrome/browser/optimization_guide/prediction/decision_tree_prediction_model.h b/chrome/browser/optimization_guide/prediction/decision_tree_prediction_model.h
new file mode 100644
index 0000000..de2b84c
--- /dev/null
+++ b/chrome/browser/optimization_guide/prediction/decision_tree_prediction_model.h
@@ -0,0 +1,43 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_OPTIMIZATION_GUIDE_PREDICTION_DECISION_TREE_PREDICTION_MODEL_H_
+#define CHROME_BROWSER_OPTIMIZATION_GUIDE_PREDICTION_DECISION_TREE_PREDICTION_MODEL_H_
+
+#include <memory>
+#include <string>
+
+#include "base/containers/flat_map.h"
+#include "base/containers/flat_set.h"
+#include "base/macros.h"
+#include "base/sequence_checker.h"
+#include "chrome/browser/optimization_guide/prediction/prediction_model.h"
+#include "components/optimization_guide/proto/models.pb.h"
+
+namespace optimization_guide {
+
+// A concrete PredictionModel capable of evaluating the decision tree model type
+// supported by the optimization guide.
+class DecisionTreePredictionModel : public PredictionModel {
+ public:
+  DecisionTreePredictionModel(
+      std::unique_ptr<optimization_guide::proto::PredictionModel>
+          prediction_model,
+      const base::flat_set<std::string>& host_model_features);
+
+  ~DecisionTreePredictionModel() override;
+
+  // PredictionModel implementation:
+  optimization_guide::OptimizationTargetDecision Predict(
+      const base::flat_map<std::string, float>& model_features) override;
+
+ private:
+  SEQUENCE_CHECKER(sequence_checker_);
+
+  DISALLOW_COPY_AND_ASSIGN(DecisionTreePredictionModel);
+};
+
+}  // namespace optimization_guide
+
+#endif  // CHROME_BROWSER_OPTIMIZATION_GUIDE_PREDICTION_DECISION_TREE_PREDICTION_MODEL_H_
diff --git a/chrome/browser/optimization_guide/prediction/decision_tree_prediction_model_unittest.cc b/chrome/browser/optimization_guide/prediction/decision_tree_prediction_model_unittest.cc
new file mode 100644
index 0000000..c478a9e
--- /dev/null
+++ b/chrome/browser/optimization_guide/prediction/decision_tree_prediction_model_unittest.cc
@@ -0,0 +1,45 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <memory>
+
+#include "base/containers/flat_map.h"
+#include "base/containers/flat_set.h"
+#include "chrome/browser/optimization_guide/prediction/decision_tree_prediction_model.h"
+#include "chrome/browser/optimization_guide/prediction/prediction_model.h"
+#include "components/optimization_guide/proto/models.pb.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace optimization_guide {
+
+TEST(DecisionTreePredictionModel, ValidDecisionTreeModel) {
+  std::unique_ptr<optimization_guide::proto::PredictionModel> prediction_model =
+      std::make_unique<optimization_guide::proto::PredictionModel>();
+
+  optimization_guide::proto::DecisionTree* decision_tree_model =
+      prediction_model->mutable_model()->mutable_decision_tree();
+  decision_tree_model->set_weight(2.0);
+
+  optimization_guide::proto::ModelInfo* model_info =
+      prediction_model->mutable_model_info();
+  model_info->set_version(1);
+  model_info->add_supported_model_types(
+      optimization_guide::proto::ModelType::MODEL_TYPE_DECISION_TREE);
+  model_info->add_supported_model_features(
+      optimization_guide::proto::ClientModelFeature::
+          CLIENT_MODEL_FEATURE_EFFECTIVE_CONNECTION_TYPE);
+
+  std::unique_ptr<PredictionModel> model =
+      PredictionModel::Create(std::move(prediction_model), {"agg1"});
+  base::flat_map<std::string, float> feature_map = {};
+  EXPECT_EQ(1, model->GetVersion());
+  EXPECT_EQ(2u, model->GetModelFeatures().size());
+  EXPECT_TRUE(model->GetModelFeatures().count("agg1"));
+  EXPECT_TRUE(model->GetModelFeatures().count(
+      "CLIENT_MODEL_FEATURE_EFFECTIVE_CONNECTION_TYPE"));
+  EXPECT_EQ(optimization_guide::OptimizationTargetDecision::kUnknown,
+            model->Predict(feature_map));
+}
+
+}  // namespace optimization_guide
diff --git a/chrome/browser/optimization_guide/optimization_guide_prediction_manager_browsertest.cc b/chrome/browser/optimization_guide/prediction/prediction_manager_browsertest.cc
similarity index 90%
rename from chrome/browser/optimization_guide/optimization_guide_prediction_manager_browsertest.cc
rename to chrome/browser/optimization_guide/prediction/prediction_manager_browsertest.cc
index 03d7c55..9cd52a6b 100644
--- a/chrome/browser/optimization_guide/optimization_guide_prediction_manager_browsertest.cc
+++ b/chrome/browser/optimization_guide/prediction/prediction_manager_browsertest.cc
@@ -19,11 +19,12 @@
 #include "net/test/embedded_test_server/http_request.h"
 #include "net/test/embedded_test_server/http_response.h"
 
-class OptimizationGuidePredictionManagerBrowserTest
-    : public InProcessBrowserTest {
+namespace optimization_guide {
+
+class PredictionManagerBrowserTest : public InProcessBrowserTest {
  public:
-  OptimizationGuidePredictionManagerBrowserTest() = default;
-  ~OptimizationGuidePredictionManagerBrowserTest() override = default;
+  PredictionManagerBrowserTest() = default;
+  ~PredictionManagerBrowserTest() override = default;
 
   void SetUp() override {
     scoped_feature_list_.InitWithFeatures(
@@ -68,10 +69,10 @@
   std::unique_ptr<net::EmbeddedTestServer> https_server_;
   base::test::ScopedFeatureList scoped_feature_list_;
 
-  DISALLOW_COPY_AND_ASSIGN(OptimizationGuidePredictionManagerBrowserTest);
+  DISALLOW_COPY_AND_ASSIGN(PredictionManagerBrowserTest);
 };
 
-IN_PROC_BROWSER_TEST_F(OptimizationGuidePredictionManagerBrowserTest,
+IN_PROC_BROWSER_TEST_F(PredictionManagerBrowserTest,
                        FCPReachedSessionStatisticsUpdated) {
   OptimizationGuideKeyedService* keyed_service =
       OptimizationGuideKeyedServiceFactory::GetForProfile(browser()->profile());
@@ -89,7 +90,7 @@
   EXPECT_EQ(1u, session_fcp->GetNumberOfSamples());
 }
 
-IN_PROC_BROWSER_TEST_F(OptimizationGuidePredictionManagerBrowserTest,
+IN_PROC_BROWSER_TEST_F(PredictionManagerBrowserTest,
                        NoFCPSessionStatisticsUnchanged) {
   OptimizationGuideKeyedService* keyed_service =
       OptimizationGuideKeyedServiceFactory::GetForProfile(browser()->profile());
@@ -113,3 +114,5 @@
   EXPECT_EQ(1u, session_fcp->GetNumberOfSamples());
   EXPECT_EQ(current_mean, session_fcp->GetMean());
 }
+
+}  // namespace optimization_guide
diff --git a/chrome/browser/optimization_guide/prediction/prediction_model.cc b/chrome/browser/optimization_guide/prediction/prediction_model.cc
new file mode 100644
index 0000000..c9f730aa
--- /dev/null
+++ b/chrome/browser/optimization_guide/prediction/prediction_model.cc
@@ -0,0 +1,87 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/optimization_guide/prediction/prediction_model.h"
+#include "chrome/browser/optimization_guide/prediction/decision_tree_prediction_model.h"
+
+namespace optimization_guide {
+
+// static
+std::unique_ptr<PredictionModel> PredictionModel::Create(
+    std::unique_ptr<optimization_guide::proto::PredictionModel>
+        prediction_model,
+    const base::flat_set<std::string>& host_model_features) {
+  if (!prediction_model->has_model())
+    return nullptr;
+
+  if (!prediction_model->has_model_info())
+    return nullptr;
+
+  if (!prediction_model->model_info().has_version())
+    return nullptr;
+
+  // Enforce that only one ModelType is specified for the PredictionModel.
+  if (prediction_model->model_info().supported_model_types_size() != 1) {
+    return nullptr;
+  }
+
+  // Check that the client supports this type of model and is not an unknown
+  // type.
+  if (!optimization_guide::proto::ModelType_IsValid(
+          prediction_model->model_info().supported_model_types(0)) ||
+      prediction_model->model_info().supported_model_types(0) ==
+          optimization_guide::proto::ModelType::MODEL_TYPE_UNKNOWN) {
+    return nullptr;
+  }
+
+  // Check that the client supports the model features for |prediction model|.
+  for (const auto& model_feature :
+       prediction_model->model_info().supported_model_features()) {
+    if (!optimization_guide::proto::ClientModelFeature_IsValid(model_feature) ||
+        model_feature == optimization_guide::proto::ClientModelFeature::
+                             CLIENT_MODEL_FEATURE_UNKNOWN)
+      return nullptr;
+  }
+
+  // The Decision Tree model type is currently the only supported model type.
+  if (prediction_model->model_info().supported_model_types(0) ==
+      optimization_guide::proto::ModelType::MODEL_TYPE_DECISION_TREE) {
+    return std::make_unique<DecisionTreePredictionModel>(
+        std::move(prediction_model), host_model_features);
+  }
+  return nullptr;
+}
+
+PredictionModel::PredictionModel(
+    std::unique_ptr<optimization_guide::proto::PredictionModel>
+        prediction_model,
+    const base::flat_set<std::string>& host_model_features) {
+  version_ = prediction_model->model_info().version();
+  model_features_.reserve(
+      prediction_model->model_info().supported_model_features_size() +
+      host_model_features.size());
+  // Insert all the client model features for the owned |model_|.
+  for (const auto& client_model_feature :
+       prediction_model->model_info().supported_model_features()) {
+    model_features_.emplace(optimization_guide::proto::ClientModelFeature_Name(
+        client_model_feature));
+  }
+  // Insert all the host model features for the owned |model_|.
+  for (const auto& host_model_feature : host_model_features)
+    model_features_.emplace(host_model_feature);
+}
+
+int64_t PredictionModel::GetVersion() const {
+  SEQUENCE_CHECKER(sequence_checker_);
+  return version_;
+}
+
+base::flat_set<std::string> PredictionModel::GetModelFeatures() const {
+  SEQUENCE_CHECKER(sequence_checker_);
+  return model_features_;
+}
+
+PredictionModel::~PredictionModel() = default;
+
+}  // namespace optimization_guide
diff --git a/chrome/browser/optimization_guide/prediction/prediction_model.h b/chrome/browser/optimization_guide/prediction/prediction_model.h
new file mode 100644
index 0000000..e4dfd4b
--- /dev/null
+++ b/chrome/browser/optimization_guide/prediction/prediction_model.h
@@ -0,0 +1,71 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_OPTIMIZATION_GUIDE_PREDICTION_PREDICTION_MODEL_H_
+#define CHROME_BROWSER_OPTIMIZATION_GUIDE_PREDICTION_PREDICTION_MODEL_H_
+
+#include <stdint.h>
+#include <memory>
+#include <string>
+
+#include "base/containers/flat_map.h"
+#include "base/containers/flat_set.h"
+#include "base/macros.h"
+#include "base/sequence_checker.h"
+#include "components/optimization_guide/optimization_guide_enums.h"
+#include "components/optimization_guide/proto/models.pb.h"
+
+namespace optimization_guide {
+
+// A PredictionModel supported by the optimization guide that makes an
+// OptimizationTargetDecision by evaluating a prediction model.
+class PredictionModel {
+ public:
+  virtual ~PredictionModel();
+
+  // Creates an Prediction model of the correct ModelType
+  // specified in |prediction_model|.
+  static std::unique_ptr<PredictionModel> Create(
+      std::unique_ptr<optimization_guide::proto::PredictionModel>
+          prediction_model,
+      const base::flat_set<std::string>& host_model_features);
+
+  // Returns the OptimizationTargetDecision by evaluating the |model_|
+  // using the provided |model_features|.
+  virtual optimization_guide::OptimizationTargetDecision Predict(
+      const base::flat_map<std::string, float>& model_features) = 0;
+
+  // Provide the version of the |model_| by |this|.
+  int64_t GetVersion() const;
+
+  // Provide the model features required for evaluation of the |model_| by
+  // |this|.
+  base::flat_set<std::string> GetModelFeatures() const;
+
+ protected:
+  PredictionModel(std::unique_ptr<optimization_guide::proto::PredictionModel>
+                      prediction_model,
+                  const base::flat_set<std::string>& host_model_features);
+
+ private:
+  // The in-memory model used for prediction.
+  std::unique_ptr<optimization_guide::proto::Model> model_;
+
+  // The information that describes the |model_|
+  std::unique_ptr<optimization_guide::proto::ModelInfo> model_info_;
+
+  // The set of features required by the |model_| to be evaluated.
+  base::flat_set<std::string> model_features_;
+
+  // The version of the |model_|.
+  int64_t version_;
+
+  SEQUENCE_CHECKER(sequence_checker_);
+
+  DISALLOW_COPY_AND_ASSIGN(PredictionModel);
+};
+
+}  // namespace optimization_guide
+
+#endif  // CHROME_BROWSER_OPTIMIZATION_GUIDE_PREDICTION_PREDICTION_MODEL_H_
diff --git a/chrome/browser/optimization_guide/prediction/prediction_model_unittest.cc b/chrome/browser/optimization_guide/prediction/prediction_model_unittest.cc
new file mode 100644
index 0000000..25bd5550
--- /dev/null
+++ b/chrome/browser/optimization_guide/prediction/prediction_model_unittest.cc
@@ -0,0 +1,143 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/optimization_guide/prediction/prediction_model.h"
+
+#include <memory>
+
+#include "components/optimization_guide/proto/models.pb.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace optimization_guide {
+
+TEST(PredictionModelTest, ValidPredictionModel) {
+  std::unique_ptr<optimization_guide::proto::PredictionModel> prediction_model =
+      std::make_unique<optimization_guide::proto::PredictionModel>();
+
+  optimization_guide::proto::DecisionTree* decision_tree_model =
+      prediction_model->mutable_model()->mutable_decision_tree();
+  decision_tree_model->set_weight(2.0);
+
+  optimization_guide::proto::ModelInfo* model_info =
+      prediction_model->mutable_model_info();
+  model_info->set_version(1);
+  model_info->add_supported_model_types(
+      optimization_guide::proto::ModelType::MODEL_TYPE_DECISION_TREE);
+  model_info->add_supported_model_features(
+      optimization_guide::proto::ClientModelFeature::
+          CLIENT_MODEL_FEATURE_EFFECTIVE_CONNECTION_TYPE);
+
+  std::unique_ptr<PredictionModel> model =
+      PredictionModel::Create(std::move(prediction_model), {"agg1"});
+
+  EXPECT_EQ(1, model->GetVersion());
+  EXPECT_EQ(2u, model->GetModelFeatures().size());
+  EXPECT_TRUE(model->GetModelFeatures().count("agg1"));
+  EXPECT_TRUE(model->GetModelFeatures().count(
+      "CLIENT_MODEL_FEATURE_EFFECTIVE_CONNECTION_TYPE"));
+}
+
+TEST(PredictionModelTest, NoModel) {
+  std::unique_ptr<optimization_guide::proto::PredictionModel> prediction_model =
+      std::make_unique<optimization_guide::proto::PredictionModel>();
+
+  std::unique_ptr<PredictionModel> model =
+      PredictionModel::Create(std::move(prediction_model), {"agg1"});
+  EXPECT_FALSE(model);
+}
+
+TEST(PredictionModelTest, NoModelVersion) {
+  std::unique_ptr<optimization_guide::proto::PredictionModel> prediction_model =
+      std::make_unique<optimization_guide::proto::PredictionModel>();
+
+  optimization_guide::proto::DecisionTree* decision_tree_model =
+      prediction_model->mutable_model()->mutable_decision_tree();
+  decision_tree_model->set_weight(2.0);
+
+  std::unique_ptr<PredictionModel> model =
+      PredictionModel::Create(std::move(prediction_model), {"agg1"});
+  EXPECT_FALSE(model);
+}
+
+TEST(PredictionModelTest, NoModelType) {
+  std::unique_ptr<optimization_guide::proto::PredictionModel> prediction_model =
+      std::make_unique<optimization_guide::proto::PredictionModel>();
+
+  optimization_guide::proto::DecisionTree* decision_tree_model =
+      prediction_model->mutable_model()->mutable_decision_tree();
+  decision_tree_model->set_weight(2.0);
+
+  optimization_guide::proto::ModelInfo* model_info =
+      prediction_model->mutable_model_info();
+  model_info->set_version(1);
+
+  std::unique_ptr<PredictionModel> model =
+      PredictionModel::Create(std::move(prediction_model), {"agg1"});
+  EXPECT_FALSE(model);
+}
+
+TEST(PredictionModelTest, UnknownModelType) {
+  std::unique_ptr<optimization_guide::proto::PredictionModel> prediction_model =
+      std::make_unique<optimization_guide::proto::PredictionModel>();
+
+  optimization_guide::proto::DecisionTree* decision_tree_model =
+      prediction_model->mutable_model()->mutable_decision_tree();
+  decision_tree_model->set_weight(2.0);
+
+  optimization_guide::proto::ModelInfo* model_info =
+      prediction_model->mutable_model_info();
+  model_info->set_version(1);
+  model_info->add_supported_model_types(
+      optimization_guide::proto::ModelType::MODEL_TYPE_UNKNOWN);
+
+  std::unique_ptr<PredictionModel> model =
+      PredictionModel::Create(std::move(prediction_model), {"agg1"});
+  EXPECT_FALSE(model);
+}
+
+TEST(PredictionModelTest, MultipleModelTypes) {
+  std::unique_ptr<optimization_guide::proto::PredictionModel> prediction_model =
+      std::make_unique<optimization_guide::proto::PredictionModel>();
+
+  optimization_guide::proto::DecisionTree* decision_tree_model =
+      prediction_model->mutable_model()->mutable_decision_tree();
+  decision_tree_model->set_weight(2.0);
+
+  optimization_guide::proto::ModelInfo* model_info =
+      prediction_model->mutable_model_info();
+  model_info->set_version(1);
+  model_info->add_supported_model_types(
+      optimization_guide::proto::ModelType::MODEL_TYPE_DECISION_TREE);
+  model_info->add_supported_model_types(
+      optimization_guide::proto::ModelType::MODEL_TYPE_UNKNOWN);
+
+  std::unique_ptr<PredictionModel> model =
+      PredictionModel::Create(std::move(prediction_model), {"agg1"});
+  EXPECT_FALSE(model);
+}
+
+TEST(PredictionModelTest, UnknownModelClientFeature) {
+  std::unique_ptr<optimization_guide::proto::PredictionModel> prediction_model =
+      std::make_unique<optimization_guide::proto::PredictionModel>();
+
+  optimization_guide::proto::DecisionTree* decision_tree_model =
+      prediction_model->mutable_model()->mutable_decision_tree();
+  decision_tree_model->set_weight(2.0);
+
+  optimization_guide::proto::ModelInfo* model_info =
+      prediction_model->mutable_model_info();
+  model_info->set_version(1);
+  model_info->add_supported_model_types(
+      optimization_guide::proto::ModelType::MODEL_TYPE_DECISION_TREE);
+
+  model_info->add_supported_model_features(
+      optimization_guide::proto::ClientModelFeature::
+          CLIENT_MODEL_FEATURE_UNKNOWN);
+
+  std::unique_ptr<PredictionModel> model =
+      PredictionModel::Create(std::move(prediction_model), {"agg1"});
+  EXPECT_FALSE(model);
+}
+
+}  // namespace optimization_guide
diff --git a/chrome/browser/page_load_metrics/page_load_metrics_initialize.cc b/chrome/browser/page_load_metrics/page_load_metrics_initialize.cc
index 4bea51c..9b4354b6 100644
--- a/chrome/browser/page_load_metrics/page_load_metrics_initialize.cc
+++ b/chrome/browser/page_load_metrics/page_load_metrics_initialize.cc
@@ -8,7 +8,6 @@
 #include <utility>
 
 #include "base/macros.h"
-#include "base/timer/timer.h"
 #include "build/build_config.h"
 #include "chrome/browser/page_load_metrics/observers/aborts_page_load_metrics_observer.h"
 #include "chrome/browser/page_load_metrics/observers/ad_metrics/ads_page_load_metrics_observer.h"
@@ -47,7 +46,7 @@
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/search/search.h"
 #include "components/page_load_metrics/browser/metrics_web_contents_observer.h"
-#include "components/page_load_metrics/browser/page_load_metrics_embedder_interface.h"
+#include "components/page_load_metrics/browser/page_load_metrics_embedder_base.h"
 #include "components/page_load_metrics/browser/page_load_tracker.h"
 #include "components/rappor/rappor_service_impl.h"
 #include "content/public/browser/web_contents.h"
@@ -69,33 +68,33 @@
 namespace {
 
 class PageLoadMetricsEmbedder
-    : public page_load_metrics::PageLoadMetricsEmbedderInterface {
+    : public page_load_metrics::PageLoadMetricsEmbedderBase {
  public:
   explicit PageLoadMetricsEmbedder(content::WebContents* web_contents);
   ~PageLoadMetricsEmbedder() override;
 
-  // page_load_metrics::PageLoadMetricsEmbedderInterface:
+  // page_load_metrics::PageLoadMetricsEmbedderBase:
   bool IsNewTabPageUrl(const GURL& url) override;
-  void RegisterObservers(page_load_metrics::PageLoadTracker* tracker) override;
-  std::unique_ptr<base::OneShotTimer> CreateTimer() override;
   bool IsPrerender(content::WebContents* web_contents) override;
   bool IsExtensionUrl(const GURL& url) override;
 
+ protected:
+  // page_load_metrics::PageLoadMetricsEmbedderBase:
+  void RegisterEmbedderObservers(
+      page_load_metrics::PageLoadTracker* tracker) override;
+  bool IsPrerendering() const override;
+
  private:
-  bool IsPrerendering() const;
-
-  content::WebContents* const web_contents_;
-
   DISALLOW_COPY_AND_ASSIGN(PageLoadMetricsEmbedder);
 };
 
 PageLoadMetricsEmbedder::PageLoadMetricsEmbedder(
     content::WebContents* web_contents)
-    : web_contents_(web_contents) {}
+    : PageLoadMetricsEmbedderBase(web_contents) {}
 
-PageLoadMetricsEmbedder::~PageLoadMetricsEmbedder() {}
+PageLoadMetricsEmbedder::~PageLoadMetricsEmbedder() = default;
 
-void PageLoadMetricsEmbedder::RegisterObservers(
+void PageLoadMetricsEmbedder::RegisterEmbedderObservers(
     page_load_metrics::PageLoadTracker* tracker) {
   if (!IsPrerendering()) {
     tracker->AddObserver(std::make_unique<AbortsPageLoadMetricsObserver>());
@@ -131,7 +130,7 @@
         std::make_unique<SignedExchangePageLoadMetricsObserver>());
     tracker->AddObserver(
         std::make_unique<HttpsEngagementPageLoadMetricsObserver>(
-            web_contents_->GetBrowserContext()));
+            web_contents()->GetBrowserContext()));
     tracker->AddObserver(std::make_unique<ProtocolPageLoadMetricsObserver>());
     tracker->AddObserver(std::make_unique<TabRestorePageLoadMetricsObserver>());
     tracker->AddObserver(std::make_unique<UseCounterPageLoadMetricsObserver>());
@@ -152,7 +151,7 @@
     std::unique_ptr<page_load_metrics::PageLoadMetricsObserver>
         no_state_prefetch_observer =
             NoStatePrefetchPageLoadMetricsObserver::CreateIfNeeded(
-                web_contents_);
+                web_contents());
     if (no_state_prefetch_observer)
       tracker->AddObserver(std::move(no_state_prefetch_observer));
 #if defined(OS_ANDROID)
@@ -161,7 +160,7 @@
     std::unique_ptr<page_load_metrics::PageLoadMetricsObserver>
         loading_predictor_observer =
             LoadingPredictorPageLoadMetricsObserver::CreateIfNeeded(
-                web_contents_);
+                web_contents());
     if (loading_predictor_observer)
       tracker->AddObserver(std::move(loading_predictor_observer));
 #if !defined(OS_ANDROID)
@@ -175,22 +174,18 @@
       std::make_unique<OmniboxSuggestionUsedMetricsObserver>(IsPrerendering()));
   tracker->AddObserver(
       SecurityStatePageLoadMetricsObserver::MaybeCreateForProfile(
-          web_contents_->GetBrowserContext()));
+          web_contents()->GetBrowserContext()));
   tracker->AddObserver(std::make_unique<DataUseMetricsObserver>());
 }
 
 bool PageLoadMetricsEmbedder::IsPrerendering() const {
-  return prerender::PrerenderContents::FromWebContents(web_contents_) !=
+  return prerender::PrerenderContents::FromWebContents(web_contents()) !=
          nullptr;
 }
 
-std::unique_ptr<base::OneShotTimer> PageLoadMetricsEmbedder::CreateTimer() {
-  return std::make_unique<base::OneShotTimer>();
-}
-
 bool PageLoadMetricsEmbedder::IsNewTabPageUrl(const GURL& url) {
   Profile* profile =
-      Profile::FromBrowserContext(web_contents_->GetBrowserContext());
+      Profile::FromBrowserContext(web_contents()->GetBrowserContext());
   if (!profile)
     return false;
   return search::IsInstantNTPURL(url, profile);
diff --git a/chrome/browser/page_load_metrics/page_load_metrics_initialize.h b/chrome/browser/page_load_metrics/page_load_metrics_initialize.h
index 52f446b..27f0c17 100644
--- a/chrome/browser/page_load_metrics/page_load_metrics_initialize.h
+++ b/chrome/browser/page_load_metrics/page_load_metrics_initialize.h
@@ -5,8 +5,6 @@
 #ifndef CHROME_BROWSER_PAGE_LOAD_METRICS_PAGE_LOAD_METRICS_INITIALIZE_H_
 #define CHROME_BROWSER_PAGE_LOAD_METRICS_PAGE_LOAD_METRICS_INITIALIZE_H_
 
-#include "components/page_load_metrics/browser/metrics_web_contents_observer.h"
-
 namespace content {
 class WebContents;
 }
diff --git a/chrome/browser/platform_util_unittest.cc b/chrome/browser/platform_util_unittest.cc
index ff182eb..f939760 100644
--- a/chrome/browser/platform_util_unittest.cc
+++ b/chrome/browser/platform_util_unittest.cc
@@ -212,13 +212,7 @@
   EXPECT_EQ(OPEN_FAILED_PATH_NOT_FOUND, CallOpenItem(nowhere_, OPEN_FILE));
 }
 
-// TODO(crbug.com/1004639) Disabled on Linux due to crashes and timeouts.
-#if defined(OS_LINUX)
-#define MAYBE_OpenFolder DISABLED_OpenFolder
-#else
-#define MAYBE_OpenFolder OpenFolder
-#endif
-TEST_F(PlatformUtilTest, MAYBE_OpenFolder) {
+TEST_F(PlatformUtilTest, OpenFolder) {
   EXPECT_EQ(OPEN_SUCCEEDED, CallOpenItem(existing_folder_, OPEN_FOLDER));
   EXPECT_EQ(OPEN_FAILED_INVALID_TYPE,
             CallOpenItem(existing_file_, OPEN_FOLDER));
diff --git a/chrome/browser/prefs/chrome_command_line_pref_store.cc b/chrome/browser/prefs/chrome_command_line_pref_store.cc
index 17550a01..60cd72c 100644
--- a/chrome/browser/prefs/chrome_command_line_pref_store.cc
+++ b/chrome/browser/prefs/chrome_command_line_pref_store.cc
@@ -67,7 +67,6 @@
          true},
         {switches::kAllowOutdatedPlugins, prefs::kPluginsAllowOutdated, true},
         {switches::kNoPings, prefs::kEnableHyperlinkAuditing, false},
-        {network::switches::kNoReferrers, prefs::kEnableReferrers, false},
         {switches::kAllowRunningInsecureContent,
          prefs::kWebKitAllowRunningInsecureContent, true},
         {switches::kAllowCrossOriginAuthPrompt,
diff --git a/chrome/browser/profiles/host_zoom_map_browsertest.cc b/chrome/browser/profiles/host_zoom_map_browsertest.cc
index f7ff230..796e5a8 100644
--- a/chrome/browser/profiles/host_zoom_map_browsertest.cc
+++ b/chrome/browser/profiles/host_zoom_map_browsertest.cc
@@ -42,6 +42,7 @@
 #include "net/test/embedded_test_server/embedded_test_server.h"
 #include "net/test/embedded_test_server/http_response.h"
 #include "testing/gmock/include/gmock/gmock.h"
+#include "third_party/blink/public/common/page/page_zoom.h"
 #include "url/gurl.h"
 
 using content::HostZoomMap;
@@ -270,7 +271,7 @@
 
 // Regression test for crbug.com/364399.
 IN_PROC_BROWSER_TEST_F(HostZoomMapBrowserTest, ToggleDefaultZoomLevel) {
-  const double default_zoom_level = content::ZoomFactorToZoomLevel(1.5);
+  const double default_zoom_level = blink::PageZoomFactorToZoomLevel(1.5);
 
   const char kTestURLTemplate1[] = "http://host1:%u/";
   const char kTestURLTemplate2[] = "http://host2:%u/";
@@ -283,26 +284,26 @@
   SetDefaultZoomLevel(default_zoom_level);
   observer.BlockUntilZoomLevelForHostHasChanged(test_url1.host());
   EXPECT_TRUE(
-      content::ZoomValuesEqual(default_zoom_level, GetZoomLevel(test_url1)));
+      blink::PageZoomValuesEqual(default_zoom_level, GetZoomLevel(test_url1)));
 
   GURL test_url2 = ConstructTestServerURL(kTestURLTemplate2);
   ui_test_utils::NavigateToURLWithDisposition(
       browser(), test_url2, WindowOpenDisposition::NEW_FOREGROUND_TAB,
       ui_test_utils::BROWSER_TEST_WAIT_FOR_NAVIGATION);
   EXPECT_TRUE(
-      content::ZoomValuesEqual(default_zoom_level, GetZoomLevel(test_url2)));
+      blink::PageZoomValuesEqual(default_zoom_level, GetZoomLevel(test_url2)));
 
   content::WebContents* web_contents =
       browser()->tab_strip_model()->GetActiveWebContents();
   zoom::PageZoom::Zoom(web_contents, content::PAGE_ZOOM_OUT);
   observer.BlockUntilZoomLevelForHostHasChanged(test_url2.host());
   EXPECT_FALSE(
-      content::ZoomValuesEqual(default_zoom_level, GetZoomLevel(test_url2)));
+      blink::PageZoomValuesEqual(default_zoom_level, GetZoomLevel(test_url2)));
 
   zoom::PageZoom::Zoom(web_contents, content::PAGE_ZOOM_IN);
   observer.BlockUntilZoomLevelForHostHasChanged(test_url2.host());
   EXPECT_TRUE(
-      content::ZoomValuesEqual(default_zoom_level, GetZoomLevel(test_url2)));
+      blink::PageZoomValuesEqual(default_zoom_level, GetZoomLevel(test_url2)));
 
   // Now both tabs should be at the default zoom level, so there should not be
   // any per-host values saved either to Pref, or internally in HostZoomMap.
diff --git a/chrome/browser/resources/accessibility/accessibility.js b/chrome/browser/resources/accessibility/accessibility.js
index 277d6c8..3dab40754 100644
--- a/chrome/browser/resources/accessibility/accessibility.js
+++ b/chrome/browser/resources/accessibility/accessibility.js
@@ -72,7 +72,7 @@
     const deny = improvementsEnabled ? $('filter-deny').value : '';
 
     // The calling |element| is a button with an id of the format
-    // <treeId>:<requestType>, where requestType is one of 'showTree',
+    // <treeId>:<requestType>, where requestType is one of 'showOrRefreshTree',
     // 'copyTree'. Send the request type to C++ so is calls the corresponding
     // function with the result.
     const requestType = element.id.split(':')[1];
@@ -80,8 +80,12 @@
       const delay = $('native_ui_delay').value;
       setTimeout(() => {
         chrome.send(
-            'requestNativeUITree',
-            [{'sessionId': data.sessionId, 'requestType': requestType}]);
+            'requestNativeUITree', [{
+              'sessionId': data.sessionId,
+              'requestType': requestType,
+              'filters':
+                  {'allow': allow, 'allowEmpty': allowEmpty, 'deny': deny}
+            }]);
       }, delay);
     } else {
       chrome.send(
@@ -301,7 +305,7 @@
     } else {
       show.textContent = 'Show accessibility tree';
     }
-    show.id = id + ':showTree';
+    show.id = id + ':showOrRefreshTree';
     show.setAttribute('aria-expanded', String(opt_refresh));
     show.addEventListener('click', requestTree.bind(this, data, show));
     return show;
@@ -312,7 +316,7 @@
     hide.textContent = 'Hide accessibility tree';
     hide.id = id + ':hideTree';
     hide.addEventListener('click', function() {
-      const show = $(id + ':showTree');
+      const show = $(id + ':showOrRefreshTree');
       show.textContent = 'Show accessibility tree';
       show.setAttribute('aria-expanded', 'false');
       show.focus();
@@ -354,7 +358,7 @@
   }
 
   // Called from C++
-  function showTree(data) {
+  function showOrRefreshTree(data) {
     const id = getIdFromData(data);
     const row = $(id);
     if (!row) {
@@ -363,7 +367,7 @@
 
     row.textContent = '';
     formatRow(row, data);
-    $(id + ':hideTree').focus();
+    $(id + ':showOrRefreshTree').focus();
   }
 
   // Called from C++
@@ -390,11 +394,11 @@
       console.error('Unable to copy accessibility tree.', data.error);
     }
 
-
     const tree = $(id + ':tree');
     // If the tree is currently shown, update it since it may have changed.
     if (tree && tree.style.display != 'none') {
-      showTree(data);
+      showOrRefreshTree(data);
+      $(id + ':copyTree').focus();
     }
   }
 
@@ -420,7 +424,11 @@
   }
 
   // These are the functions we export so they can be called from C++.
-  return {copyTree: copyTree, initialize: initialize, showTree: showTree};
+  return {
+    copyTree: copyTree,
+    initialize: initialize,
+    showOrRefreshTree: showOrRefreshTree
+  };
 });
 
 document.addEventListener('DOMContentLoaded', accessibility.initialize);
diff --git a/chrome/browser/resources/chromeos/chromevox/chromevox/background/keymaps/next_keymap.json b/chrome/browser/resources/chromeos/chromevox/chromevox/background/keymaps/next_keymap.json
index 972d1fb..33edb1598 100644
--- a/chrome/browser/resources/chromeos/chromevox/chromevox/background/keymaps/next_keymap.json
+++ b/chrome/browser/resources/chromeos/chromevox/chromevox/background/keymaps/next_keymap.json
@@ -998,7 +998,7 @@
       }
     },
     {
-      "command": "getBatteryDescription",
+      "command": "announceBatteryDescription",
       "sequence": {
         "cvoxModifier": true,
         "keys": {
@@ -1007,7 +1007,7 @@
       }
     },
     {
-      "command": "getRichTextDescription",
+      "command": "announceRichTextDescription",
       "sequence": {
         "cvoxModifier": true,
         "keys": {
diff --git a/chrome/browser/resources/chromeos/chromevox/common/command_store.js b/chrome/browser/resources/chromeos/chromevox/common/command_store.js
index 957cbb8..9f8e99c 100644
--- a/chrome/browser/resources/chromeos/chromevox/common/command_store.js
+++ b/chrome/browser/resources/chromeos/chromevox/common/command_store.js
@@ -816,6 +816,16 @@
   'pauseAllMedia':
       {announce: false, msgId: 'pause_all_media', category: 'information'},
 
+  'announceBatteryDescription': {
+    announce: true,
+    msgId: 'announce_battery_description',
+    category: 'information'
+  },
+  'announceRichTextDescription': {
+    announce: true,
+    msgId: 'announce_rich_text_description',
+    category: 'information'
+  },
   'readPhoneticPronunciation': {
     announce: true,
     msgId: 'read_phonetic_pronunciation',
diff --git a/chrome/browser/resources/chromeos/chromevox/cvox2/background/command_handler.js b/chrome/browser/resources/chromeos/chromevox/cvox2/background/command_handler.js
index 5a5c944..c0dd8da7 100644
--- a/chrome/browser/resources/chromeos/chromevox/cvox2/background/command_handler.js
+++ b/chrome/browser/resources/chromeos/chromevox/cvox2/background/command_handler.js
@@ -866,7 +866,7 @@
       }
       cvox.ChromeVox.tts.speak(announce, cvox.QueueMode.FLUSH);
       return false;
-    case 'getBatteryDescription':
+    case 'announceBatteryDescription':
       chrome.accessibilityPrivate.getBatteryDescription(function(
           batteryDescription) {
         new Output()
@@ -875,7 +875,7 @@
             .go();
       });
       break;
-    case 'getRichTextDescription':
+    case 'announceRichTextDescription':
       var node = ChromeVoxState.instance.currentRange.start.node;
       var optSubs = [];
       node.fontSize ? optSubs.push('font size: ' + node.fontSize) :
diff --git a/chrome/browser/resources/chromeos/chromevox/cvox2/background/output.js b/chrome/browser/resources/chromeos/chromevox/cvox2/background/output.js
index ac14615..c42033a 100644
--- a/chrome/browser/resources/chromeos/chromevox/cvox2/background/output.js
+++ b/chrome/browser/resources/chromeos/chromevox/cvox2/background/output.js
@@ -138,6 +138,16 @@
   alert: {msgId: 'role_alert'},
   alertDialog: {msgId: 'role_alertdialog', outputContextFirst: true},
   article: {msgId: 'role_article', inherits: 'abstractItem'},
+  annotationAttribution:
+      {msgId: 'role_annotation_attribution', inherits: 'abstractContainer'},
+  annotationCommentary:
+      {msgId: 'role_annotation_commentary', inherits: 'abstractContainer'},
+  annotationPresence:
+      {msgId: 'role_annotation_presence', inherits: 'abstractContainer'},
+  annotationRevision:
+      {msgId: 'role_annotation_revision', inherits: 'abstractContainer'},
+  annotationSuggestion:
+      {msgId: 'role_annotation_suggestion', inherits: 'abstractContainer'},
   application: {msgId: 'role_application', inherits: 'abstractContainer'},
   banner: {msgId: 'role_banner', inherits: 'abstractContainer'},
   button: {msgId: 'role_button', earconId: 'BUTTON'},
diff --git a/chrome/browser/resources/chromeos/chromevox/cvox2/background/output_test.extjs b/chrome/browser/resources/chromeos/chromevox/cvox2/background/output_test.extjs
index fdc2b91..4239337 100644
--- a/chrome/browser/resources/chromeos/chromevox/cvox2/background/output_test.extjs
+++ b/chrome/browser/resources/chromeos/chromevox/cvox2/background/output_test.extjs
@@ -525,6 +525,11 @@
 
 SYNC_TEST_F('ChromeVoxOutputE2ETest', 'MessageIdAndEarconValidity', function() {
   const kNoBrailleMessageRequired = new Set([
+    'annotationAttribution',
+    'annotationCommentary',
+    'annotationPresence',
+    'annotationRevision',
+    'annotationSuggestion',
     'contentDeletion',
     'contentInsertion',
     'docAbstract',
diff --git a/chrome/browser/resources/chromeos/chromevox/strings/chromevox_strings.grd b/chrome/browser/resources/chromeos/chromevox/strings/chromevox_strings.grd
index 8665039..c1c43ae 100644
--- a/chrome/browser/resources/chromeos/chromevox/strings/chromevox_strings.grd
+++ b/chrome/browser/resources/chromeos/chromevox/strings/chromevox_strings.grd
@@ -1072,6 +1072,21 @@
       <message desc="This is an abbreviated ARIA widget role name shown on a braille display. When translating, try to find a contracted form of the translation for 'alert dialog' according to local conventions. If reasonable, use all lowercase and avoid punctuation to keep the number of characters as low as possible." name="IDS_CHROMEVOX_ROLE_ALERTDIALOG_BRL">
         alrt dlg
       </message>
+      <message  desc="Accessibility role description for attribution, meaning authoring information tied to specific content" name="IDS_CHROMEVOX_ROLE_ANNOTATION_ATTRIBUTION">
+        Authoring info
+      </message>
+      <message desc="Accessibility role description for commentary" name="IDS_CHROMEVOX_ROLE_ANNOTATION_COMMENTARY">
+        Comments
+      </message>
+      <message desc="Accessibility role description for presence, meaning information about another collaborator who is currently reviewing or editing this content" name="IDS_CHROMEVOX_ROLE_ANNOTATION_PRESENCE">
+        Live presence
+      </message>
+      <message desc="Accessibility role description for revision, meaining historical change info tied to this content" name="IDS_CHROMEVOX_ROLE_ANNOTATION_REVISION">
+        Revision
+      </message>
+      <message desc="Accessibility role description for suggestion, meaning a suggested change to some content" name="IDS_CHROMEVOX_ROLE_ANNOTATION_SUGGESTION" >
+        Suggestion
+      </message>
       <message desc="Describes an element with the ARIA role button." name="IDS_CHROMEVOX_ROLE_BUTTON">
         Button
       </message>
@@ -3715,6 +3730,12 @@
       <message desc="Spoken to inform the user that the node's name is empty" name="IDS_CHROMEVOX_EMPTY_NAME">
         No available text for this item
       </message>
+      <message desc="The description of the announceBatteryDescription key. Displayed in the ChromeVox menu." name="IDS_CHROMEVOX_ANNOUNCE_BATTERY_DESCRIPTION">
+        Announce current battery status
+      </message>
+      <message desc="The description of the announceRichTextDescription key. Displayed in the ChromeVox menu." name="IDS_CHROMEVOX_ANNOUNCE_RICH_TEXT_DESCRIPTION">
+        Announce formatting for current item
+      </message>
     </messages>
   </release>
 </grit>
diff --git a/chrome/browser/resources/chromeos/emulator/device_emulator_pages.html b/chrome/browser/resources/chromeos/emulator/device_emulator_pages.html
index 9b0123d..07b7ec0 100644
--- a/chrome/browser/resources/chromeos/emulator/device_emulator_pages.html
+++ b/chrome/browser/resources/chromeos/emulator/device_emulator_pages.html
@@ -30,7 +30,7 @@
       }
 
       iron-pages > * {
-        @apply(--shadow-elevation-2dp);
+        @apply --shadow-elevation-2dp;
         background-color: white;
         display: block;
         margin: 20px;
diff --git a/chrome/browser/resources/chromeos/internet_detail_dialog/internet_detail_dialog.html b/chrome/browser/resources/chromeos/internet_detail_dialog/internet_detail_dialog.html
index ce2ccb2..1262e855 100644
--- a/chrome/browser/resources/chromeos/internet_detail_dialog/internet_detail_dialog.html
+++ b/chrome/browser/resources/chromeos/internet_detail_dialog/internet_detail_dialog.html
@@ -45,7 +45,7 @@
       }
 
       .section {
-        @apply(--cr-section);
+        @apply --cr-section;
         margin-bottom: 10px;
       }
 
diff --git a/chrome/browser/resources/chromeos/login/demo_preferences.css b/chrome/browser/resources/chromeos/login/demo_preferences.css
index 1148cf1..a48ff77 100644
--- a/chrome/browser/resources/chromeos/login/demo_preferences.css
+++ b/chrome/browser/resources/chromeos/login/demo_preferences.css
@@ -2,7 +2,7 @@
  * Use of this source code is governed by a BSD-style license that can be
  * found in the LICENSE file. */
 
-:root {
+:host {
   --oobe-dialog-list-item-border: 1px solid rgba(0, 0, 0, 0.06);
 }
 
diff --git a/chrome/browser/resources/chromeos/login/marketing_opt_in.css b/chrome/browser/resources/chromeos/login/marketing_opt_in.css
index 35a0331..05f3732 100644
--- a/chrome/browser/resources/chromeos/login/marketing_opt_in.css
+++ b/chrome/browser/resources/chromeos/login/marketing_opt_in.css
@@ -2,7 +2,7 @@
  * Use of this source code is governed by a BSD-style license that can be
  * found in the LICENSE file. */
 
-:root {
+:host {
   --marketing-opt-in-dialog-list-item-border: 1px solid var(--google-grey-200);
 }
 
diff --git a/chrome/browser/resources/chromeos/login/md_screen_container.html b/chrome/browser/resources/chromeos/login/md_screen_container.html
index efe2f3c7..dce7555 100644
--- a/chrome/browser/resources/chromeos/login/md_screen_container.html
+++ b/chrome/browser/resources/chromeos/login/md_screen_container.html
@@ -1,7 +1,7 @@
 <div id="background" class="background-initial"></div>
 <include src="api_keys_notice.html">
 <div id="scroll-container">
-  <div id="outer-container" class="down">
+  <div id="outer-container">
     <div id="oobe" class="faded">
       <div id="inner-container" class="down">
         <include src="[OOBE]_screens.html">
diff --git a/chrome/browser/resources/chromeos/login/oobe_fonts.css b/chrome/browser/resources/chromeos/login/oobe_fonts.css
index c4c7ce9..1f815b7 100644
--- a/chrome/browser/resources/chromeos/login/oobe_fonts.css
+++ b/chrome/browser/resources/chromeos/login/oobe_fonts.css
@@ -2,7 +2,7 @@
  * Use of this source code is governed by a BSD-style license that can be
  * found in the LICENSE file. */
 
-:root {
+:host {
   --oobe-button-font-family: "Google Sans", Roboto, sans-serif;
   --oobe-default-font-family: Roboto, sans-serif;
   --oobe-title-font-family: "Google Sans", Roboto, sans-serif;
diff --git a/chrome/browser/resources/chromeos/login/oobe_hid_detection.css b/chrome/browser/resources/chromeos/login/oobe_hid_detection.css
index 183b460b..896c60a 100644
--- a/chrome/browser/resources/chromeos/login/oobe_hid_detection.css
+++ b/chrome/browser/resources/chromeos/login/oobe_hid_detection.css
@@ -2,7 +2,7 @@
  * Use of this source code is governed by a BSD-style license that can be
  * found in the LICENSE file. */
 
-:root {
+:host {
   --oobe-hid-detection-item-border: 1px solid var(--google-grey-200);
 }
 
diff --git a/chrome/browser/resources/chromeos/login/oobe_screen.css b/chrome/browser/resources/chromeos/login/oobe_screen.css
index 835911d..09ae3fd 100644
--- a/chrome/browser/resources/chromeos/login/oobe_screen.css
+++ b/chrome/browser/resources/chromeos/login/oobe_screen.css
@@ -4,7 +4,7 @@
  *
  * This contains common styling for all the OOBE screens. */
 
-:root {
+:host {
   --oobe-text-button-focused-border-color:
       rgba(66, 133, 244, 0.4); /* #4284f4 */
 }
@@ -53,16 +53,11 @@
   height: 28px;
   justify-content: flex-end;
   min-height: 0;
-  padding-right: 34px;  /* Double the padding of .step */
+  padding-inline-end: 34px;  /* Double the padding of .step */
   position: absolute;
   width: 100%;
 }
 
-html[dir=rtl] .step-controls {
-  /* Hack as padding-inline-end doesn't catch direction. crbug.com/363836 */
-  padding-left: 34px;
-}
-
 .animation .step-controls button {
   /* Don't grey out disabled buttons during animation. */
   color: buttontext !important;
diff --git a/chrome/browser/resources/chromeos/login/oobe_welcome.css b/chrome/browser/resources/chromeos/login/oobe_welcome.css
index d47f9254..30665a4b 100644
--- a/chrome/browser/resources/chromeos/login/oobe_welcome.css
+++ b/chrome/browser/resources/chromeos/login/oobe_welcome.css
@@ -2,7 +2,7 @@
  * Use of this source code is governed by a BSD-style license that can be
  * found in the LICENSE file. */
 
-:root {
+:host {
   --oobe-dialog-list-item-border: 1px solid rgb(235,235,235); /* #EBEBEB */
   --oobe-a11y-dialog-list-item-border: 1px solid var(--google-grey-200);
 }
diff --git a/chrome/browser/resources/chromeos/login/oobe_welcome.js b/chrome/browser/resources/chromeos/login/oobe_welcome.js
index e32eb739..6b10ef0 100644
--- a/chrome/browser/resources/chromeos/login/oobe_welcome.js
+++ b/chrome/browser/resources/chromeos/login/oobe_welcome.js
@@ -92,7 +92,6 @@
   ready: function() {
     this.initializeLoginScreen('WelcomeScreen', {
       resetAllowed: true,
-      commonScreenSize: true,
       enableDebuggingAllowed: true,
       enterDemoModeAllowed: true,
       noAnimatedTransition: true,
diff --git a/chrome/browser/resources/local_ntp/local_ntp.js b/chrome/browser/resources/local_ntp/local_ntp.js
index d720532..7b382c3 100644
--- a/chrome/browser/resources/local_ntp/local_ntp.js
+++ b/chrome/browser/resources/local_ntp/local_ntp.js
@@ -90,7 +90,6 @@
   LEFT_ALIGN_ATTRIBUTION: 'left-align-attribution',
   // Vertically centers the most visited section for a non-Google provided page.
   NON_GOOGLE_PAGE: 'non-google-page',
-  NON_WHITE_BG: 'non-white-bg',
   SEARCH_ICON: 'search-icon',  // Magnifying glass/search icon.
   SELECTED: 'selected',  // A selected (via up/down arrow key) realbox match.
   SHOW_ELEMENT: 'show-element',
@@ -107,14 +106,6 @@
 ];
 
 /**
- * Background color for Chrome dark mode. Used to determine if it is possible to
- * display a Google Doodle, or if the notifier should be used instead.
- * @type {string}
- * @const
- */
-const DARK_MODE_BACKGROUND_COLOR = 'rgba(53,54,58,1)';
-
-/**
  * The period of time (ms) before transitions can be applied to a toast
  * notification after modifying the "display" property.
  * @type {number}
@@ -244,14 +235,6 @@
   'PageUp',
 ];
 
-/**
- * Background colors considered "white". Used to determine if it is possible to
- * display a Google Doodle, or if the notifier should be used instead. Also used
- * to determine if a colored or white logo should be used.
- * @const
- */
-const WHITE_BACKGROUND_COLORS = ['rgba(255,255,255,1)', 'rgba(0,0,0,0)'];
-
 // Local statics.
 
 /** @type {!Array<!AutocompleteMatch>} */
@@ -1486,23 +1469,10 @@
   isDarkModeEnabled = window.matchMedia('(prefers-color-scheme: dark)').matches;
   document.body.classList.toggle('light-chip', !getUseDarkChips(info));
 
-  const background = [
-    convertToRGBAColor(info.backgroundColorRgba), info.imageUrl,
-    info.imageTiling, info.imageHorizontalAlignment, info.imageVerticalAlignment
-  ].join(' ').trim();
-
-  // If a custom background has been selected the image will be applied to the
-  // custom-background element instead of the body.
-  if (!info.customBackgroundConfigured) {
-    document.body.style.background = background;
-  }
-
   // Dark mode uses a white Google logo.
   const useWhiteLogo =
       info.alternateLogo || (info.usingDefaultTheme && isDarkModeEnabled);
   document.body.classList.toggle(CLASSES.ALTERNATE_LOGO, useWhiteLogo);
-  const isNonWhiteBackground = !WHITE_BACKGROUND_COLORS.includes(background);
-  document.body.classList.toggle(CLASSES.NON_WHITE_BG, isNonWhiteBackground);
 
   if (info.logoColor) {
     document.body.style.setProperty(
@@ -1512,14 +1482,22 @@
   // The doodle notifier should be shown for non-default backgrounds. This
   // includes non-white backgrounds, excluding dark mode gray if dark mode is
   // enabled.
-  const isDefaultBackground = WHITE_BACKGROUND_COLORS.includes(background) ||
-      (isDarkModeEnabled && background === DARK_MODE_BACKGROUND_COLOR);
+  const isDefaultBackground = info.usingDefaultTheme && !info.imageUrl;
   document.body.classList.toggle(CLASSES.USE_NOTIFIER, !isDefaultBackground);
 
-  updateThemeAttribution(info.attributionUrl, info.imageHorizontalAlignment);
-  setCustomThemeStyle(info);
+  // If a custom background has been selected the image will be applied to the
+  // custom-background element instead of the body.
+  if (!info.customBackgroundConfigured) {
+    document.body.style.background = [
+      convertToRGBAColor(info.backgroundColorRgba), info.imageUrl,
+      info.imageTiling, info.imageHorizontalAlignment,
+      info.imageVerticalAlignment
+    ].join(' ').trim();
 
-  if (info.customBackgroundConfigured) {
+    $(IDS.CUSTOM_BG).style.opacity = '0';
+    $(IDS.CUSTOM_BG).style.backgroundImage = '';
+    customize.clearAttribution();
+  } else {
     // Do anything only if the custom background changed.
     const imageUrl = assert(info.imageUrl);
     if (!$(IDS.CUSTOM_BG).style.backgroundImage.includes(imageUrl)) {
@@ -1549,12 +1527,11 @@
           '' + info.attribution1, '' + info.attribution2,
           '' + info.attributionActionUrl);
     }
-  } else {
-    $(IDS.CUSTOM_BG).style.opacity = '0';
-    $(IDS.CUSTOM_BG).style.backgroundImage = '';
-    customize.clearAttribution();
   }
 
+  updateThemeAttribution(info.attributionUrl, info.imageHorizontalAlignment);
+  setCustomThemeStyle(info);
+
   $(customize.IDS.RESTORE_DEFAULT)
       .classList.toggle(
           customize.CLASSES.OPTION_DISABLED, !info.customBackgroundConfigured);
diff --git a/chrome/browser/resources/pdf/elements/viewer-bookmark.js b/chrome/browser/resources/pdf/elements/viewer-bookmark.js
index f1cce48..5be034e 100644
--- a/chrome/browser/resources/pdf/elements/viewer-bookmark.js
+++ b/chrome/browser/resources/pdf/elements/viewer-bookmark.js
@@ -7,8 +7,9 @@
  *
  * The bookmark may point at a location in the PDF or a URI.
  * If it points at a location, |page| indicates which 0-based page it leads to.
- * Optionally, |y| is the y position in that page, in pixel coordinates.
- * If it points at an URI, |uri| is the target for that bookmark.
+ * Optionally, |x| is the x position in that page, |y| is the y position in that
+ * page, in pixel coordinates. If it points at an URI, |uri| is the target for
+ * that bookmark.
  *
  * |children| is an array of the |Bookmark|s that are below this in a table of
  * contents tree
@@ -16,6 +17,7 @@
  * @typedef {{
  *   title: string,
  *   page: (number | undefined),
+ *   x: (number | undefined),
  *   y: (number | undefined),
  *   uri: (string | undefined),
  *   children: !Array<!Bookmark>
@@ -81,10 +83,11 @@
   /** @private */
   onClick_: function() {
     if (this.bookmark.hasOwnProperty('page')) {
-      if (this.bookmark.hasOwnProperty('y')) {
+      if (this.bookmark.hasOwnProperty('x') &&
+          this.bookmark.hasOwnProperty('y')) {
         this.fire('change-page-and-xy', {
           page: this.bookmark.page,
-          x: 0,
+          x: this.bookmark.x,
           y: this.bookmark.y,
           origin: 'bookmark'
         });
diff --git a/chrome/browser/resources/settings/appearance_page/appearance_page.js b/chrome/browser/resources/settings/appearance_page/appearance_page.js
index 2551cf3..582ae81 100644
--- a/chrome/browser/resources/settings/appearance_page/appearance_page.js
+++ b/chrome/browser/resources/settings/appearance_page/appearance_page.js
@@ -339,7 +339,7 @@
   },
 
   /**
-   * @see content::ZoomValuesEqual().
+   * @see blink::PageZoomValuesEqual().
    * @param {number} zoom1
    * @param {number} zoom2
    * @return {boolean}
diff --git a/chrome/browser/ssl/BUILD.gn b/chrome/browser/ssl/BUILD.gn
index f5175c8..b731b9f 100644
--- a/chrome/browser/ssl/BUILD.gn
+++ b/chrome/browser/ssl/BUILD.gn
@@ -8,5 +8,6 @@
   sources = [
     "cert_logger.proto",
     "ssl_error_assistant.proto",
+    "tls_deprecation_config.proto",
   ]
 }
diff --git a/chrome/browser/ssl/tls_deprecation_config.cc b/chrome/browser/ssl/tls_deprecation_config.cc
new file mode 100644
index 0000000..6ece8afc
--- /dev/null
+++ b/chrome/browser/ssl/tls_deprecation_config.cc
@@ -0,0 +1,63 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/ssl/tls_deprecation_config.h"
+
+#include <algorithm>
+#include <string>
+#include <utility>
+
+#include "base/no_destructor.h"
+#include "base/strings/string_piece.h"
+#include "chrome/browser/ssl/tls_deprecation_config.pb.h"
+#include "crypto/sha2.h"
+#include "url/gurl.h"
+
+namespace {
+
+class TLSDeprecationConfigSingleton {
+ public:
+  void SetProto(
+      std::unique_ptr<chrome_browser_ssl::LegacyTLSExperimentConfig> proto) {
+    proto_ = std::move(proto);
+  }
+
+  chrome_browser_ssl::LegacyTLSExperimentConfig* GetProto() const {
+    return proto_.get();
+  }
+
+  static TLSDeprecationConfigSingleton& GetInstance() {
+    static base::NoDestructor<TLSDeprecationConfigSingleton> instance;
+    return *instance;
+  }
+
+ private:
+  std::unique_ptr<chrome_browser_ssl::LegacyTLSExperimentConfig> proto_;
+};
+
+}  // namespace
+
+void SetRemoteTLSDeprecationConfigProto(
+    std::unique_ptr<chrome_browser_ssl::LegacyTLSExperimentConfig> proto) {
+  TLSDeprecationConfigSingleton::GetInstance().SetProto(std::move(proto));
+}
+
+bool IsTLSDeprecationConfigControlSite(const GURL& url) {
+  if (!url.has_host() || !url.SchemeIsCryptographic())
+    return false;
+
+  auto* proto = TLSDeprecationConfigSingleton::GetInstance().GetProto();
+  if (!proto)
+    return false;
+
+  std::string host_hash = crypto::SHA256HashString(url.host_piece());
+  const auto& control_site_hashes = proto->control_site_hashes();
+
+  // Perform binary search on the sorted list of control site hashes to check
+  // if the input URL's hostname is included.
+  auto lower = std::lower_bound(control_site_hashes.begin(),
+                                control_site_hashes.end(), host_hash);
+
+  return lower != control_site_hashes.end() && *lower == host_hash;
+}
diff --git a/chrome/browser/ssl/tls_deprecation_config.h b/chrome/browser/ssl/tls_deprecation_config.h
new file mode 100644
index 0000000..33e6311
--- /dev/null
+++ b/chrome/browser/ssl/tls_deprecation_config.h
@@ -0,0 +1,21 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_SSL_TLS_DEPRECATION_CONFIG_H_
+#define CHROME_BROWSER_SSL_TLS_DEPRECATION_CONFIG_H_
+
+#include <memory>
+
+class GURL;
+
+namespace chrome_browser_ssl {
+class LegacyTLSExperimentConfig;
+}  // namespace chrome_browser_ssl
+
+void SetRemoteTLSDeprecationConfigProto(
+    std::unique_ptr<chrome_browser_ssl::LegacyTLSExperimentConfig> proto);
+
+bool IsTLSDeprecationConfigControlSite(const GURL& url);
+
+#endif  // CHROME_BROWSER_SSL_TLS_DEPRECATION_CONFIG_H_
diff --git a/chrome/browser/ssl/tls_deprecation_config.proto b/chrome/browser/ssl/tls_deprecation_config.proto
new file mode 100644
index 0000000..a54d5d3
--- /dev/null
+++ b/chrome/browser/ssl/tls_deprecation_config.proto
@@ -0,0 +1,19 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+syntax = "proto2";
+
+package chrome_browser_ssl;
+
+option optimize_for = LITE_RUNTIME;
+
+// The set of sites to be used as a control group for Legacy TLS experiments.
+// Warning UI will not be shown on these sites.
+message LegacyTLSExperimentConfig {
+  optional uint32 version_id = 1;
+  // SHA-256 hash of the hostname of sites in the control group (e.g., for
+  // "https://test.example.com/foo" the hostname is "test.example.com"). This
+  // list must be in sorted order (alphanumeric by hash value).
+  repeated string control_site_hashes = 2;
+}
diff --git a/chrome/browser/themes/theme_service.cc b/chrome/browser/themes/theme_service.cc
index 2cd05dcf..35c1fb73 100644
--- a/chrome/browser/themes/theme_service.cc
+++ b/chrome/browser/themes/theme_service.cc
@@ -1146,6 +1146,10 @@
                 .color,
             custom};
   };
+  auto get_resulting_paint_color = [&](OmniboxColor fg, OmniboxColor bg) {
+    return OmniboxColor{GetResultingPaintColor(fg.value, bg.value),
+                        fg.custom || bg.custom};
+  };
 
   auto get_base_color = [&](int id) -> OmniboxColor {
     SkColor color;
@@ -1153,9 +1157,15 @@
       return {color, true};
     return {GetDefaultColor(id, incognito), false};
   };
+  // Avoid infinite loop caused by GetColor() below.
+  if (id == TP::COLOR_TOOLBAR)
+    return base::nullopt;
   // These are the only base colors.
-  OmniboxColor fg = get_base_color(TP::COLOR_OMNIBOX_TEXT);
-  OmniboxColor bg = get_base_color(TP::COLOR_OMNIBOX_BACKGROUND);
+  OmniboxColor bg = get_resulting_paint_color(
+      get_base_color(TP::COLOR_OMNIBOX_BACKGROUND),
+      {GetColor(TP::COLOR_TOOLBAR, incognito, nullptr), false});
+  OmniboxColor fg =
+      get_resulting_paint_color(get_base_color(TP::COLOR_OMNIBOX_TEXT), bg);
   if (invert) {
     // Given a color with some contrast against the opposite endpoint, returns a
     // color with that same contrast against the nearby endpoint.
@@ -1171,8 +1181,12 @@
   const bool dark = IsDark(bg.value);
 
   auto results_bg_color = [&]() { return get_color_with_max_contrast(fg); };
+  auto bg_hovered_color = [&]() { return blend_toward_max_contrast(bg, 0x0A); };
   auto security_chip_color = [&](OmniboxColor color) {
-    return blend_for_min_contrast(color, bg);
+    return blend_for_min_contrast(color, bg_hovered_color());
+  };
+  auto results_bg_hovered_color = [&]() {
+    return blend_toward_max_contrast(results_bg_color(), 0x1A);
   };
   auto url_color = [&](OmniboxColor bg) {
     return blend_for_min_contrast(
@@ -1194,7 +1208,7 @@
       case TP::COLOR_OMNIBOX_BACKGROUND:
         return bg;
       case TP::COLOR_OMNIBOX_BACKGROUND_HOVERED:
-        return blend_toward_max_contrast(bg, 0x0A);
+        return bg_hovered_color();
       case TP::COLOR_OMNIBOX_RESULTS_BG:
         return results_bg_color();
       case TP::COLOR_OMNIBOX_RESULTS_BG_SELECTED:
@@ -1210,9 +1224,9 @@
       case TP::COLOR_OMNIBOX_BUBBLE_OUTLINE_EXPERIMENTAL_KEYWORD_MODE:
         return url_color(results_bg_color());
       case TP::COLOR_OMNIBOX_TEXT_DIMMED:
-        return blend_with_clamped_contrast(bg);
+        return blend_with_clamped_contrast(bg_hovered_color());
       case TP::COLOR_OMNIBOX_RESULTS_TEXT_DIMMED:
-        return blend_with_clamped_contrast(results_bg_color());
+        return blend_with_clamped_contrast(results_bg_hovered_color());
       case TP::COLOR_OMNIBOX_RESULTS_TEXT_DIMMED_SELECTED:
         return blend_with_clamped_contrast(results_bg_selected_color());
       case TP::COLOR_OMNIBOX_RESULTS_ICON:
@@ -1222,9 +1236,9 @@
         return blend_for_min_contrast(derive_default_icon_color(fg),
                                       results_bg_selected_color());
       case TP::COLOR_OMNIBOX_RESULTS_BG_HOVERED:
-        return blend_toward_max_contrast(results_bg_color(), 0x1A);
+        return results_bg_hovered_color();
       case TP::COLOR_OMNIBOX_RESULTS_URL:
-        return url_color(results_bg_color());
+        return url_color(results_bg_hovered_color());
       case TP::COLOR_OMNIBOX_RESULTS_URL_SELECTED:
         return url_color(results_bg_selected_color());
       case TP::COLOR_OMNIBOX_SECURITY_CHIP_DEFAULT:
diff --git a/chrome/browser/themes/theme_service_unittest.cc b/chrome/browser/themes/theme_service_unittest.cc
index 68974d7..419f000 100644
--- a/chrome/browser/themes/theme_service_unittest.cc
+++ b/chrome/browser/themes/theme_service_unittest.cc
@@ -127,7 +127,6 @@
     bool has_custom_color;
     base::Optional<SkColor> color =
         theme_service->GetOmniboxColor(id, incognito, &has_custom_color);
-    EXPECT_FALSE(has_custom_color);
     EXPECT_TRUE(color);
     return color.value_or(gfx::kPlaceholderColor);
   }
@@ -587,19 +586,12 @@
            TP::COLOR_OMNIBOX_BACKGROUND},
           {TP::COLOR_OMNIBOX_SECURITY_CHIP_DANGEROUS,
            TP::COLOR_OMNIBOX_RESULTS_BG},
-          // TODO(thomasanderson): Because colors are computed relative to
-          // non-hovered backgrounds, some colors over hovered backgrounds do
-          // not have sufficient contrast in all configurations.  Computing the
-          // non-contrasty colors here relative to hovered backgrounds should
-          // fix this and not reduce contrast of non-hovered backgrounds.
-          // {TP::COLOR_OMNIBOX_TEXT_DIMMED,
-          //  TP::COLOR_OMNIBOX_BACKGROUND_HOVERED},
-          // {TP::COLOR_OMNIBOX_RESULTS_TEXT_DIMMED,
-          //  TP::COLOR_OMNIBOX_RESULTS_BG_HOVERED},
-          // {TP::COLOR_OMNIBOX_RESULTS_URL,
-          //  TP::COLOR_OMNIBOX_RESULTS_BG_HOVERED},
-          // {TP::COLOR_OMNIBOX_SECURITY_CHIP_DANGEROUS,
-          //  TP::COLOR_OMNIBOX_BACKGROUND_HOVERED},
+          {TP::COLOR_OMNIBOX_TEXT_DIMMED, TP::COLOR_OMNIBOX_BACKGROUND_HOVERED},
+          {TP::COLOR_OMNIBOX_RESULTS_TEXT_DIMMED,
+           TP::COLOR_OMNIBOX_RESULTS_BG_HOVERED},
+          {TP::COLOR_OMNIBOX_RESULTS_URL, TP::COLOR_OMNIBOX_RESULTS_BG_HOVERED},
+          {TP::COLOR_OMNIBOX_SECURITY_CHIP_DANGEROUS,
+           TP::COLOR_OMNIBOX_BACKGROUND_HOVERED},
       };
       auto check_sufficient_contrast = [&](int id1, int id2) {
         const float contrast = color_utils::GetContrastRatio(
@@ -616,4 +608,62 @@
   }
 }
 
+// Ensure nothing DCHECKs if either of COLOR_OMNIBOX_BACKGROUND or
+// COLOR_OMNIBOX_TEXT are translucent (https://crbug.com/1006102).
+TEST_F(ThemeServiceTest, TranslucentOmniboxBackgroundAndText) {
+  using TP = ThemeProperties;
+  ThemeService* theme_service =
+      ThemeServiceFactory::GetForProfile(profile_.get());
+
+  class TranslucentOmniboxThemeSupplier : public CustomThemeSupplier {
+   public:
+    TranslucentOmniboxThemeSupplier()
+        : CustomThemeSupplier(ThemeType::EXTENSION) {}
+    bool GetColor(int id, SkColor* color) const override {
+      switch (id) {
+        case TP::COLOR_OMNIBOX_BACKGROUND:
+          *color = SkColorSetARGB(127, 255, 255, 255);
+          return true;
+        case TP::COLOR_OMNIBOX_TEXT:
+          *color = SkColorSetARGB(127, 0, 0, 0);
+          return true;
+      }
+      return CustomThemeSupplier::GetColor(id, color);
+    }
+
+   private:
+    ~TranslucentOmniboxThemeSupplier() override = default;
+  };
+  set_theme_supplier(theme_service,
+                     base::MakeRefCounted<TranslucentOmniboxThemeSupplier>());
+
+  constexpr int ids[] = {
+      TP::COLOR_OMNIBOX_BACKGROUND,
+      TP::COLOR_OMNIBOX_TEXT,
+      TP::COLOR_OMNIBOX_BACKGROUND_HOVERED,
+      TP::COLOR_OMNIBOX_SELECTED_KEYWORD,
+      TP::COLOR_OMNIBOX_TEXT_DIMMED,
+      TP::COLOR_OMNIBOX_RESULTS_BG,
+      TP::COLOR_OMNIBOX_RESULTS_BG_HOVERED,
+      TP::COLOR_OMNIBOX_RESULTS_BG_SELECTED,
+      TP::COLOR_OMNIBOX_RESULTS_TEXT_SELECTED,
+      TP::COLOR_OMNIBOX_RESULTS_TEXT_DIMMED,
+      TP::COLOR_OMNIBOX_RESULTS_TEXT_DIMMED_SELECTED,
+      TP::COLOR_OMNIBOX_RESULTS_ICON,
+      TP::COLOR_OMNIBOX_RESULTS_ICON_SELECTED,
+      TP::COLOR_OMNIBOX_RESULTS_URL,
+      TP::COLOR_OMNIBOX_RESULTS_URL_SELECTED,
+      TP::COLOR_OMNIBOX_BUBBLE_OUTLINE,
+      TP::COLOR_OMNIBOX_BUBBLE_OUTLINE_EXPERIMENTAL_KEYWORD_MODE,
+      TP::COLOR_OMNIBOX_SECURITY_CHIP_DEFAULT,
+      TP::COLOR_OMNIBOX_SECURITY_CHIP_SECURE,
+      TP::COLOR_OMNIBOX_SECURITY_CHIP_DANGEROUS,
+  };
+
+  for (int id : ids) {
+    GetOmniboxColor(theme_service, id, false);
+    GetOmniboxColor(theme_service, id, true);
+  }
+}
+
 }  // namespace theme_service_internal
diff --git a/chrome/browser/touch_to_fill/android/internal/java/strings/android_touch_to_fill_strings.grd b/chrome/browser/touch_to_fill/android/internal/java/strings/android_touch_to_fill_strings.grd
index d8658da..b613018 100644
--- a/chrome/browser/touch_to_fill/android/internal/java/strings/android_touch_to_fill_strings.grd
+++ b/chrome/browser/touch_to_fill/android/internal/java/strings/android_touch_to_fill_strings.grd
@@ -50,6 +50,60 @@
     <output filename="values-zh-rCN/android_touch_to_fill_strings.xml" lang="zh-CN" type="android" />
     <output filename="values-zh-rTW/android_touch_to_fill_strings.xml" lang="zh-TW" type="android" />
   </outputs>
+  <translations>
+    <file lang="am" path="translations/android_touch_to_fill_strings_am.xtb" />
+    <file lang="ar" path="translations/android_touch_to_fill_strings_ar.xtb" />
+    <file lang="bg" path="translations/android_touch_to_fill_strings_bg.xtb" />
+    <file lang="bn" path="translations/android_touch_to_fill_strings_bn.xtb" />
+    <file lang="ca" path="translations/android_touch_to_fill_strings_ca.xtb" />
+    <file lang="cs" path="translations/android_touch_to_fill_strings_cs.xtb" />
+    <file lang="da" path="translations/android_touch_to_fill_strings_da.xtb" />
+    <file lang="de" path="translations/android_touch_to_fill_strings_de.xtb" />
+    <file lang="el" path="translations/android_touch_to_fill_strings_el.xtb" />
+    <file lang="en-GB" path="translations/android_touch_to_fill_strings_en-GB.xtb" />
+    <file lang="es" path="translations/android_touch_to_fill_strings_es.xtb" />
+    <file lang="es-419" path="translations/android_touch_to_fill_strings_es-419.xtb" />
+    <file lang="et" path="translations/android_touch_to_fill_strings_et.xtb" />
+    <file lang="fa" path="translations/android_touch_to_fill_strings_fa.xtb" />
+    <file lang="fi" path="translations/android_touch_to_fill_strings_fi.xtb" />
+    <file lang="fil" path="translations/android_touch_to_fill_strings_fil.xtb" />
+    <file lang="fr" path="translations/android_touch_to_fill_strings_fr.xtb" />
+    <file lang="gu" path="translations/android_touch_to_fill_strings_gu.xtb" />
+    <file lang="hi" path="translations/android_touch_to_fill_strings_hi.xtb" />
+    <file lang="hr" path="translations/android_touch_to_fill_strings_hr.xtb" />
+    <file lang="hu" path="translations/android_touch_to_fill_strings_hu.xtb" />
+    <file lang="id" path="translations/android_touch_to_fill_strings_id.xtb" />
+    <file lang="it" path="translations/android_touch_to_fill_strings_it.xtb" />
+    <file lang="iw" path="translations/android_touch_to_fill_strings_iw.xtb" />
+    <file lang="ja" path="translations/android_touch_to_fill_strings_ja.xtb" />
+    <file lang="ko" path="translations/android_touch_to_fill_strings_ko.xtb" />
+    <file lang="kn" path="translations/android_touch_to_fill_strings_kn.xtb" />
+    <file lang="lt" path="translations/android_touch_to_fill_strings_lt.xtb" />
+    <file lang="lv" path="translations/android_touch_to_fill_strings_lv.xtb" />
+    <file lang="ml" path="translations/android_touch_to_fill_strings_ml.xtb" />
+    <file lang="mr" path="translations/android_touch_to_fill_strings_mr.xtb" />
+    <file lang="ms" path="translations/android_touch_to_fill_strings_ms.xtb" />
+    <file lang="nl" path="translations/android_touch_to_fill_strings_nl.xtb" />
+    <file lang="no" path="translations/android_touch_to_fill_strings_no.xtb" />
+    <file lang="pl" path="translations/android_touch_to_fill_strings_pl.xtb" />
+    <file lang="pt-BR" path="translations/android_touch_to_fill_strings_pt-BR.xtb" />
+    <file lang="pt-PT" path="translations/android_touch_to_fill_strings_pt-PT.xtb" />
+    <file lang="ro" path="translations/android_touch_to_fill_strings_ro.xtb" />
+    <file lang="ru" path="translations/android_touch_to_fill_strings_ru.xtb" />
+    <file lang="sk" path="translations/android_touch_to_fill_strings_sk.xtb" />
+    <file lang="sl" path="translations/android_touch_to_fill_strings_sl.xtb" />
+    <file lang="sr" path="translations/android_touch_to_fill_strings_sr.xtb" />
+    <file lang="sv" path="translations/android_touch_to_fill_strings_sv.xtb" />
+    <file lang="sw" path="translations/android_touch_to_fill_strings_sw.xtb" />
+    <file lang="ta" path="translations/android_touch_to_fill_strings_ta.xtb" />
+    <file lang="te" path="translations/android_touch_to_fill_strings_te.xtb" />
+    <file lang="th" path="translations/android_touch_to_fill_strings_th.xtb" />
+    <file lang="tr" path="translations/android_touch_to_fill_strings_tr.xtb" />
+    <file lang="uk" path="translations/android_touch_to_fill_strings_uk.xtb" />
+    <file lang="vi" path="translations/android_touch_to_fill_strings_vi.xtb" />
+    <file lang="zh-CN" path="translations/android_touch_to_fill_strings_zh-CN.xtb" />
+    <file lang="zh-TW" path="translations/android_touch_to_fill_strings_zh-TW.xtb" />
+  </translations>
   <release allow_pseudo="false" seq="1">
     <messages fallback_to_english="true">
       <!-- Touch To Fill -->
diff --git a/chrome/browser/touch_to_fill/android/internal/java/strings/translations/android_touch_to_fill_strings_am.xtb b/chrome/browser/touch_to_fill/android/internal/java/strings/translations/android_touch_to_fill_strings_am.xtb
new file mode 100644
index 0000000..1a8356cf
--- /dev/null
+++ b/chrome/browser/touch_to_fill/android/internal/java/strings/translations/android_touch_to_fill_strings_am.xtb
@@ -0,0 +1 @@
+<?xml version="1.0" ?><!DOCTYPE translationbundle><translationbundle lang="am"></translationbundle>
diff --git a/chrome/browser/touch_to_fill/android/internal/java/strings/translations/android_touch_to_fill_strings_ar.xtb b/chrome/browser/touch_to_fill/android/internal/java/strings/translations/android_touch_to_fill_strings_ar.xtb
new file mode 100644
index 0000000..577c15f
--- /dev/null
+++ b/chrome/browser/touch_to_fill/android/internal/java/strings/translations/android_touch_to_fill_strings_ar.xtb
@@ -0,0 +1 @@
+<?xml version="1.0" ?><!DOCTYPE translationbundle><translationbundle lang="ar"></translationbundle>
diff --git a/chrome/browser/touch_to_fill/android/internal/java/strings/translations/android_touch_to_fill_strings_bg.xtb b/chrome/browser/touch_to_fill/android/internal/java/strings/translations/android_touch_to_fill_strings_bg.xtb
new file mode 100644
index 0000000..25d06b3
--- /dev/null
+++ b/chrome/browser/touch_to_fill/android/internal/java/strings/translations/android_touch_to_fill_strings_bg.xtb
@@ -0,0 +1 @@
+<?xml version="1.0" ?><!DOCTYPE translationbundle><translationbundle lang="bg"></translationbundle>
diff --git a/chrome/browser/touch_to_fill/android/internal/java/strings/translations/android_touch_to_fill_strings_bn.xtb b/chrome/browser/touch_to_fill/android/internal/java/strings/translations/android_touch_to_fill_strings_bn.xtb
new file mode 100644
index 0000000..b02b1e7f
--- /dev/null
+++ b/chrome/browser/touch_to_fill/android/internal/java/strings/translations/android_touch_to_fill_strings_bn.xtb
@@ -0,0 +1 @@
+<?xml version="1.0" ?><!DOCTYPE translationbundle><translationbundle lang="bn"></translationbundle>
diff --git a/chrome/browser/touch_to_fill/android/internal/java/strings/translations/android_touch_to_fill_strings_ca.xtb b/chrome/browser/touch_to_fill/android/internal/java/strings/translations/android_touch_to_fill_strings_ca.xtb
new file mode 100644
index 0000000..1e9d243
--- /dev/null
+++ b/chrome/browser/touch_to_fill/android/internal/java/strings/translations/android_touch_to_fill_strings_ca.xtb
@@ -0,0 +1 @@
+<?xml version="1.0" ?><!DOCTYPE translationbundle><translationbundle lang="ca"></translationbundle>
diff --git a/chrome/browser/touch_to_fill/android/internal/java/strings/translations/android_touch_to_fill_strings_cs.xtb b/chrome/browser/touch_to_fill/android/internal/java/strings/translations/android_touch_to_fill_strings_cs.xtb
new file mode 100644
index 0000000..b6103f7
--- /dev/null
+++ b/chrome/browser/touch_to_fill/android/internal/java/strings/translations/android_touch_to_fill_strings_cs.xtb
@@ -0,0 +1 @@
+<?xml version="1.0" ?><!DOCTYPE translationbundle><translationbundle lang="cs"></translationbundle>
diff --git a/chrome/browser/touch_to_fill/android/internal/java/strings/translations/android_touch_to_fill_strings_da.xtb b/chrome/browser/touch_to_fill/android/internal/java/strings/translations/android_touch_to_fill_strings_da.xtb
new file mode 100644
index 0000000..6feffd32
--- /dev/null
+++ b/chrome/browser/touch_to_fill/android/internal/java/strings/translations/android_touch_to_fill_strings_da.xtb
@@ -0,0 +1 @@
+<?xml version="1.0" ?><!DOCTYPE translationbundle><translationbundle lang="da"></translationbundle>
diff --git a/chrome/browser/touch_to_fill/android/internal/java/strings/translations/android_touch_to_fill_strings_de.xtb b/chrome/browser/touch_to_fill/android/internal/java/strings/translations/android_touch_to_fill_strings_de.xtb
new file mode 100644
index 0000000..d2908e8a
--- /dev/null
+++ b/chrome/browser/touch_to_fill/android/internal/java/strings/translations/android_touch_to_fill_strings_de.xtb
@@ -0,0 +1 @@
+<?xml version="1.0" ?><!DOCTYPE translationbundle><translationbundle lang="de"></translationbundle>
diff --git a/chrome/browser/touch_to_fill/android/internal/java/strings/translations/android_touch_to_fill_strings_el.xtb b/chrome/browser/touch_to_fill/android/internal/java/strings/translations/android_touch_to_fill_strings_el.xtb
new file mode 100644
index 0000000..2d96e6c
--- /dev/null
+++ b/chrome/browser/touch_to_fill/android/internal/java/strings/translations/android_touch_to_fill_strings_el.xtb
@@ -0,0 +1 @@
+<?xml version="1.0" ?><!DOCTYPE translationbundle><translationbundle lang="el"></translationbundle>
diff --git a/chrome/browser/touch_to_fill/android/internal/java/strings/translations/android_touch_to_fill_strings_en-GB.xtb b/chrome/browser/touch_to_fill/android/internal/java/strings/translations/android_touch_to_fill_strings_en-GB.xtb
new file mode 100644
index 0000000..769a524
--- /dev/null
+++ b/chrome/browser/touch_to_fill/android/internal/java/strings/translations/android_touch_to_fill_strings_en-GB.xtb
@@ -0,0 +1 @@
+<?xml version="1.0" ?><!DOCTYPE translationbundle><translationbundle lang="en-GB"></translationbundle>
diff --git a/chrome/browser/touch_to_fill/android/internal/java/strings/translations/android_touch_to_fill_strings_es-419.xtb b/chrome/browser/touch_to_fill/android/internal/java/strings/translations/android_touch_to_fill_strings_es-419.xtb
new file mode 100644
index 0000000..37258dd
--- /dev/null
+++ b/chrome/browser/touch_to_fill/android/internal/java/strings/translations/android_touch_to_fill_strings_es-419.xtb
@@ -0,0 +1 @@
+<?xml version="1.0" ?><!DOCTYPE translationbundle><translationbundle lang="es-419"></translationbundle>
diff --git a/chrome/browser/touch_to_fill/android/internal/java/strings/translations/android_touch_to_fill_strings_es.xtb b/chrome/browser/touch_to_fill/android/internal/java/strings/translations/android_touch_to_fill_strings_es.xtb
new file mode 100644
index 0000000..27d8ca3
--- /dev/null
+++ b/chrome/browser/touch_to_fill/android/internal/java/strings/translations/android_touch_to_fill_strings_es.xtb
@@ -0,0 +1 @@
+<?xml version="1.0" ?><!DOCTYPE translationbundle><translationbundle lang="es"></translationbundle>
diff --git a/chrome/browser/touch_to_fill/android/internal/java/strings/translations/android_touch_to_fill_strings_et.xtb b/chrome/browser/touch_to_fill/android/internal/java/strings/translations/android_touch_to_fill_strings_et.xtb
new file mode 100644
index 0000000..a14139f
--- /dev/null
+++ b/chrome/browser/touch_to_fill/android/internal/java/strings/translations/android_touch_to_fill_strings_et.xtb
@@ -0,0 +1 @@
+<?xml version="1.0" ?><!DOCTYPE translationbundle><translationbundle lang="et"></translationbundle>
diff --git a/chrome/browser/touch_to_fill/android/internal/java/strings/translations/android_touch_to_fill_strings_etc.xtb b/chrome/browser/touch_to_fill/android/internal/java/strings/translations/android_touch_to_fill_strings_etc.xtb
new file mode 100644
index 0000000..4f2ff61
--- /dev/null
+++ b/chrome/browser/touch_to_fill/android/internal/java/strings/translations/android_touch_to_fill_strings_etc.xtb
@@ -0,0 +1 @@
+<?xml version="1.0" ?><!DOCTYPE translationbundle><translationbundle lang="etc"></translationbundle>
diff --git a/chrome/browser/touch_to_fill/android/internal/java/strings/translations/android_touch_to_fill_strings_fa.xtb b/chrome/browser/touch_to_fill/android/internal/java/strings/translations/android_touch_to_fill_strings_fa.xtb
new file mode 100644
index 0000000..41bc8c3
--- /dev/null
+++ b/chrome/browser/touch_to_fill/android/internal/java/strings/translations/android_touch_to_fill_strings_fa.xtb
@@ -0,0 +1 @@
+<?xml version="1.0" ?><!DOCTYPE translationbundle><translationbundle lang="fa"></translationbundle>
diff --git a/chrome/browser/touch_to_fill/android/internal/java/strings/translations/android_touch_to_fill_strings_fi.xtb b/chrome/browser/touch_to_fill/android/internal/java/strings/translations/android_touch_to_fill_strings_fi.xtb
new file mode 100644
index 0000000..b2ed2bf
--- /dev/null
+++ b/chrome/browser/touch_to_fill/android/internal/java/strings/translations/android_touch_to_fill_strings_fi.xtb
@@ -0,0 +1 @@
+<?xml version="1.0" ?><!DOCTYPE translationbundle><translationbundle lang="fi"></translationbundle>
diff --git a/chrome/browser/touch_to_fill/android/internal/java/strings/translations/android_touch_to_fill_strings_fil.xtb b/chrome/browser/touch_to_fill/android/internal/java/strings/translations/android_touch_to_fill_strings_fil.xtb
new file mode 100644
index 0000000..6ca565417
--- /dev/null
+++ b/chrome/browser/touch_to_fill/android/internal/java/strings/translations/android_touch_to_fill_strings_fil.xtb
@@ -0,0 +1 @@
+<?xml version="1.0" ?><!DOCTYPE translationbundle><translationbundle lang="fil"></translationbundle>
diff --git a/chrome/browser/touch_to_fill/android/internal/java/strings/translations/android_touch_to_fill_strings_fr.xtb b/chrome/browser/touch_to_fill/android/internal/java/strings/translations/android_touch_to_fill_strings_fr.xtb
new file mode 100644
index 0000000..1ce4293c
--- /dev/null
+++ b/chrome/browser/touch_to_fill/android/internal/java/strings/translations/android_touch_to_fill_strings_fr.xtb
@@ -0,0 +1 @@
+<?xml version="1.0" ?><!DOCTYPE translationbundle><translationbundle lang="fr"></translationbundle>
diff --git a/chrome/browser/touch_to_fill/android/internal/java/strings/translations/android_touch_to_fill_strings_gu.xtb b/chrome/browser/touch_to_fill/android/internal/java/strings/translations/android_touch_to_fill_strings_gu.xtb
new file mode 100644
index 0000000..1b8a058
--- /dev/null
+++ b/chrome/browser/touch_to_fill/android/internal/java/strings/translations/android_touch_to_fill_strings_gu.xtb
@@ -0,0 +1 @@
+<?xml version="1.0" ?><!DOCTYPE translationbundle><translationbundle lang="gu"></translationbundle>
diff --git a/chrome/browser/touch_to_fill/android/internal/java/strings/translations/android_touch_to_fill_strings_hi.xtb b/chrome/browser/touch_to_fill/android/internal/java/strings/translations/android_touch_to_fill_strings_hi.xtb
new file mode 100644
index 0000000..e9f9cc5
--- /dev/null
+++ b/chrome/browser/touch_to_fill/android/internal/java/strings/translations/android_touch_to_fill_strings_hi.xtb
@@ -0,0 +1 @@
+<?xml version="1.0" ?><!DOCTYPE translationbundle><translationbundle lang="hi"></translationbundle>
diff --git a/chrome/browser/touch_to_fill/android/internal/java/strings/translations/android_touch_to_fill_strings_hr.xtb b/chrome/browser/touch_to_fill/android/internal/java/strings/translations/android_touch_to_fill_strings_hr.xtb
new file mode 100644
index 0000000..abb8268
--- /dev/null
+++ b/chrome/browser/touch_to_fill/android/internal/java/strings/translations/android_touch_to_fill_strings_hr.xtb
@@ -0,0 +1 @@
+<?xml version="1.0" ?><!DOCTYPE translationbundle><translationbundle lang="hr"></translationbundle>
diff --git a/chrome/browser/touch_to_fill/android/internal/java/strings/translations/android_touch_to_fill_strings_hu.xtb b/chrome/browser/touch_to_fill/android/internal/java/strings/translations/android_touch_to_fill_strings_hu.xtb
new file mode 100644
index 0000000..5a7e2c9
--- /dev/null
+++ b/chrome/browser/touch_to_fill/android/internal/java/strings/translations/android_touch_to_fill_strings_hu.xtb
@@ -0,0 +1 @@
+<?xml version="1.0" ?><!DOCTYPE translationbundle><translationbundle lang="hu"></translationbundle>
diff --git a/chrome/browser/touch_to_fill/android/internal/java/strings/translations/android_touch_to_fill_strings_id.xtb b/chrome/browser/touch_to_fill/android/internal/java/strings/translations/android_touch_to_fill_strings_id.xtb
new file mode 100644
index 0000000..bced312
--- /dev/null
+++ b/chrome/browser/touch_to_fill/android/internal/java/strings/translations/android_touch_to_fill_strings_id.xtb
@@ -0,0 +1 @@
+<?xml version="1.0" ?><!DOCTYPE translationbundle><translationbundle lang="id"></translationbundle>
diff --git a/chrome/browser/touch_to_fill/android/internal/java/strings/translations/android_touch_to_fill_strings_it.xtb b/chrome/browser/touch_to_fill/android/internal/java/strings/translations/android_touch_to_fill_strings_it.xtb
new file mode 100644
index 0000000..d56be5c
--- /dev/null
+++ b/chrome/browser/touch_to_fill/android/internal/java/strings/translations/android_touch_to_fill_strings_it.xtb
@@ -0,0 +1 @@
+<?xml version="1.0" ?><!DOCTYPE translationbundle><translationbundle lang="it"></translationbundle>
diff --git a/chrome/browser/touch_to_fill/android/internal/java/strings/translations/android_touch_to_fill_strings_iw.xtb b/chrome/browser/touch_to_fill/android/internal/java/strings/translations/android_touch_to_fill_strings_iw.xtb
new file mode 100644
index 0000000..d17d24e
--- /dev/null
+++ b/chrome/browser/touch_to_fill/android/internal/java/strings/translations/android_touch_to_fill_strings_iw.xtb
@@ -0,0 +1 @@
+<?xml version="1.0" ?><!DOCTYPE translationbundle><translationbundle lang="iw"></translationbundle>
diff --git a/chrome/browser/touch_to_fill/android/internal/java/strings/translations/android_touch_to_fill_strings_ja.xtb b/chrome/browser/touch_to_fill/android/internal/java/strings/translations/android_touch_to_fill_strings_ja.xtb
new file mode 100644
index 0000000..c5828bf
--- /dev/null
+++ b/chrome/browser/touch_to_fill/android/internal/java/strings/translations/android_touch_to_fill_strings_ja.xtb
@@ -0,0 +1 @@
+<?xml version="1.0" ?><!DOCTYPE translationbundle><translationbundle lang="ja"></translationbundle>
diff --git a/chrome/browser/touch_to_fill/android/internal/java/strings/translations/android_touch_to_fill_strings_kn.xtb b/chrome/browser/touch_to_fill/android/internal/java/strings/translations/android_touch_to_fill_strings_kn.xtb
new file mode 100644
index 0000000..6f2561a
--- /dev/null
+++ b/chrome/browser/touch_to_fill/android/internal/java/strings/translations/android_touch_to_fill_strings_kn.xtb
@@ -0,0 +1 @@
+<?xml version="1.0" ?><!DOCTYPE translationbundle><translationbundle lang="kn"></translationbundle>
diff --git a/chrome/browser/touch_to_fill/android/internal/java/strings/translations/android_touch_to_fill_strings_ko.xtb b/chrome/browser/touch_to_fill/android/internal/java/strings/translations/android_touch_to_fill_strings_ko.xtb
new file mode 100644
index 0000000..aac09f4
--- /dev/null
+++ b/chrome/browser/touch_to_fill/android/internal/java/strings/translations/android_touch_to_fill_strings_ko.xtb
@@ -0,0 +1 @@
+<?xml version="1.0" ?><!DOCTYPE translationbundle><translationbundle lang="ko"></translationbundle>
diff --git a/chrome/browser/touch_to_fill/android/internal/java/strings/translations/android_touch_to_fill_strings_lt.xtb b/chrome/browser/touch_to_fill/android/internal/java/strings/translations/android_touch_to_fill_strings_lt.xtb
new file mode 100644
index 0000000..e386c81
--- /dev/null
+++ b/chrome/browser/touch_to_fill/android/internal/java/strings/translations/android_touch_to_fill_strings_lt.xtb
@@ -0,0 +1 @@
+<?xml version="1.0" ?><!DOCTYPE translationbundle><translationbundle lang="lt"></translationbundle>
diff --git a/chrome/browser/touch_to_fill/android/internal/java/strings/translations/android_touch_to_fill_strings_lv.xtb b/chrome/browser/touch_to_fill/android/internal/java/strings/translations/android_touch_to_fill_strings_lv.xtb
new file mode 100644
index 0000000..c27c4065
--- /dev/null
+++ b/chrome/browser/touch_to_fill/android/internal/java/strings/translations/android_touch_to_fill_strings_lv.xtb
@@ -0,0 +1 @@
+<?xml version="1.0" ?><!DOCTYPE translationbundle><translationbundle lang="lv"></translationbundle>
diff --git a/chrome/browser/touch_to_fill/android/internal/java/strings/translations/android_touch_to_fill_strings_ml.xtb b/chrome/browser/touch_to_fill/android/internal/java/strings/translations/android_touch_to_fill_strings_ml.xtb
new file mode 100644
index 0000000..970244e
--- /dev/null
+++ b/chrome/browser/touch_to_fill/android/internal/java/strings/translations/android_touch_to_fill_strings_ml.xtb
@@ -0,0 +1 @@
+<?xml version="1.0" ?><!DOCTYPE translationbundle><translationbundle lang="ml"></translationbundle>
diff --git a/chrome/browser/touch_to_fill/android/internal/java/strings/translations/android_touch_to_fill_strings_mr.xtb b/chrome/browser/touch_to_fill/android/internal/java/strings/translations/android_touch_to_fill_strings_mr.xtb
new file mode 100644
index 0000000..7eb198d
--- /dev/null
+++ b/chrome/browser/touch_to_fill/android/internal/java/strings/translations/android_touch_to_fill_strings_mr.xtb
@@ -0,0 +1 @@
+<?xml version="1.0" ?><!DOCTYPE translationbundle><translationbundle lang="mr"></translationbundle>
diff --git a/chrome/browser/touch_to_fill/android/internal/java/strings/translations/android_touch_to_fill_strings_ms.xtb b/chrome/browser/touch_to_fill/android/internal/java/strings/translations/android_touch_to_fill_strings_ms.xtb
new file mode 100644
index 0000000..b8f88eb5
--- /dev/null
+++ b/chrome/browser/touch_to_fill/android/internal/java/strings/translations/android_touch_to_fill_strings_ms.xtb
@@ -0,0 +1 @@
+<?xml version="1.0" ?><!DOCTYPE translationbundle><translationbundle lang="ms"></translationbundle>
diff --git a/chrome/browser/touch_to_fill/android/internal/java/strings/translations/android_touch_to_fill_strings_nl.xtb b/chrome/browser/touch_to_fill/android/internal/java/strings/translations/android_touch_to_fill_strings_nl.xtb
new file mode 100644
index 0000000..08c20249
--- /dev/null
+++ b/chrome/browser/touch_to_fill/android/internal/java/strings/translations/android_touch_to_fill_strings_nl.xtb
@@ -0,0 +1 @@
+<?xml version="1.0" ?><!DOCTYPE translationbundle><translationbundle lang="nl"></translationbundle>
diff --git a/chrome/browser/touch_to_fill/android/internal/java/strings/translations/android_touch_to_fill_strings_no.xtb b/chrome/browser/touch_to_fill/android/internal/java/strings/translations/android_touch_to_fill_strings_no.xtb
new file mode 100644
index 0000000..52b6011
--- /dev/null
+++ b/chrome/browser/touch_to_fill/android/internal/java/strings/translations/android_touch_to_fill_strings_no.xtb
@@ -0,0 +1 @@
+<?xml version="1.0" ?><!DOCTYPE translationbundle><translationbundle lang="no"></translationbundle>
diff --git a/chrome/browser/touch_to_fill/android/internal/java/strings/translations/android_touch_to_fill_strings_pl.xtb b/chrome/browser/touch_to_fill/android/internal/java/strings/translations/android_touch_to_fill_strings_pl.xtb
new file mode 100644
index 0000000..57c76f6
--- /dev/null
+++ b/chrome/browser/touch_to_fill/android/internal/java/strings/translations/android_touch_to_fill_strings_pl.xtb
@@ -0,0 +1 @@
+<?xml version="1.0" ?><!DOCTYPE translationbundle><translationbundle lang="pl"></translationbundle>
diff --git a/chrome/browser/touch_to_fill/android/internal/java/strings/translations/android_touch_to_fill_strings_pt-BR.xtb b/chrome/browser/touch_to_fill/android/internal/java/strings/translations/android_touch_to_fill_strings_pt-BR.xtb
new file mode 100644
index 0000000..1ccc1be
--- /dev/null
+++ b/chrome/browser/touch_to_fill/android/internal/java/strings/translations/android_touch_to_fill_strings_pt-BR.xtb
@@ -0,0 +1 @@
+<?xml version="1.0" ?><!DOCTYPE translationbundle><translationbundle lang="pt-BR"></translationbundle>
diff --git a/chrome/browser/touch_to_fill/android/internal/java/strings/translations/android_touch_to_fill_strings_pt-PT.xtb b/chrome/browser/touch_to_fill/android/internal/java/strings/translations/android_touch_to_fill_strings_pt-PT.xtb
new file mode 100644
index 0000000..448ac9de
--- /dev/null
+++ b/chrome/browser/touch_to_fill/android/internal/java/strings/translations/android_touch_to_fill_strings_pt-PT.xtb
@@ -0,0 +1 @@
+<?xml version="1.0" ?><!DOCTYPE translationbundle><translationbundle lang="pt-PT"></translationbundle>
diff --git a/chrome/browser/touch_to_fill/android/internal/java/strings/translations/android_touch_to_fill_strings_ro.xtb b/chrome/browser/touch_to_fill/android/internal/java/strings/translations/android_touch_to_fill_strings_ro.xtb
new file mode 100644
index 0000000..ee107e4
--- /dev/null
+++ b/chrome/browser/touch_to_fill/android/internal/java/strings/translations/android_touch_to_fill_strings_ro.xtb
@@ -0,0 +1 @@
+<?xml version="1.0" ?><!DOCTYPE translationbundle><translationbundle lang="ro"></translationbundle>
diff --git a/chrome/browser/touch_to_fill/android/internal/java/strings/translations/android_touch_to_fill_strings_ru.xtb b/chrome/browser/touch_to_fill/android/internal/java/strings/translations/android_touch_to_fill_strings_ru.xtb
new file mode 100644
index 0000000..1161eea
--- /dev/null
+++ b/chrome/browser/touch_to_fill/android/internal/java/strings/translations/android_touch_to_fill_strings_ru.xtb
@@ -0,0 +1 @@
+<?xml version="1.0" ?><!DOCTYPE translationbundle><translationbundle lang="ru"></translationbundle>
diff --git a/chrome/browser/touch_to_fill/android/internal/java/strings/translations/android_touch_to_fill_strings_sk.xtb b/chrome/browser/touch_to_fill/android/internal/java/strings/translations/android_touch_to_fill_strings_sk.xtb
new file mode 100644
index 0000000..285c7cb
--- /dev/null
+++ b/chrome/browser/touch_to_fill/android/internal/java/strings/translations/android_touch_to_fill_strings_sk.xtb
@@ -0,0 +1 @@
+<?xml version="1.0" ?><!DOCTYPE translationbundle><translationbundle lang="sk"></translationbundle>
diff --git a/chrome/browser/touch_to_fill/android/internal/java/strings/translations/android_touch_to_fill_strings_sl.xtb b/chrome/browser/touch_to_fill/android/internal/java/strings/translations/android_touch_to_fill_strings_sl.xtb
new file mode 100644
index 0000000..5b943686
--- /dev/null
+++ b/chrome/browser/touch_to_fill/android/internal/java/strings/translations/android_touch_to_fill_strings_sl.xtb
@@ -0,0 +1 @@
+<?xml version="1.0" ?><!DOCTYPE translationbundle><translationbundle lang="sl"></translationbundle>
diff --git a/chrome/browser/touch_to_fill/android/internal/java/strings/translations/android_touch_to_fill_strings_sr.xtb b/chrome/browser/touch_to_fill/android/internal/java/strings/translations/android_touch_to_fill_strings_sr.xtb
new file mode 100644
index 0000000..037a5c14
--- /dev/null
+++ b/chrome/browser/touch_to_fill/android/internal/java/strings/translations/android_touch_to_fill_strings_sr.xtb
@@ -0,0 +1 @@
+<?xml version="1.0" ?><!DOCTYPE translationbundle><translationbundle lang="sr"></translationbundle>
diff --git a/chrome/browser/touch_to_fill/android/internal/java/strings/translations/android_touch_to_fill_strings_sv.xtb b/chrome/browser/touch_to_fill/android/internal/java/strings/translations/android_touch_to_fill_strings_sv.xtb
new file mode 100644
index 0000000..8f4581fd
--- /dev/null
+++ b/chrome/browser/touch_to_fill/android/internal/java/strings/translations/android_touch_to_fill_strings_sv.xtb
@@ -0,0 +1 @@
+<?xml version="1.0" ?><!DOCTYPE translationbundle><translationbundle lang="sv"></translationbundle>
diff --git a/chrome/browser/touch_to_fill/android/internal/java/strings/translations/android_touch_to_fill_strings_sw.xtb b/chrome/browser/touch_to_fill/android/internal/java/strings/translations/android_touch_to_fill_strings_sw.xtb
new file mode 100644
index 0000000..0b25c33
--- /dev/null
+++ b/chrome/browser/touch_to_fill/android/internal/java/strings/translations/android_touch_to_fill_strings_sw.xtb
@@ -0,0 +1 @@
+<?xml version="1.0" ?><!DOCTYPE translationbundle><translationbundle lang="sw"></translationbundle>
diff --git a/chrome/browser/touch_to_fill/android/internal/java/strings/translations/android_touch_to_fill_strings_ta.xtb b/chrome/browser/touch_to_fill/android/internal/java/strings/translations/android_touch_to_fill_strings_ta.xtb
new file mode 100644
index 0000000..ab9e8aab
--- /dev/null
+++ b/chrome/browser/touch_to_fill/android/internal/java/strings/translations/android_touch_to_fill_strings_ta.xtb
@@ -0,0 +1 @@
+<?xml version="1.0" ?><!DOCTYPE translationbundle><translationbundle lang="ta"></translationbundle>
diff --git a/chrome/browser/touch_to_fill/android/internal/java/strings/translations/android_touch_to_fill_strings_te.xtb b/chrome/browser/touch_to_fill/android/internal/java/strings/translations/android_touch_to_fill_strings_te.xtb
new file mode 100644
index 0000000..c35f476
--- /dev/null
+++ b/chrome/browser/touch_to_fill/android/internal/java/strings/translations/android_touch_to_fill_strings_te.xtb
@@ -0,0 +1 @@
+<?xml version="1.0" ?><!DOCTYPE translationbundle><translationbundle lang="te"></translationbundle>
diff --git a/chrome/browser/touch_to_fill/android/internal/java/strings/translations/android_touch_to_fill_strings_th.xtb b/chrome/browser/touch_to_fill/android/internal/java/strings/translations/android_touch_to_fill_strings_th.xtb
new file mode 100644
index 0000000..5d81291
--- /dev/null
+++ b/chrome/browser/touch_to_fill/android/internal/java/strings/translations/android_touch_to_fill_strings_th.xtb
@@ -0,0 +1 @@
+<?xml version="1.0" ?><!DOCTYPE translationbundle><translationbundle lang="th"></translationbundle>
diff --git a/chrome/browser/touch_to_fill/android/internal/java/strings/translations/android_touch_to_fill_strings_tr.xtb b/chrome/browser/touch_to_fill/android/internal/java/strings/translations/android_touch_to_fill_strings_tr.xtb
new file mode 100644
index 0000000..ead1d39
--- /dev/null
+++ b/chrome/browser/touch_to_fill/android/internal/java/strings/translations/android_touch_to_fill_strings_tr.xtb
@@ -0,0 +1 @@
+<?xml version="1.0" ?><!DOCTYPE translationbundle><translationbundle lang="tr"></translationbundle>
diff --git a/chrome/browser/touch_to_fill/android/internal/java/strings/translations/android_touch_to_fill_strings_uk.xtb b/chrome/browser/touch_to_fill/android/internal/java/strings/translations/android_touch_to_fill_strings_uk.xtb
new file mode 100644
index 0000000..29134e1
--- /dev/null
+++ b/chrome/browser/touch_to_fill/android/internal/java/strings/translations/android_touch_to_fill_strings_uk.xtb
@@ -0,0 +1 @@
+<?xml version="1.0" ?><!DOCTYPE translationbundle><translationbundle lang="uk"></translationbundle>
diff --git a/chrome/browser/touch_to_fill/android/internal/java/strings/translations/android_touch_to_fill_strings_vi.xtb b/chrome/browser/touch_to_fill/android/internal/java/strings/translations/android_touch_to_fill_strings_vi.xtb
new file mode 100644
index 0000000..d858f99
--- /dev/null
+++ b/chrome/browser/touch_to_fill/android/internal/java/strings/translations/android_touch_to_fill_strings_vi.xtb
@@ -0,0 +1 @@
+<?xml version="1.0" ?><!DOCTYPE translationbundle><translationbundle lang="vi"></translationbundle>
diff --git a/chrome/browser/touch_to_fill/android/internal/java/strings/translations/android_touch_to_fill_strings_zh-CN.xtb b/chrome/browser/touch_to_fill/android/internal/java/strings/translations/android_touch_to_fill_strings_zh-CN.xtb
new file mode 100644
index 0000000..effe01e
--- /dev/null
+++ b/chrome/browser/touch_to_fill/android/internal/java/strings/translations/android_touch_to_fill_strings_zh-CN.xtb
@@ -0,0 +1 @@
+<?xml version="1.0" ?><!DOCTYPE translationbundle><translationbundle lang="zh-CN"></translationbundle>
diff --git a/chrome/browser/touch_to_fill/android/internal/java/strings/translations/android_touch_to_fill_strings_zh-TW.xtb b/chrome/browser/touch_to_fill/android/internal/java/strings/translations/android_touch_to_fill_strings_zh-TW.xtb
new file mode 100644
index 0000000..cb82bea
--- /dev/null
+++ b/chrome/browser/touch_to_fill/android/internal/java/strings/translations/android_touch_to_fill_strings_zh-TW.xtb
@@ -0,0 +1 @@
+<?xml version="1.0" ?><!DOCTYPE translationbundle><translationbundle lang="zh-TW"></translationbundle>
diff --git a/chrome/browser/ui/BUILD.gn b/chrome/browser/ui/BUILD.gn
index ac801fb4..6c4a1c1 100644
--- a/chrome/browser/ui/BUILD.gn
+++ b/chrome/browser/ui/BUILD.gn
@@ -932,6 +932,7 @@
       "global_media_controls/media_toolbar_button_controller.h",
       "global_media_controls/media_toolbar_button_controller_delegate.cc",
       "global_media_controls/media_toolbar_button_controller_delegate.h",
+      "global_media_controls/media_toolbar_button_observer.h",
       "hats/hats_helper.cc",
       "hats/hats_helper.h",
       "hats/hats_service.cc",
diff --git a/chrome/browser/ui/extensions/terminal_system_app_menu_model_chromeos.cc b/chrome/browser/ui/extensions/terminal_system_app_menu_model_chromeos.cc
index 5b8f17c..468df351 100644
--- a/chrome/browser/ui/extensions/terminal_system_app_menu_model_chromeos.cc
+++ b/chrome/browser/ui/extensions/terminal_system_app_menu_model_chromeos.cc
@@ -20,21 +20,6 @@
 #include "url/third_party/mozilla/url_parse.h"
 #include "url/url_canon.h"
 
-namespace {
-
-static const base::NoDestructor<base::flat_map<int, std::string>> g_commands({
-    // Opens settings page.
-    {IDC_OPTIONS, "options"},
-    // Split the currently selected pane vertically.
-    {IDC_TERMINAL_SPLIT_VERTICAL, "splitv"},
-    // Split the currently selected pane horizontally.
-    {IDC_TERMINAL_SPLIT_HORIZONTAL, "splith"},
-    // Open the find dialog.
-    {IDC_FIND, "find"},
-});
-
-}  // namespace
-
 TerminalSystemAppMenuModel::TerminalSystemAppMenuModel(
     ui::AcceleratorProvider* provider,
     Browser* browser)
@@ -57,12 +42,23 @@
 
 void TerminalSystemAppMenuModel::ExecuteCommand(int command_id,
                                                 int event_flags) {
-  auto it = g_commands->find(command_id);
-  if (it == g_commands->end()) {
+  static const base::NoDestructor<base::flat_map<int, std::string>> kCommands({
+      // Opens settings page.
+      {IDC_OPTIONS, "options"},
+      // Split the currently selected pane vertically.
+      {IDC_TERMINAL_SPLIT_VERTICAL, "splitv"},
+      // Split the currently selected pane horizontally.
+      {IDC_TERMINAL_SPLIT_HORIZONTAL, "splith"},
+      // Open the find dialog.
+      {IDC_FIND, "find"},
+  });
+
+  auto it = kCommands->find(command_id);
+  if (it == kCommands->end()) {
     NOTREACHED() << "Unknown command " << command_id;
     return;
   }
-  std::string fragment = it->second;
+  const std::string& fragment = it->second;
   url::Replacements<char> replacements;
   replacements.SetRef(fragment.c_str(), url::Component(0, fragment.size()));
   NavigateParams params(
diff --git a/chrome/browser/ui/global_media_controls/media_toolbar_button_observer.h b/chrome/browser/ui/global_media_controls/media_toolbar_button_observer.h
new file mode 100644
index 0000000..8544b58
--- /dev/null
+++ b/chrome/browser/ui/global_media_controls/media_toolbar_button_observer.h
@@ -0,0 +1,31 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_UI_GLOBAL_MEDIA_CONTROLS_MEDIA_TOOLBAR_BUTTON_OBSERVER_H_
+#define CHROME_BROWSER_UI_GLOBAL_MEDIA_CONTROLS_MEDIA_TOOLBAR_BUTTON_OBSERVER_H_
+
+#include "base/observer_list_types.h"
+
+class MediaToolbarButtonObserver : public base::CheckedObserver {
+ public:
+  // Called when the media dialog is opened.
+  virtual void OnMediaDialogOpened() = 0;
+
+  // Called when the toolbar button is shown.
+  virtual void OnMediaButtonShown() = 0;
+
+  // Called when the toolbar button is hidden.
+  virtual void OnMediaButtonHidden() = 0;
+
+  // Called when the toolbar button is enabled.
+  virtual void OnMediaButtonEnabled() = 0;
+
+  // Called when the toolbar button is disabled.
+  virtual void OnMediaButtonDisabled() = 0;
+
+ protected:
+  ~MediaToolbarButtonObserver() override = default;
+};
+
+#endif  // CHROME_BROWSER_UI_GLOBAL_MEDIA_CONTROLS_MEDIA_TOOLBAR_BUTTON_OBSERVER_H_
diff --git a/chrome/browser/ui/in_product_help/global_media_controls_in_product_help.cc b/chrome/browser/ui/in_product_help/global_media_controls_in_product_help.cc
index eca3448..d71f10d 100644
--- a/chrome/browser/ui/in_product_help/global_media_controls_in_product_help.cc
+++ b/chrome/browser/ui/in_product_help/global_media_controls_in_product_help.cc
@@ -57,7 +57,17 @@
     StopListening();
 }
 
-void GlobalMediaControlsInProductHelp::ToolbarIconEnabled() {
+void GlobalMediaControlsInProductHelp::OnMediaDialogOpened() {
+  GetTracker()->NotifyEvent(
+      feature_engagement::events::kGlobalMediaControlsOpened);
+}
+
+void GlobalMediaControlsInProductHelp::OnMediaButtonHidden() {
+  // Media has stopped playing. Stop watching for active tab changes.
+  StopListening();
+}
+
+void GlobalMediaControlsInProductHelp::OnMediaButtonEnabled() {
   // If the current window isn't for our profile, then the playing media that
   // caused the toolbar icon to be enabled is definitely not in the foreground
   // tab (since it can't be in this window). Therefore, we don't assume the
@@ -82,16 +92,11 @@
   observed_tab_strip_model_->AddObserver(this);
 }
 
-void GlobalMediaControlsInProductHelp::ToolbarIconDisabled() {
+void GlobalMediaControlsInProductHelp::OnMediaButtonDisabled() {
   // Media has stopped playing. Stop watching for active tab changes.
   StopListening();
 }
 
-void GlobalMediaControlsInProductHelp::GlobalMediaControlsOpened() {
-  GetTracker()->NotifyEvent(
-      feature_engagement::events::kGlobalMediaControlsOpened);
-}
-
 void GlobalMediaControlsInProductHelp::HelpDismissed() {
   GetTracker()->Dismissed(feature_engagement::kIPHGlobalMediaControlsFeature);
 }
diff --git a/chrome/browser/ui/in_product_help/global_media_controls_in_product_help.h b/chrome/browser/ui/in_product_help/global_media_controls_in_product_help.h
index 1f7d2d2..f0d7092f 100644
--- a/chrome/browser/ui/in_product_help/global_media_controls_in_product_help.h
+++ b/chrome/browser/ui/in_product_help/global_media_controls_in_product_help.h
@@ -7,6 +7,7 @@
 
 #include "base/macros.h"
 #include "chrome/browser/ui/browser_list_observer.h"
+#include "chrome/browser/ui/global_media_controls/media_toolbar_button_observer.h"
 #include "chrome/browser/ui/tabs/tab_strip_model_observer.h"
 #include "components/keyed_service/core/keyed_service.h"
 
@@ -24,7 +25,8 @@
 // interesting user actions.
 class GlobalMediaControlsInProductHelp : public KeyedService,
                                          public TabStripModelObserver,
-                                         public BrowserListObserver {
+                                         public BrowserListObserver,
+                                         public MediaToolbarButtonObserver {
  public:
   explicit GlobalMediaControlsInProductHelp(Profile* profile);
   ~GlobalMediaControlsInProductHelp() override;
@@ -39,14 +41,12 @@
   void OnBrowserClosing(Browser* browser) override;
   void OnBrowserSetLastActive(Browser* browser) override;
 
-  // Called when the Global Media Controls toolbar icon is enabled and shown.
-  void ToolbarIconEnabled();
-
-  // Called when the Global Media Controls toolbar icon is disabled or hidden.
-  void ToolbarIconDisabled();
-
-  // Called when the Global Media Controls dialog is opened.
-  void GlobalMediaControlsOpened();
+  // MediaToolbarButtonObserver implementation.
+  void OnMediaDialogOpened() override;
+  void OnMediaButtonShown() override {}
+  void OnMediaButtonHidden() override;
+  void OnMediaButtonEnabled() override;
+  void OnMediaButtonDisabled() override;
 
   // Must be called when IPH promo finishes showing, whether by use of the
   // feature or by timing out.
diff --git a/chrome/browser/ui/in_product_help/global_media_controls_in_product_help_unittest.cc b/chrome/browser/ui/in_product_help/global_media_controls_in_product_help_unittest.cc
index 92eac4e..c381b57 100644
--- a/chrome/browser/ui/in_product_help/global_media_controls_in_product_help_unittest.cc
+++ b/chrome/browser/ui/in_product_help/global_media_controls_in_product_help_unittest.cc
@@ -75,7 +75,7 @@
   // Start playing media in a tab.
   BrowserList::SetLastActive(browser());
   AddTab(browser(), GURL("chrome://blank"));
-  gmc_iph.ToolbarIconEnabled();
+  gmc_iph.OnMediaButtonEnabled();
 
   // Open a new foreground tab.
   AddTab(browser(), GURL("chrome://blank"));
@@ -118,12 +118,12 @@
   // Start playing media.
   BrowserList::SetLastActive(browser());
   AddTab(browser(), GURL("chrome://blank"));
-  gmc_iph.ToolbarIconEnabled();
+  gmc_iph.OnMediaButtonEnabled();
 
   // Switch to the other profile and play media.
   BrowserList::SetLastActive(alt_browser.get());
   AddTab(alt_browser.get(), GURL("chrome://blank"));
-  alt_gmc_iph.ToolbarIconEnabled();
+  alt_gmc_iph.OnMediaButtonEnabled();
 
   // Open a new foreground tab to the other profile.
   AddTab(alt_browser.get(), GURL("chrome://blank"));
@@ -142,10 +142,10 @@
   // Start playing media in a tab.
   BrowserList::SetLastActive(browser());
   AddTab(browser(), GURL("chrome://blank"));
-  gmc_iph.ToolbarIconEnabled();
+  gmc_iph.OnMediaButtonEnabled();
 
   // Stop playing media.
-  gmc_iph.ToolbarIconDisabled();
+  gmc_iph.OnMediaButtonDisabled();
 
   // Open a new foreground tab.
   AddTab(browser(), GURL("chrome://blank"));
diff --git a/chrome/browser/ui/libgtkui/BUILD.gn b/chrome/browser/ui/libgtkui/BUILD.gn
index 4f71e4c..7b73cd3 100644
--- a/chrome/browser/ui/libgtkui/BUILD.gn
+++ b/chrome/browser/ui/libgtkui/BUILD.gn
@@ -12,12 +12,6 @@
 
 jumbo_component("libgtkui") {
   sources = [
-    "app_indicator_icon.cc",
-    "app_indicator_icon.h",
-    "app_indicator_icon_menu.cc",
-    "app_indicator_icon_menu.h",
-    "gtk_background_painter.cc",
-    "gtk_background_painter.h",
     "gtk_key_bindings_handler.cc",
     "gtk_key_bindings_handler.h",
     "gtk_ui.cc",
@@ -27,8 +21,6 @@
     "input_method_context_impl_gtk.cc",
     "input_method_context_impl_gtk.h",
     "libgtkui_export.h",
-    "menu_util.cc",
-    "menu_util.h",
     "native_theme_gtk.cc",
     "native_theme_gtk.h",
     "nav_button_provider_gtk.cc",
@@ -51,13 +43,6 @@
     "unity_service.h",
   ]
 
-  if (gtk_version <= 3) {
-    sources += [
-      "gtk_status_icon.cc",
-      "gtk_status_icon.h",
-    ]
-  }
-
   configs += [
     "//build/config/linux/pangocairo",
     "//build/config/linux:x11",
diff --git a/chrome/browser/ui/libgtkui/app_indicator_icon.cc b/chrome/browser/ui/libgtkui/app_indicator_icon.cc
deleted file mode 100644
index 90c366ea..0000000
--- a/chrome/browser/ui/libgtkui/app_indicator_icon.cc
+++ /dev/null
@@ -1,372 +0,0 @@
-// Copyright 2013 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "chrome/browser/ui/libgtkui/app_indicator_icon.h"
-
-#include <dlfcn.h>
-#include <gtk/gtk.h>
-
-#include "base/bind.h"
-#include "base/environment.h"
-#include "base/files/file_util.h"
-#include "base/hash/md5.h"
-#include "base/memory/ref_counted_memory.h"
-#include "base/strings/string_number_conversions.h"
-#include "base/strings/stringprintf.h"
-#include "base/strings/utf_string_conversions.h"
-#include "base/task/post_task.h"
-#include "chrome/browser/ui/libgtkui/app_indicator_icon_menu.h"
-#include "content/public/browser/browser_thread.h"
-#include "third_party/skia/include/core/SkBitmap.h"
-#include "third_party/skia/include/core/SkCanvas.h"
-#include "ui/base/models/menu_model.h"
-#include "ui/gfx/codec/png_codec.h"
-#include "ui/gfx/image/image.h"
-#include "ui/gfx/image/image_skia.h"
-
-namespace {
-
-typedef enum {
-  APP_INDICATOR_CATEGORY_APPLICATION_STATUS,
-  APP_INDICATOR_CATEGORY_COMMUNICATIONS,
-  APP_INDICATOR_CATEGORY_SYSTEM_SERVICES,
-  APP_INDICATOR_CATEGORY_HARDWARE,
-  APP_INDICATOR_CATEGORY_OTHER
-} AppIndicatorCategory;
-
-typedef enum {
-  APP_INDICATOR_STATUS_PASSIVE,
-  APP_INDICATOR_STATUS_ACTIVE,
-  APP_INDICATOR_STATUS_ATTENTION
-} AppIndicatorStatus;
-
-typedef AppIndicator* (*app_indicator_new_func)(const gchar* id,
-                                                const gchar* icon_name,
-                                                AppIndicatorCategory category);
-
-typedef AppIndicator* (*app_indicator_new_with_path_func)(
-    const gchar* id,
-    const gchar* icon_name,
-    AppIndicatorCategory category,
-    const gchar* icon_theme_path);
-
-typedef void (*app_indicator_set_status_func)(AppIndicator* self,
-                                              AppIndicatorStatus status);
-
-typedef void (*app_indicator_set_attention_icon_full_func)(
-    AppIndicator* self,
-    const gchar* icon_name,
-    const gchar* icon_desc);
-
-typedef void (*app_indicator_set_menu_func)(AppIndicator* self, GtkMenu* menu);
-
-typedef void (*app_indicator_set_icon_full_func)(AppIndicator* self,
-                                                 const gchar* icon_name,
-                                                 const gchar* icon_desc);
-
-typedef void (*app_indicator_set_icon_theme_path_func)(
-    AppIndicator* self,
-    const gchar* icon_theme_path);
-
-bool g_attempted_load = false;
-bool g_opened = false;
-
-// Retrieved functions from libappindicator.
-app_indicator_new_func app_indicator_new = nullptr;
-app_indicator_new_with_path_func app_indicator_new_with_path = nullptr;
-app_indicator_set_status_func app_indicator_set_status = nullptr;
-app_indicator_set_attention_icon_full_func
-    app_indicator_set_attention_icon_full = nullptr;
-app_indicator_set_menu_func app_indicator_set_menu = nullptr;
-app_indicator_set_icon_full_func app_indicator_set_icon_full = nullptr;
-app_indicator_set_icon_theme_path_func app_indicator_set_icon_theme_path =
-    nullptr;
-
-void EnsureLibAppIndicatorLoaded() {
-  if (g_attempted_load)
-    return;
-
-  g_attempted_load = true;
-
-  std::string lib_name =
-      "libappindicator" + base::NumberToString(GTK_MAJOR_VERSION) + ".so";
-  void* indicator_lib = dlopen(lib_name.c_str(), RTLD_LAZY);
-
-  if (!indicator_lib) {
-    lib_name += ".1";
-    indicator_lib = dlopen(lib_name.c_str(), RTLD_LAZY);
-  }
-
-  if (!indicator_lib)
-    return;
-
-  g_opened = true;
-
-  app_indicator_new = reinterpret_cast<app_indicator_new_func>(
-      dlsym(indicator_lib, "app_indicator_new"));
-
-  app_indicator_new_with_path =
-      reinterpret_cast<app_indicator_new_with_path_func>(
-          dlsym(indicator_lib, "app_indicator_new_with_path"));
-
-  app_indicator_set_status = reinterpret_cast<app_indicator_set_status_func>(
-      dlsym(indicator_lib, "app_indicator_set_status"));
-
-  app_indicator_set_attention_icon_full =
-      reinterpret_cast<app_indicator_set_attention_icon_full_func>(
-          dlsym(indicator_lib, "app_indicator_set_attention_icon_full"));
-
-  app_indicator_set_menu = reinterpret_cast<app_indicator_set_menu_func>(
-      dlsym(indicator_lib, "app_indicator_set_menu"));
-
-  app_indicator_set_icon_full =
-      reinterpret_cast<app_indicator_set_icon_full_func>(
-          dlsym(indicator_lib, "app_indicator_set_icon_full"));
-
-  app_indicator_set_icon_theme_path =
-      reinterpret_cast<app_indicator_set_icon_theme_path_func>(
-          dlsym(indicator_lib, "app_indicator_set_icon_theme_path"));
-}
-
-// Writes |bitmap| to a file at |path|. Returns true if successful.
-bool WriteFile(const base::FilePath& path, const SkBitmap& bitmap) {
-  std::vector<unsigned char> png_data;
-  if (!gfx::PNGCodec::EncodeBGRASkBitmap(bitmap, false, &png_data))
-    return false;
-  int bytes_written = base::WriteFile(
-      path, reinterpret_cast<char*>(&png_data[0]), png_data.size());
-  return (bytes_written == static_cast<int>(png_data.size()));
-}
-
-void DeleteTempDirectory(const base::FilePath& dir_path) {
-  if (dir_path.empty())
-    return;
-  base::DeleteFile(dir_path, true);
-}
-
-}  // namespace
-
-namespace libgtkui {
-
-AppIndicatorIcon::AppIndicatorIcon(std::string id,
-                                   const gfx::ImageSkia& image,
-                                   const base::string16& tool_tip)
-    : id_(id), icon_(nullptr), menu_model_(nullptr), icon_change_count_(0) {
-  std::unique_ptr<base::Environment> env(base::Environment::Create());
-  desktop_env_ = base::nix::GetDesktopEnvironment(env.get());
-
-  EnsureLibAppIndicatorLoaded();
-  tool_tip_ = base::UTF16ToUTF8(tool_tip);
-  SetIcon(image);
-}
-AppIndicatorIcon::~AppIndicatorIcon() {
-  if (icon_) {
-    app_indicator_set_status(icon_, APP_INDICATOR_STATUS_PASSIVE);
-    g_object_unref(icon_);
-    base::PostTask(
-        FROM_HERE,
-        {base::ThreadPool(), base::MayBlock(), base::TaskPriority::BEST_EFFORT},
-        base::BindOnce(&DeleteTempDirectory, temp_dir_));
-  }
-}
-
-// static
-bool AppIndicatorIcon::CouldOpen() {
-  EnsureLibAppIndicatorLoaded();
-  return g_opened;
-}
-
-void AppIndicatorIcon::SetIcon(const gfx::ImageSkia& image) {
-  if (!g_opened)
-    return;
-
-  ++icon_change_count_;
-
-  // Copy the bitmap because it may be freed by the time it's accessed in
-  // another thread.
-  SkBitmap safe_bitmap = *image.bitmap();
-
-  const base::TaskTraits kTraits = {
-      base::ThreadPool(), base::MayBlock(), base::TaskPriority::USER_VISIBLE,
-      base::TaskShutdownBehavior::SKIP_ON_SHUTDOWN};
-
-  if (desktop_env_ == base::nix::DESKTOP_ENVIRONMENT_KDE4 ||
-      desktop_env_ == base::nix::DESKTOP_ENVIRONMENT_KDE5) {
-    base::PostTaskAndReplyWithResult(
-        FROM_HERE, kTraits,
-        base::Bind(AppIndicatorIcon::WriteKDE4TempImageOnWorkerThread,
-                   safe_bitmap, temp_dir_),
-        base::Bind(&AppIndicatorIcon::SetImageFromFile,
-                   weak_factory_.GetWeakPtr()));
-  } else {
-    base::PostTaskAndReplyWithResult(
-        FROM_HERE, kTraits,
-        base::Bind(AppIndicatorIcon::WriteUnityTempImageOnWorkerThread,
-                   safe_bitmap, icon_change_count_, id_),
-        base::Bind(&AppIndicatorIcon::SetImageFromFile,
-                   weak_factory_.GetWeakPtr()));
-  }
-}
-
-void AppIndicatorIcon::SetToolTip(const base::string16& tool_tip) {
-  DCHECK(!tool_tip_.empty());
-  tool_tip_ = base::UTF16ToUTF8(tool_tip);
-  UpdateClickActionReplacementMenuItem();
-}
-
-void AppIndicatorIcon::UpdatePlatformContextMenu(ui::MenuModel* model) {
-  if (!g_opened)
-    return;
-
-  menu_model_ = model;
-
-  // The icon is created asynchronously so it might not exist when the menu is
-  // set.
-  if (icon_)
-    SetMenu();
-}
-
-void AppIndicatorIcon::RefreshPlatformContextMenu() {
-  menu_->Refresh();
-}
-
-// static
-AppIndicatorIcon::SetImageFromFileParams
-AppIndicatorIcon::WriteKDE4TempImageOnWorkerThread(
-    const SkBitmap& bitmap,
-    const base::FilePath& existing_temp_dir) {
-  base::FilePath temp_dir = existing_temp_dir;
-  if (temp_dir.empty() &&
-      !base::CreateNewTempDirectory(base::FilePath::StringType(), &temp_dir)) {
-    LOG(WARNING) << "Could not create temporary directory";
-    return SetImageFromFileParams();
-  }
-
-  base::FilePath icon_theme_path = temp_dir.AppendASCII("icons");
-
-  // On KDE4, an image located in a directory ending with
-  // "icons/hicolor/22x22/apps" can be used as the app indicator image because
-  // "/usr/share/icons/hicolor/22x22/apps" exists.
-  base::FilePath image_dir =
-      icon_theme_path.AppendASCII("hicolor").AppendASCII("22x22").AppendASCII(
-          "apps");
-
-  if (!base::CreateDirectory(image_dir))
-    return SetImageFromFileParams();
-
-  // On KDE4, the name of the image file for each different looking bitmap must
-  // be unique. It must also be unique across runs of Chrome.
-  std::vector<unsigned char> bitmap_png_data;
-  if (!gfx::PNGCodec::EncodeBGRASkBitmap(bitmap, false, &bitmap_png_data)) {
-    LOG(WARNING) << "Could not encode icon";
-    return SetImageFromFileParams();
-  }
-  base::MD5Digest digest;
-  base::MD5Sum(reinterpret_cast<char*>(&bitmap_png_data[0]),
-               bitmap_png_data.size(), &digest);
-  std::string icon_name = base::StringPrintf(
-      "chrome_app_indicator2_%s", base::MD5DigestToBase16(digest).c_str());
-
-  // If |bitmap| is smaller than 22x22, KDE does some really ugly resizing.
-  // Pad |bitmap| with transparent pixels to make it 22x22.
-  const int kMinimalSize = 22;
-  SkBitmap scaled_bitmap;
-  scaled_bitmap.allocN32Pixels(std::max(bitmap.width(), kMinimalSize),
-                               std::max(bitmap.height(), kMinimalSize));
-  scaled_bitmap.eraseARGB(0, 0, 0, 0);
-  SkCanvas canvas(scaled_bitmap);
-  canvas.drawBitmap(bitmap, (scaled_bitmap.width() - bitmap.width()) / 2,
-                    (scaled_bitmap.height() - bitmap.height()) / 2);
-
-  base::FilePath image_path = image_dir.Append(icon_name + ".png");
-  if (!WriteFile(image_path, scaled_bitmap))
-    return SetImageFromFileParams();
-
-  SetImageFromFileParams params;
-  params.parent_temp_dir = temp_dir;
-  params.icon_theme_path = icon_theme_path.value();
-  params.icon_name = icon_name;
-  return params;
-}
-
-// static
-AppIndicatorIcon::SetImageFromFileParams
-AppIndicatorIcon::WriteUnityTempImageOnWorkerThread(const SkBitmap& bitmap,
-                                                    int icon_change_count,
-                                                    const std::string& id) {
-  // Create a new temporary directory for each image on Unity since using a
-  // single temporary directory seems to have issues when changing icons in
-  // quick succession.
-  base::FilePath temp_dir;
-  if (!base::CreateNewTempDirectory(base::FilePath::StringType(), &temp_dir)) {
-    LOG(WARNING) << "Could not create temporary directory";
-    return SetImageFromFileParams();
-  }
-
-  std::string icon_name =
-      base::StringPrintf("%s_%d", id.c_str(), icon_change_count);
-  base::FilePath image_path = temp_dir.Append(icon_name + ".png");
-  SetImageFromFileParams params;
-  if (WriteFile(image_path, bitmap)) {
-    params.parent_temp_dir = temp_dir;
-    params.icon_theme_path = temp_dir.value();
-    params.icon_name = icon_name;
-  }
-  return params;
-}
-
-void AppIndicatorIcon::SetImageFromFile(const SetImageFromFileParams& params) {
-  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
-  if (params.icon_theme_path.empty())
-    return;
-
-  if (!icon_) {
-    icon_ =
-        app_indicator_new_with_path(id_.c_str(),
-                                    params.icon_name.c_str(),
-                                    APP_INDICATOR_CATEGORY_APPLICATION_STATUS,
-                                    params.icon_theme_path.c_str());
-    app_indicator_set_status(icon_, APP_INDICATOR_STATUS_ACTIVE);
-    SetMenu();
-  } else {
-    app_indicator_set_icon_theme_path(icon_, params.icon_theme_path.c_str());
-    app_indicator_set_icon_full(icon_, params.icon_name.c_str(), "icon");
-  }
-
-  if (temp_dir_ != params.parent_temp_dir) {
-    base::PostTask(
-        FROM_HERE,
-        {base::ThreadPool(), base::MayBlock(), base::TaskPriority::BEST_EFFORT},
-        base::BindOnce(&DeleteTempDirectory, temp_dir_));
-    temp_dir_ = params.parent_temp_dir;
-  }
-}
-
-void AppIndicatorIcon::SetMenu() {
-  menu_ = std::make_unique<AppIndicatorIconMenu>(menu_model_);
-  UpdateClickActionReplacementMenuItem();
-  app_indicator_set_menu(icon_, menu_->GetGtkMenu());
-}
-
-void AppIndicatorIcon::UpdateClickActionReplacementMenuItem() {
-  // The menu may not have been created yet.
-  if (!menu_.get())
-    return;
-
-  if (!delegate()->HasClickAction() && menu_model_)
-    return;
-
-  DCHECK(!tool_tip_.empty());
-  menu_->UpdateClickActionReplacementMenuItem(
-      tool_tip_.c_str(),
-      base::Bind(&AppIndicatorIcon::OnClickActionReplacementMenuItemActivated,
-                 base::Unretained(this)));
-}
-
-void AppIndicatorIcon::OnClickActionReplacementMenuItemActivated() {
-  if (delegate())
-    delegate()->OnClick();
-}
-
-}  // namespace libgtkui
diff --git a/chrome/browser/ui/libgtkui/app_indicator_icon.h b/chrome/browser/ui/libgtkui/app_indicator_icon.h
deleted file mode 100644
index 327879a..0000000
--- a/chrome/browser/ui/libgtkui/app_indicator_icon.h
+++ /dev/null
@@ -1,110 +0,0 @@
-// Copyright 2013 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CHROME_BROWSER_UI_LIBGTKUI_APP_INDICATOR_ICON_H_
-#define CHROME_BROWSER_UI_LIBGTKUI_APP_INDICATOR_ICON_H_
-
-#include <memory>
-
-#include "base/files/file_path.h"
-#include "base/macros.h"
-#include "base/memory/weak_ptr.h"
-#include "base/nix/xdg_util.h"
-#include "ui/base/glib/glib_signal.h"
-#include "ui/views/linux_ui/status_icon_linux.h"
-
-typedef struct _AppIndicator AppIndicator;
-typedef struct _GtkWidget GtkWidget;
-
-class SkBitmap;
-
-namespace gfx {
-class ImageSkia;
-}
-
-namespace ui {
-class MenuModel;
-}
-
-namespace libgtkui {
-class AppIndicatorIconMenu;
-
-// Status icon implementation which uses libappindicator.
-class AppIndicatorIcon : public views::StatusIconLinux {
- public:
-  // The id uniquely identifies the new status icon from other chrome status
-  // icons.
-  AppIndicatorIcon(std::string id,
-                   const gfx::ImageSkia& image,
-                   const base::string16& tool_tip);
-  ~AppIndicatorIcon() override;
-
-  // Indicates whether libappindicator so could be opened.
-  static bool CouldOpen();
-
-  // Overridden from views::StatusIconLinux:
-  void SetIcon(const gfx::ImageSkia& image) override;
-  void SetToolTip(const base::string16& tool_tip) override;
-  void UpdatePlatformContextMenu(ui::MenuModel* menu) override;
-  void RefreshPlatformContextMenu() override;
-
- private:
-  struct SetImageFromFileParams {
-    // The temporary directory in which the icon(s) were written.
-    base::FilePath parent_temp_dir;
-
-    // The icon theme path to pass to libappindicator.
-    std::string icon_theme_path;
-
-    // The icon name to pass to libappindicator.
-    std::string icon_name;
-  };
-
-  // Writes |bitmap| to a temporary directory on a worker thread. The temporary
-  // directory is selected based on KDE's quirks.
-  static SetImageFromFileParams WriteKDE4TempImageOnWorkerThread(
-      const SkBitmap& bitmap,
-      const base::FilePath& existing_temp_dir);
-
-  // Writes |bitmap| to a temporary directory on a worker thread. The temporary
-  // directory is selected based on Unity's quirks.
-  static SetImageFromFileParams WriteUnityTempImageOnWorkerThread(
-      const SkBitmap& bitmap,
-      int icon_change_count,
-      const std::string& id);
-
-  void SetImageFromFile(const SetImageFromFileParams& params);
-  void SetMenu();
-
-  // Sets a menu item at the top of the menu as a replacement for the status
-  // icon click action. Clicking on this menu item should simulate a status icon
-  // click by despatching a click event.
-  void UpdateClickActionReplacementMenuItem();
-
-  // Callback for when the status icon click replacement menu item is activated.
-  void OnClickActionReplacementMenuItemActivated();
-
-  std::string id_;
-  std::string tool_tip_;
-
-  // Used to select KDE or Unity for image setting.
-  base::nix::DesktopEnvironment desktop_env_;
-
-  // Gtk status icon wrapper
-  AppIndicator* icon_;
-
-  std::unique_ptr<AppIndicatorIconMenu> menu_;
-  ui::MenuModel* menu_model_;
-
-  base::FilePath temp_dir_;
-  int icon_change_count_;
-
-  base::WeakPtrFactory<AppIndicatorIcon> weak_factory_{this};
-
-  DISALLOW_COPY_AND_ASSIGN(AppIndicatorIcon);
-};
-
-}  // namespace libgtkui
-
-#endif  // CHROME_BROWSER_UI_LIBGTKUI_APP_INDICATOR_ICON_H_
diff --git a/chrome/browser/ui/libgtkui/app_indicator_icon_menu.cc b/chrome/browser/ui/libgtkui/app_indicator_icon_menu.cc
deleted file mode 100644
index 1d3a0da..0000000
--- a/chrome/browser/ui/libgtkui/app_indicator_icon_menu.cc
+++ /dev/null
@@ -1,123 +0,0 @@
-// Copyright 2014 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "chrome/browser/ui/libgtkui/app_indicator_icon_menu.h"
-
-#include <gtk/gtk.h>
-
-#include "base/bind.h"
-#include "base/debug/leak_annotations.h"
-#include "chrome/browser/ui/libgtkui/menu_util.h"
-#include "ui/base/models/menu_model.h"
-
-namespace libgtkui {
-
-AppIndicatorIconMenu::AppIndicatorIconMenu(ui::MenuModel* model)
-    : menu_model_(model),
-      click_action_replacement_menu_item_added_(false),
-      gtk_menu_(nullptr),
-      block_activation_(false) {
-  {
-    ANNOTATE_SCOPED_MEMORY_LEAK; // http://crbug.com/378770
-    gtk_menu_ = gtk_menu_new();
-  }
-  g_object_ref_sink(gtk_menu_);
-  if (menu_model_) {
-    BuildSubmenuFromModel(menu_model_,
-                          gtk_menu_,
-                          G_CALLBACK(OnMenuItemActivatedThunk),
-                          &block_activation_,
-                          this);
-    Refresh();
-  }
-}
-
-AppIndicatorIconMenu::~AppIndicatorIconMenu() {
-  gtk_widget_destroy(gtk_menu_);
-  g_object_unref(gtk_menu_);
-}
-
-void AppIndicatorIconMenu::UpdateClickActionReplacementMenuItem(
-    const char* label,
-    const base::Closure& callback) {
-  click_action_replacement_callback_ = callback;
-
-  if (click_action_replacement_menu_item_added_) {
-    GList* children = gtk_container_get_children(GTK_CONTAINER(gtk_menu_));
-    for (GList* child = children; child; child = g_list_next(child)) {
-      if (g_object_get_data(G_OBJECT(child->data), "click-action-item") !=
-          nullptr) {
-        gtk_menu_item_set_label(GTK_MENU_ITEM(child->data), label);
-        break;
-      }
-    }
-    g_list_free(children);
-  } else {
-    click_action_replacement_menu_item_added_ = true;
-
-    // If |menu_model_| is non empty, add a separator to separate the
-    // "click action replacement menu item" from the other menu items.
-    if (menu_model_ && menu_model_->GetItemCount() > 0) {
-      GtkWidget* menu_item = gtk_separator_menu_item_new();
-      gtk_widget_show(menu_item);
-      gtk_menu_shell_prepend(GTK_MENU_SHELL(gtk_menu_), menu_item);
-    }
-
-    GtkWidget* menu_item = gtk_menu_item_new_with_mnemonic(label);
-    g_object_set_data(
-        G_OBJECT(menu_item), "click-action-item", GINT_TO_POINTER(1));
-    g_signal_connect(menu_item,
-                     "activate",
-                     G_CALLBACK(OnClickActionReplacementMenuItemActivatedThunk),
-                     this);
-    gtk_widget_show(menu_item);
-    gtk_menu_shell_prepend(GTK_MENU_SHELL(gtk_menu_), menu_item);
-  }
-}
-
-void AppIndicatorIconMenu::Refresh() {
-  gtk_container_foreach(
-      GTK_CONTAINER(gtk_menu_), SetMenuItemInfo, &block_activation_);
-}
-
-GtkMenu* AppIndicatorIconMenu::GetGtkMenu() {
-  return GTK_MENU(gtk_menu_);
-}
-
-
-void AppIndicatorIconMenu::OnClickActionReplacementMenuItemActivated(
-    GtkWidget* menu_item) {
-  click_action_replacement_callback_.Run();
-}
-
-void AppIndicatorIconMenu::OnMenuItemActivated(GtkWidget* menu_item) {
-  if (block_activation_)
-    return;
-
-  ui::MenuModel* model = ModelForMenuItem(GTK_MENU_ITEM(menu_item));
-  if (!model) {
-    // There won't be a model for "native" submenus like the "Input Methods"
-    // context menu. We don't need to handle activation messages for submenus
-    // anyway, so we can just return here.
-    DCHECK(gtk_menu_item_get_submenu(GTK_MENU_ITEM(menu_item)));
-    return;
-  }
-
-  // The activate signal is sent to radio items as they get deselected;
-  // ignore it in this case.
-  if (GTK_IS_RADIO_MENU_ITEM(menu_item) &&
-      !gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(menu_item))) {
-    return;
-  }
-
-  int id;
-  if (!GetMenuItemID(menu_item, &id))
-    return;
-
-  // The menu item can still be activated by hotkeys even if it is disabled.
-  if (model->IsEnabledAt(id))
-    ExecuteCommand(model, id);
-}
-
-}  // namespace libgtkui
diff --git a/chrome/browser/ui/libgtkui/app_indicator_icon_menu.h b/chrome/browser/ui/libgtkui/app_indicator_icon_menu.h
deleted file mode 100644
index aecee69..0000000
--- a/chrome/browser/ui/libgtkui/app_indicator_icon_menu.h
+++ /dev/null
@@ -1,71 +0,0 @@
-// Copyright 2014 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CHROME_BROWSER_UI_LIBGTKUI_APP_INDICATOR_ICON_MENU_H_
-#define CHROME_BROWSER_UI_LIBGTKUI_APP_INDICATOR_ICON_MENU_H_
-
-#include "base/callback.h"
-#include "base/macros.h"
-#include "ui/base/glib/glib_signal.h"
-
-typedef struct _GtkMenu GtkMenu;
-typedef struct _GtkWidget GtkWidget;
-
-namespace ui {
-class MenuModel;
-}
-
-namespace libgtkui {
-
-// The app indicator icon's menu.
-class AppIndicatorIconMenu {
- public:
-  explicit AppIndicatorIconMenu(ui::MenuModel* model);
-  virtual ~AppIndicatorIconMenu();
-
-  // Sets a menu item at the top of |gtk_menu_| as a replacement for the app
-  // indicator icon's click action. |callback| is called when the menu item
-  // is activated.
-  void UpdateClickActionReplacementMenuItem(const char* label,
-                                            const base::Closure& callback);
-
-  // Refreshes all the menu item labels and menu item checked/enabled states.
-  void Refresh();
-
-  GtkMenu* GetGtkMenu();
-
- private:
-  // Callback for when the "click action replacement" menu item is activated.
-  CHROMEG_CALLBACK_0(AppIndicatorIconMenu,
-                     void,
-                     OnClickActionReplacementMenuItemActivated,
-                     GtkWidget*);
-
-  // Callback for when a menu item is activated.
-  CHROMEG_CALLBACK_0(AppIndicatorIconMenu,
-                     void,
-                     OnMenuItemActivated,
-                     GtkWidget*);
-
-  // Not owned.
-  ui::MenuModel* menu_model_;
-
-  // Whether a "click action replacement" menu item has been added to the menu.
-  bool click_action_replacement_menu_item_added_;
-
-  // Called when the click action replacement menu item is activated. When a
-  // menu item from |menu_model_| is activated, MenuModel::ActivatedAt() is
-  // invoked and is assumed to do any necessary processing.
-  base::Closure click_action_replacement_callback_;
-
-  GtkWidget* gtk_menu_;
-
-  bool block_activation_;
-
-  DISALLOW_COPY_AND_ASSIGN(AppIndicatorIconMenu);
-};
-
-}  // namespace libgtkui
-
-#endif  // CHROME_BROWSER_UI_LIBGTKUI_APP_INDICATOR_ICON_MENU_H_
diff --git a/chrome/browser/ui/libgtkui/gtk_background_painter.cc b/chrome/browser/ui/libgtkui/gtk_background_painter.cc
deleted file mode 100644
index bc8c3b8..0000000
--- a/chrome/browser/ui/libgtkui/gtk_background_painter.cc
+++ /dev/null
@@ -1,62 +0,0 @@
-// Copyright 2017 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "chrome/browser/ui/libgtkui/gtk_background_painter.h"
-
-#include "ui/gfx/canvas.h"
-#include "ui/views/controls/button/button.h"
-#include "ui/views/view.h"
-#include "ui/views/widget/widget.h"
-
-namespace libgtkui {
-
-namespace {
-
-GtkStateFlags ButtonStateToStateFlags(views::Button::ButtonState state) {
-  switch (state) {
-    case views::Button::STATE_DISABLED:
-      return GTK_STATE_FLAG_INSENSITIVE;
-    case views::Button::STATE_HOVERED:
-      return GTK_STATE_FLAG_PRELIGHT;
-    case views::Button::STATE_NORMAL:
-      return GTK_STATE_FLAG_NORMAL;
-    case views::Button::STATE_PRESSED:
-      return static_cast<GtkStateFlags>(GTK_STATE_FLAG_PRELIGHT |
-                                        GTK_STATE_FLAG_ACTIVE);
-    default:
-      NOTREACHED();
-      return GTK_STATE_FLAG_NORMAL;
-  }
-}
-
-}  // namespace
-
-GtkBackgroundPainter::GtkBackgroundPainter(const views::Button* button,
-                                           ScopedStyleContext context)
-    : button_(button), context_(std::move(context)) {}
-
-GtkBackgroundPainter::~GtkBackgroundPainter() {}
-
-void GtkBackgroundPainter::Paint(gfx::Canvas* canvas, views::View* view) const {
-  float scale = canvas->image_scale();
-  SkBitmap bitmap;
-  bitmap.allocN32Pixels(scale * view->width(), scale * view->height());
-  bitmap.eraseColor(0);
-  CairoSurface surface(bitmap);
-  cairo_t* cr = surface.cairo();
-  gtk_style_context_set_state(context_, CalculateStateFlags());
-  cairo_scale(cr, scale, scale);
-  gtk_render_background(context_, cr, 0, 0, view->width(), view->height());
-  gtk_render_frame(context_, cr, 0, 0, view->width(), view->height());
-  canvas->DrawImageInt(gfx::ImageSkia(gfx::ImageSkiaRep(bitmap, scale)), 0, 0);
-}
-
-GtkStateFlags GtkBackgroundPainter::CalculateStateFlags() const {
-  GtkStateFlags state = ButtonStateToStateFlags(button_->state());
-  if (!button_->GetWidget()->IsActive())
-    state = static_cast<GtkStateFlags>(state | GTK_STATE_FLAG_BACKDROP);
-  return state;
-}
-
-}  // namespace libgtkui
diff --git a/chrome/browser/ui/libgtkui/gtk_background_painter.h b/chrome/browser/ui/libgtkui/gtk_background_painter.h
deleted file mode 100644
index 29544c7..0000000
--- a/chrome/browser/ui/libgtkui/gtk_background_painter.h
+++ /dev/null
@@ -1,39 +0,0 @@
-// Copyright 2017 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CHROME_BROWSER_UI_LIBGTKUI_GTK_BACKGROUND_PAINTER_H_
-#define CHROME_BROWSER_UI_LIBGTKUI_GTK_BACKGROUND_PAINTER_H_
-
-#include "base/macros.h"
-#include "chrome/browser/ui/libgtkui/gtk_util.h"
-#include "ui/views/background.h"
-
-namespace views {
-class Button;
-}
-
-namespace libgtkui {
-
-// A background that paints a button using GTK foreign drawing.  The
-// type and style of widget to be drawn is decided by a
-// GtkStyleContext.  Always renders a background and a frame.
-class GtkBackgroundPainter : public views::Background {
- public:
-  GtkBackgroundPainter(const views::Button* button, ScopedStyleContext context);
-  ~GtkBackgroundPainter() override;
-
-  void Paint(gfx::Canvas* canvas, views::View* view) const override;
-
- private:
-  GtkStateFlags CalculateStateFlags() const;
-
-  const views::Button* button_;
-  mutable ScopedStyleContext context_;
-
-  DISALLOW_COPY_AND_ASSIGN(GtkBackgroundPainter);
-};
-
-}  // namespace libgtkui
-
-#endif  // CHROME_BROWSER_UI_LIBGTKUI_GTK_BACKGROUND_PAINTER_H_
diff --git a/chrome/browser/ui/libgtkui/gtk_status_icon.cc b/chrome/browser/ui/libgtkui/gtk_status_icon.cc
deleted file mode 100644
index 19d0565..0000000
--- a/chrome/browser/ui/libgtkui/gtk_status_icon.cc
+++ /dev/null
@@ -1,82 +0,0 @@
-// Copyright 2014 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "chrome/browser/ui/libgtkui/gtk_status_icon.h"
-
-#include <gtk/gtk.h>
-
-#include "base/debug/leak_annotations.h"
-#include "base/strings/utf_string_conversions.h"
-#include "chrome/browser/ui/libgtkui/app_indicator_icon_menu.h"
-#include "chrome/browser/ui/libgtkui/skia_utils_gtk.h"
-#include "ui/base/models/menu_model.h"
-#include "ui/gfx/image/image_skia.h"
-
-G_GNUC_BEGIN_IGNORE_DEPRECATIONS
-
-namespace libgtkui {
-
-GtkStatusIcon::GtkStatusIcon(const gfx::ImageSkia& image,
-                             const base::string16& tool_tip) {
-  GdkPixbuf* pixbuf = GdkPixbufFromSkBitmap(*image.bitmap());
-  {
-    // GTK has a bug that leaks 384 bytes when creating a GtkStatusIcon.  It
-    // will not be fixed since the status icon was deprecated in version 3.14.
-    // Luckily, Chromium doesn't need to create a status icon very often, if at
-    // all.
-    ANNOTATE_SCOPED_MEMORY_LEAK;
-    gtk_status_icon_ = gtk_status_icon_new_from_pixbuf(pixbuf);
-  }
-  g_object_unref(pixbuf);
-
-  g_signal_connect(gtk_status_icon_, "activate", G_CALLBACK(OnClickThunk),
-                   this);
-  g_signal_connect(gtk_status_icon_, "popup_menu",
-                   G_CALLBACK(OnContextMenuRequestedThunk), this);
-  SetToolTip(tool_tip);
-}
-
-GtkStatusIcon::~GtkStatusIcon() {
-  gtk_status_icon_set_visible(gtk_status_icon_, FALSE);
-  g_object_unref(gtk_status_icon_);
-}
-
-void GtkStatusIcon::SetIcon(const gfx::ImageSkia& image) {
-  GdkPixbuf* pixbuf = GdkPixbufFromSkBitmap(*image.bitmap());
-  gtk_status_icon_set_from_pixbuf(gtk_status_icon_, pixbuf);
-  g_object_unref(pixbuf);
-}
-
-void GtkStatusIcon::SetToolTip(const base::string16& tool_tip) {
-  gtk_status_icon_set_tooltip_text(gtk_status_icon_,
-                                   base::UTF16ToUTF8(tool_tip).c_str());
-}
-
-void GtkStatusIcon::UpdatePlatformContextMenu(ui::MenuModel* model) {
-  menu_.reset();
-  if (model)
-    menu_ = std::make_unique<AppIndicatorIconMenu>(model);
-}
-
-void GtkStatusIcon::RefreshPlatformContextMenu() {
-  if (menu_.get())
-    menu_->Refresh();
-}
-
-void GtkStatusIcon::OnClick(GtkStatusIcon* status_icon) {
-  if (delegate())
-    delegate()->OnClick();
-}
-
-void GtkStatusIcon::OnContextMenuRequested(GtkStatusIcon* status_icon,
-                                           guint button,
-                                           guint32 activate_time) {
-  if (menu_.get()) {
-    gtk_menu_popup(menu_->GetGtkMenu(), nullptr, nullptr,
-                   gtk_status_icon_position_menu, gtk_status_icon_, button,
-                   activate_time);
-  }
-}
-
-}  // namespace libgtkui
diff --git a/chrome/browser/ui/libgtkui/gtk_status_icon.h b/chrome/browser/ui/libgtkui/gtk_status_icon.h
deleted file mode 100644
index e9526607..0000000
--- a/chrome/browser/ui/libgtkui/gtk_status_icon.h
+++ /dev/null
@@ -1,64 +0,0 @@
-// Copyright 2014 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CHROME_BROWSER_UI_LIBGTKUI_GTK_STATUS_ICON_H_
-#define CHROME_BROWSER_UI_LIBGTKUI_GTK_STATUS_ICON_H_
-
-// THIS FILE IS DEPRECATED (REPLACED BY StatusIconLinuxDbus) AND WILL SOON BE
-// REMOVED.
-
-#include <memory>
-
-#include "base/macros.h"
-#include "base/strings/string16.h"
-#include "ui/base/glib/glib_integers.h"
-#include "ui/base/glib/glib_signal.h"
-#include "ui/views/linux_ui/status_icon_linux.h"
-
-typedef struct _GtkStatusIcon GtkStatusIcon;
-
-namespace gfx {
-class ImageSkia;
-}
-
-namespace ui {
-class MenuModel;
-}
-
-namespace libgtkui {
-class AppIndicatorIconMenu;
-
-// Status icon implementation which uses the system tray X11 spec (via
-// GtkStatusIcon).
-class GtkStatusIcon : public views::StatusIconLinux {
- public:
-  GtkStatusIcon(const gfx::ImageSkia& image, const base::string16& tool_tip);
-  ~GtkStatusIcon() override;
-
-  // Overridden from views::StatusIconLinux:
-  void SetIcon(const gfx::ImageSkia& image) override;
-  void SetToolTip(const base::string16& tool_tip) override;
-  void UpdatePlatformContextMenu(ui::MenuModel* menu) override;
-  void RefreshPlatformContextMenu() override;
-
- private:
-  CHROMEG_CALLBACK_0(GtkStatusIcon, void, OnClick, GtkStatusIcon*);
-
-  CHROMEG_CALLBACK_2(GtkStatusIcon,
-                     void,
-                     OnContextMenuRequested,
-                     GtkStatusIcon*,
-                     guint,
-                     guint);
-
-  ::GtkStatusIcon* gtk_status_icon_;
-
-  std::unique_ptr<AppIndicatorIconMenu> menu_;
-
-  DISALLOW_COPY_AND_ASSIGN(GtkStatusIcon);
-};
-
-}  // namespace libgtkui
-
-#endif  // CHROME_BROWSER_UI_LIBGTKUI_GTK_STATUS_ICON_H_
diff --git a/chrome/browser/ui/libgtkui/gtk_ui.cc b/chrome/browser/ui/libgtkui/gtk_ui.cc
index 6fd9009d..a7e5ce9 100644
--- a/chrome/browser/ui/libgtkui/gtk_ui.cc
+++ b/chrome/browser/ui/libgtkui/gtk_ui.cc
@@ -24,10 +24,8 @@
 #include "base/strings/string_split.h"
 #include "base/strings/stringprintf.h"
 #include "chrome/browser/themes/theme_properties.h"
-#include "chrome/browser/ui/libgtkui/app_indicator_icon.h"
 #include "chrome/browser/ui/libgtkui/gtk_event_loop_x11.h"
 #include "chrome/browser/ui/libgtkui/gtk_key_bindings_handler.h"
-#include "chrome/browser/ui/libgtkui/gtk_status_icon.h"
 #include "chrome/browser/ui/libgtkui/gtk_util.h"
 #include "chrome/browser/ui/libgtkui/input_method_context_impl_gtk.h"
 #include "chrome/browser/ui/libgtkui/native_theme_gtk.h"
@@ -530,40 +528,6 @@
     unity::SetProgressFraction(percentage);
 }
 
-bool GtkUi::IsStatusIconSupported() const {
-#if GTK_CHECK_VERSION(3, 90, 0)
-  // TODO(thomasanderson): Provide some sort of status icon for GTK4.  The GTK3
-  // config has two options.  The first is to use GTK status icons, but these
-  // were removed in GTK4.  The second is to use libappindicator.  However, that
-  // library has a dependency on GTK3, and loading multiple versions of GTK into
-  // the same process is explicitly unsupported.
-  NOTIMPLEMENTED();
-  return false;
-#else
-  return true;
-#endif
-}
-
-std::unique_ptr<views::StatusIconLinux> GtkUi::CreateLinuxStatusIcon(
-    const gfx::ImageSkia& image,
-    const base::string16& tool_tip,
-    const char* id_prefix) const {
-#if GTK_CHECK_VERSION(3, 90, 0)
-  NOTIMPLEMENTED();
-  return nullptr;
-#else
-  if (AppIndicatorIcon::CouldOpen()) {
-    ++indicators_count;
-    return std::unique_ptr<views::StatusIconLinux>(new AppIndicatorIcon(
-        base::StringPrintf("%s%d", id_prefix, indicators_count), image,
-        tool_tip));
-  } else {
-    return std::unique_ptr<views::StatusIconLinux>(
-        new GtkStatusIcon(image, tool_tip));
-  }
-#endif
-}
-
 gfx::Image GtkUi::GetIconForContentType(const std::string& content_type,
                                         int size) const {
   // This call doesn't take a reference.
diff --git a/chrome/browser/ui/libgtkui/gtk_ui.h b/chrome/browser/ui/libgtkui/gtk_ui.h
index 37eb483..44ad6106 100644
--- a/chrome/browser/ui/libgtkui/gtk_ui.h
+++ b/chrome/browser/ui/libgtkui/gtk_ui.h
@@ -83,11 +83,6 @@
   bool GetDefaultUsesSystemTheme() const override;
   void SetDownloadCount(int count) const override;
   void SetProgressFraction(float percentage) const override;
-  bool IsStatusIconSupported() const override;
-  std::unique_ptr<views::StatusIconLinux> CreateLinuxStatusIcon(
-      const gfx::ImageSkia& image,
-      const base::string16& tool_tip,
-      const char* id_prefix) const override;
   gfx::Image GetIconForContentType(const std::string& content_type,
                                    int size) const override;
   std::unique_ptr<views::Border> CreateNativeBorder(
diff --git a/chrome/browser/ui/libgtkui/menu_util.cc b/chrome/browser/ui/libgtkui/menu_util.cc
deleted file mode 100644
index aa6a7a97..0000000
--- a/chrome/browser/ui/libgtkui/menu_util.cc
+++ /dev/null
@@ -1,279 +0,0 @@
-// Copyright 2013 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "chrome/browser/ui/libgtkui/menu_util.h"
-
-#include <map>
-#include <string>
-
-#include "base/strings/utf_string_conversions.h"
-#include "chrome/app/chrome_command_ids.h"
-#include "chrome/browser/ui/libgtkui/gtk_util.h"
-#include "chrome/browser/ui/libgtkui/skia_utils_gtk.h"
-#include "ui/base/accelerators/accelerator.h"
-#include "ui/base/accelerators/menu_label_accelerator_util_linux.h"
-#include "ui/base/models/menu_model.h"
-
-namespace libgtkui {
-
-GtkWidget* BuildMenuItemWithImage(const std::string& label, GtkWidget* image) {
-// GTK4 removed support for image menu items.
-#if GTK_CHECK_VERSION(3, 90, 0)
-  return gtk_menu_item_new_with_mnemonic(label.c_str());
-#else
-  G_GNUC_BEGIN_IGNORE_DEPRECATIONS;
-  GtkWidget* menu_item = gtk_image_menu_item_new_with_mnemonic(label.c_str());
-  gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(menu_item), image);
-  G_GNUC_END_IGNORE_DEPRECATIONS;
-  return menu_item;
-#endif
-}
-
-GtkWidget* BuildMenuItemWithImage(const std::string& label,
-                                  const gfx::Image& icon) {
-  GdkPixbuf* pixbuf = GdkPixbufFromSkBitmap(*icon.ToSkBitmap());
-
-  GtkWidget* menu_item =
-      BuildMenuItemWithImage(label, gtk_image_new_from_pixbuf(pixbuf));
-  g_object_unref(pixbuf);
-  return menu_item;
-}
-
-GtkWidget* BuildMenuItemWithLabel(const std::string& label) {
-  return gtk_menu_item_new_with_mnemonic(label.c_str());
-}
-
-ui::MenuModel* ModelForMenuItem(GtkMenuItem* menu_item) {
-  return reinterpret_cast<ui::MenuModel*>(
-      g_object_get_data(G_OBJECT(menu_item), "model"));
-}
-
-GtkWidget* AppendMenuItemToMenu(int index,
-                                ui::MenuModel* model,
-                                GtkWidget* menu_item,
-                                GtkWidget* menu,
-                                bool connect_to_activate,
-                                GCallback item_activated_cb,
-                                void* this_ptr) {
-  // Set the ID of a menu item.
-  // Add 1 to the menu_id to avoid setting zero (null) to "menu-id".
-  g_object_set_data(G_OBJECT(menu_item), "menu-id", GINT_TO_POINTER(index + 1));
-
-  // Native menu items do their own thing, so only selectively listen for the
-  // activate signal.
-  if (connect_to_activate) {
-    g_signal_connect(menu_item, "activate", item_activated_cb, this_ptr);
-  }
-
-  // AppendMenuItemToMenu is used both internally when we control menu creation
-  // from a model (where the model can choose to hide certain menu items), and
-  // with immediate commands which don't provide the option.
-  if (model) {
-    if (model->IsVisibleAt(index))
-      gtk_widget_show(menu_item);
-  } else {
-    gtk_widget_show(menu_item);
-  }
-  gtk_menu_shell_append(GTK_MENU_SHELL(menu), menu_item);
-  return menu_item;
-}
-
-bool GetMenuItemID(GtkWidget* menu_item, int* menu_id) {
-  gpointer id_ptr = g_object_get_data(G_OBJECT(menu_item), "menu-id");
-  if (id_ptr != nullptr) {
-    *menu_id = GPOINTER_TO_INT(id_ptr) - 1;
-    return true;
-  }
-
-  return false;
-}
-
-void ExecuteCommand(ui::MenuModel* model, int id) {
-  GdkEvent* event = gtk_get_current_event();
-  int event_flags = 0;
-
-  if (event && event->type == GDK_BUTTON_RELEASE)
-    event_flags = EventFlagsFromGdkState(event->button.state);
-  model->ActivatedAt(id, event_flags);
-
-  if (event)
-    gdk_event_free(event);
-}
-
-void BuildSubmenuFromModel(ui::MenuModel* model,
-                           GtkWidget* menu,
-                           GCallback item_activated_cb,
-                           bool* block_activation,
-                           void* this_ptr) {
-  std::map<int, GtkWidget*> radio_groups;
-  GtkWidget* menu_item = nullptr;
-  for (int i = 0; i < model->GetItemCount(); ++i) {
-    gfx::Image icon;
-    std::string label = ui::ConvertAcceleratorsFromWindowsStyle(
-        base::UTF16ToUTF8(model->GetLabelAt(i)));
-
-    bool connect_to_activate = true;
-
-    switch (model->GetTypeAt(i)) {
-      case ui::MenuModel::TYPE_SEPARATOR:
-        menu_item = gtk_separator_menu_item_new();
-        break;
-
-      case ui::MenuModel::TYPE_CHECK:
-        menu_item = gtk_check_menu_item_new_with_mnemonic(label.c_str());
-        break;
-
-      case ui::MenuModel::TYPE_RADIO: {
-        auto iter = radio_groups.find(model->GetGroupIdAt(i));
-
-        if (iter == radio_groups.end()) {
-          menu_item =
-              gtk_radio_menu_item_new_with_mnemonic(nullptr, label.c_str());
-          radio_groups[model->GetGroupIdAt(i)] = menu_item;
-        } else {
-          menu_item = gtk_radio_menu_item_new_with_mnemonic_from_widget(
-              GTK_RADIO_MENU_ITEM(iter->second), label.c_str());
-        }
-        break;
-      }
-      case ui::MenuModel::TYPE_BUTTON_ITEM: {
-        NOTIMPLEMENTED();
-        break;
-      }
-      case ui::MenuModel::TYPE_SUBMENU:
-      case ui::MenuModel::TYPE_COMMAND: {
-        if (model->GetIconAt(i, &icon))
-          menu_item = BuildMenuItemWithImage(label, icon);
-        else
-          menu_item = BuildMenuItemWithLabel(label);
-#if !GTK_CHECK_VERSION(3, 90, 0)
-        G_GNUC_BEGIN_IGNORE_DEPRECATIONS;
-        if (GTK_IS_IMAGE_MENU_ITEM(menu_item)) {
-          gtk_image_menu_item_set_always_show_image(
-              GTK_IMAGE_MENU_ITEM(menu_item), TRUE);
-        }
-        G_GNUC_END_IGNORE_DEPRECATIONS;
-#endif
-        break;
-      }
-
-      default:
-        NOTREACHED();
-    }
-
-    if (model->GetTypeAt(i) == ui::MenuModel::TYPE_SUBMENU) {
-      GtkWidget* submenu = gtk_menu_new();
-      ui::MenuModel* submenu_model = model->GetSubmenuModelAt(i);
-      BuildSubmenuFromModel(submenu_model,
-                            submenu,
-                            item_activated_cb,
-                            block_activation,
-                            this_ptr);
-      gtk_menu_item_set_submenu(GTK_MENU_ITEM(menu_item), submenu);
-
-      // Update all the menu item info in the newly-generated menu.
-      gtk_container_foreach(
-          GTK_CONTAINER(submenu), SetMenuItemInfo, block_activation);
-      submenu_model->MenuWillShow();
-      connect_to_activate = false;
-    }
-
-#if defined(USE_X11)
-    ui::Accelerator accelerator;
-    if (model->GetAcceleratorAt(i, &accelerator)) {
-      gtk_widget_add_accelerator(menu_item, "activate", nullptr,
-                                 GetGdkKeyCodeForAccelerator(accelerator),
-                                 GetGdkModifierForAccelerator(accelerator),
-                                 GTK_ACCEL_VISIBLE);
-    }
-#endif
-
-    g_object_set_data(G_OBJECT(menu_item), "model", model);
-    AppendMenuItemToMenu(i,
-                         model,
-                         menu_item,
-                         menu,
-                         connect_to_activate,
-                         item_activated_cb,
-                         this_ptr);
-
-    menu_item = nullptr;
-  }
-}
-
-void SetMenuItemInfo(GtkWidget* widget, void* block_activation_ptr) {
-  if (GTK_IS_SEPARATOR_MENU_ITEM(widget)) {
-    // We need to explicitly handle this case because otherwise we'll ask the
-    // menu delegate about something with an invalid id.
-    return;
-  }
-
-  int id;
-  if (!GetMenuItemID(widget, &id))
-    return;
-
-  ui::MenuModel* model = ModelForMenuItem(GTK_MENU_ITEM(widget));
-  if (!model) {
-    // If we're not providing the sub menu, then there's no model.  For
-    // example, the IME submenu doesn't have a model.
-    return;
-  }
-  bool* block_activation = static_cast<bool*>(block_activation_ptr);
-
-  if (GTK_IS_CHECK_MENU_ITEM(widget)) {
-    GtkCheckMenuItem* item = GTK_CHECK_MENU_ITEM(widget);
-
-    // gtk_check_menu_item_set_active() will send the activate signal. Touching
-    // the underlying "active" property will also call the "activate" handler
-    // for this menu item. So we prevent the "activate" handler from
-    // being called while we set the checkbox.
-    // Why not use one of the glib signal-blocking functions?  Because when we
-    // toggle a radio button, it will deactivate one of the other radio buttons,
-    // which we don't have a pointer to.
-    *block_activation = true;
-    gtk_check_menu_item_set_active(item, model->IsItemCheckedAt(id));
-    *block_activation = false;
-  }
-
-  if (GTK_IS_MENU_ITEM(widget)) {
-    gtk_widget_set_sensitive(widget, model->IsEnabledAt(id));
-
-    if (model->IsVisibleAt(id)) {
-      // Update the menu item label if it is dynamic.
-      if (model->IsItemDynamicAt(id)) {
-        std::string label = ui::ConvertAcceleratorsFromWindowsStyle(
-            base::UTF16ToUTF8(model->GetLabelAt(id)));
-
-        gtk_menu_item_set_label(GTK_MENU_ITEM(widget), label.c_str());
-#if !GTK_CHECK_VERSION(3, 90, 0)
-        G_GNUC_BEGIN_IGNORE_DEPRECATIONS;
-        if (GTK_IS_IMAGE_MENU_ITEM(widget)) {
-          gfx::Image icon;
-          if (model->GetIconAt(id, &icon)) {
-            GdkPixbuf* pixbuf = GdkPixbufFromSkBitmap(*icon.ToSkBitmap());
-            gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(widget),
-                                          gtk_image_new_from_pixbuf(pixbuf));
-            g_object_unref(pixbuf);
-          } else {
-            gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(widget), nullptr);
-          }
-        }
-        G_GNUC_END_IGNORE_DEPRECATIONS;
-#endif
-      }
-
-      gtk_widget_show(widget);
-    } else {
-      gtk_widget_hide(widget);
-    }
-
-    GtkWidget* submenu = gtk_menu_item_get_submenu(GTK_MENU_ITEM(widget));
-    if (submenu) {
-      gtk_container_foreach(
-          GTK_CONTAINER(submenu), &SetMenuItemInfo, block_activation_ptr);
-    }
-  }
-}
-
-}  // namespace libgtkui
diff --git a/chrome/browser/ui/libgtkui/menu_util.h b/chrome/browser/ui/libgtkui/menu_util.h
deleted file mode 100644
index e0e5f41b..0000000
--- a/chrome/browser/ui/libgtkui/menu_util.h
+++ /dev/null
@@ -1,58 +0,0 @@
-// Copyright 2013 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CHROME_BROWSER_UI_LIBGTKUI_MENU_UTIL_H_
-#define CHROME_BROWSER_UI_LIBGTKUI_MENU_UTIL_H_
-
-#include <gtk/gtk.h>
-
-#include "ui/gfx/image/image.h"
-
-// THIS FILE IS DEPRECATED (REPLACED BY DbusMenu) AND WILL SOON BE REMOVED.
-
-namespace ui {
-class MenuModel;
-}
-
-namespace libgtkui {
-// Builds GtkImageMenuItems.
-GtkWidget* BuildMenuItemWithImage(const std::string& label, GtkWidget* image);
-GtkWidget* BuildMenuItemWithImage(const std::string& label,
-                                  const gfx::Image& icon);
-GtkWidget* BuildMenuItemWithLabel(const std::string& label);
-
-ui::MenuModel* ModelForMenuItem(GtkMenuItem* menu_item);
-
-// This method is used to build the menu dynamically. The return value is the
-// new menu item.
-GtkWidget* AppendMenuItemToMenu(int index,
-                                ui::MenuModel* model,
-                                GtkWidget* menu_item,
-                                GtkWidget* menu,
-                                bool connect_to_activate,
-                                GCallback item_activated_cb,
-                                void* this_ptr);
-
-// Gets the ID of a menu item.
-// Returns true if the menu item has an ID.
-bool GetMenuItemID(GtkWidget* menu_item, int* menu_id);
-
-// Execute command associated with specified id.
-void ExecuteCommand(ui::MenuModel* model, int id);
-
-// Creates a GtkMenu from |model_|. block_activation_ptr is used to disable
-// the item_activated_callback while we set up the set up the menu items.
-// See comments in definition of SetMenuItemInfo for more info.
-void BuildSubmenuFromModel(ui::MenuModel* model,
-                           GtkWidget* menu,
-                           GCallback item_activated_cb,
-                           bool* block_activation,
-                           void* this_ptr);
-
-// Sets the check mark, enabled/disabled state and dynamic labels on menu items.
-void SetMenuItemInfo(GtkWidget* widget, void* block_activation_ptr);
-
-}  // namespace libgtkui
-
-#endif  // CHROME_BROWSER_UI_LIBGTKUI_MENU_UTIL_H_
diff --git a/chrome/browser/ui/search/instant_theme_browsertest.cc b/chrome/browser/ui/search/instant_theme_browsertest.cc
index 8ede9f3..c8869ab 100644
--- a/chrome/browser/ui/search/instant_theme_browsertest.cc
+++ b/chrome/browser/ui/search/instant_theme_browsertest.cc
@@ -28,6 +28,7 @@
 #include "extensions/browser/extension_registry.h"
 #include "net/test/embedded_test_server/embedded_test_server.h"
 #include "testing/gtest/include/gtest/gtest.h"
+#include "ui/gfx/color_utils.h"
 
 class TestThemeInfoObserver : public InstantServiceObserver {
  public:
@@ -39,9 +40,8 @@
 
   void WaitForThemeApplied(bool theme_installed) {
     DCHECK(!quit_closure_);
-
     theme_installed_ = theme_installed;
-    if (!theme_info_.using_default_theme == theme_installed) {
+    if (hasThemeInstalled(theme_info_) == theme_installed_) {
       return;
     }
 
@@ -50,13 +50,11 @@
     run_loop.Run();
   }
 
-  bool IsUsingDefaultTheme() { return theme_info_.using_default_theme; }
-
  private:
   void ThemeInfoChanged(const ThemeBackgroundInfo& theme_info) override {
     theme_info_ = theme_info;
 
-    if (quit_closure_ && !theme_info_.using_default_theme == theme_installed_) {
+    if (quit_closure_ && hasThemeInstalled(theme_info) == theme_installed_) {
       std::move(quit_closure_).Run();
       quit_closure_.Reset();
     }
@@ -64,6 +62,10 @@
 
   void MostVisitedInfoChanged(const InstantMostVisitedInfo&) override {}
 
+  bool hasThemeInstalled(const ThemeBackgroundInfo& theme_info) {
+    return theme_info.theme_id != "";
+  }
+
   InstantService* const service_;
 
   ThemeBackgroundInfo theme_info_;
@@ -224,7 +226,6 @@
   const std::string helper_js = "document.body.style.cssText";
   TestThemeInfoObserver observer(
       InstantServiceFactory::GetForProfile(browser()->profile()));
-
   // Open new tab.
   content::WebContents* active_tab =
       local_ntp_test_utils::OpenNewTab(browser(), GURL("about:blank"));
diff --git a/chrome/browser/ui/ui_features.cc b/chrome/browser/ui/ui_features.cc
index 0005b79..b0590ceb 100644
--- a/chrome/browser/ui/ui_features.cc
+++ b/chrome/browser/ui/ui_features.cc
@@ -79,11 +79,6 @@
 const base::Feature kWebUITabStrip{"WebUITabStrip",
                                    base::FEATURE_DISABLED_BY_DEFAULT};
 
-#if defined(OS_LINUX) && !defined(OS_CHROMEOS)
-constexpr base::Feature kEnableDbusAndX11StatusIcons{
-    "EnableDbusAndX11StatusIcons", base::FEATURE_ENABLED_BY_DEFAULT};
-#endif
-
 #if defined(OS_CHROMEOS)
 // Enables a warning about connecting to hidden WiFi networks.
 // https://crbug.com/903908
diff --git a/chrome/browser/ui/ui_features.h b/chrome/browser/ui/ui_features.h
index a26431f7..0f8c0ff9 100644
--- a/chrome/browser/ui/ui_features.h
+++ b/chrome/browser/ui/ui_features.h
@@ -48,10 +48,6 @@
 
 extern const base::Feature kWebUITabStrip;
 
-#if defined(OS_LINUX) && !defined(OS_CHROMEOS)
-extern const base::Feature kEnableDbusAndX11StatusIcons;
-#endif
-
 #if defined(OS_CHROMEOS)
 extern const base::Feature kHiddenNetworkWarning;
 #endif  // defined(OS_CHROMEOS)
diff --git a/chrome/browser/ui/views/feature_promos/global_media_controls_promo_controller.cc b/chrome/browser/ui/views/feature_promos/global_media_controls_promo_controller.cc
index fcfc7b7..c88921f 100644
--- a/chrome/browser/ui/views/feature_promos/global_media_controls_promo_controller.cc
+++ b/chrome/browser/ui/views/feature_promos/global_media_controls_promo_controller.cc
@@ -66,8 +66,11 @@
   FinishPromo();
 }
 
-void GlobalMediaControlsPromoController::
-    OnMediaToolbarButtonDisabledOrHidden() {
+void GlobalMediaControlsPromoController::OnMediaButtonHidden() {
+  FinishPromo();
+}
+
+void GlobalMediaControlsPromoController::OnMediaButtonDisabled() {
   FinishPromo();
 }
 
diff --git a/chrome/browser/ui/views/feature_promos/global_media_controls_promo_controller.h b/chrome/browser/ui/views/feature_promos/global_media_controls_promo_controller.h
index 3b5d8b3..b3c18dd 100644
--- a/chrome/browser/ui/views/feature_promos/global_media_controls_promo_controller.h
+++ b/chrome/browser/ui/views/feature_promos/global_media_controls_promo_controller.h
@@ -5,6 +5,7 @@
 #ifndef CHROME_BROWSER_UI_VIEWS_FEATURE_PROMOS_GLOBAL_MEDIA_CONTROLS_PROMO_CONTROLLER_H_
 #define CHROME_BROWSER_UI_VIEWS_FEATURE_PROMOS_GLOBAL_MEDIA_CONTROLS_PROMO_CONTROLLER_H_
 
+#include "chrome/browser/ui/global_media_controls/media_toolbar_button_observer.h"
 #include "chrome/browser/ui/views/feature_promos/feature_promo_bubble_view.h"
 #include "ui/views/widget/widget_observer.h"
 
@@ -15,7 +16,8 @@
 // showing the promo bubble and highlighting the appropriate app menu items.
 // Notifies the GlobalMediaControlsInProductHelp service when the promo is
 // finished.
-class GlobalMediaControlsPromoController : public views::WidgetObserver {
+class GlobalMediaControlsPromoController : public views::WidgetObserver,
+                                           public MediaToolbarButtonObserver {
  public:
   GlobalMediaControlsPromoController(MediaToolbarButtonView* owner,
                                      Profile* profile);
@@ -24,11 +26,12 @@
   // Shows the IPH promo. Should only be called once.
   void ShowPromo();
 
-  // Called when the global media controls dialog is opened.
-  void OnMediaDialogOpened();
-
-  // Called when the global media controls toolbar button is disabled or hidden.
-  void OnMediaToolbarButtonDisabledOrHidden();
+  // MediaToolbarButtonObserver implementation.
+  void OnMediaDialogOpened() override;
+  void OnMediaButtonShown() override {}
+  void OnMediaButtonHidden() override;
+  void OnMediaButtonEnabled() override {}
+  void OnMediaButtonDisabled() override;
 
   void disable_bubble_timeout_for_test() {
     disable_bubble_timeout_for_test_ = true;
diff --git a/chrome/browser/ui/views/frame/browser_non_client_frame_view_ash_browsertest.cc b/chrome/browser/ui/views/frame/browser_non_client_frame_view_ash_browsertest.cc
index 8d9c4c1..db81f1c 100644
--- a/chrome/browser/ui/views/frame/browser_non_client_frame_view_ash_browsertest.cc
+++ b/chrome/browser/ui/views/frame/browser_non_client_frame_view_ash_browsertest.cc
@@ -78,6 +78,7 @@
 #include "content/public/test/test_navigation_observer.h"
 #include "net/dns/mock_host_resolver.h"
 #include "services/service_manager/public/cpp/connector.h"
+#include "third_party/blink/public/common/page/page_zoom.h"
 #include "ui/aura/client/aura_constants.h"
 #include "ui/aura/test/env_test_helper.h"
 #include "ui/base/class_property.h"
@@ -966,7 +967,7 @@
   EXPECT_FALSE(zoom_icon->GetVisible());
   EXPECT_FALSE(ZoomBubbleView::GetZoomBubble());
 
-  zoom_controller->SetZoomLevel(content::ZoomFactorToZoomLevel(1.5));
+  zoom_controller->SetZoomLevel(blink::PageZoomFactorToZoomLevel(1.5));
   base::RunLoop().RunUntilIdle();
 
   EXPECT_TRUE(zoom_icon->GetVisible());
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 2dfdfc6..74d02a5 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
@@ -28,8 +28,11 @@
       connector_(connector),
       controller_(source_id, connector_, this),
       browser_(browser) {
-  in_product_help_ = GlobalMediaControlsInProductHelpFactory::GetForProfile(
-      browser_->profile());
+  GlobalMediaControlsInProductHelp* in_product_help =
+      GlobalMediaControlsInProductHelpFactory::GetForProfile(
+          browser_->profile());
+  if (in_product_help)
+    AddObserver(in_product_help);
 
   button_controller()->set_notify_action(
       views::ButtonController::NotifyAction::kOnPress);
@@ -45,41 +48,72 @@
 
 MediaToolbarButtonView::~MediaToolbarButtonView() = default;
 
+void MediaToolbarButtonView::AddObserver(MediaToolbarButtonObserver* observer) {
+  observers_.AddObserver(observer);
+}
+
+void MediaToolbarButtonView::RemoveObserver(
+    MediaToolbarButtonObserver* observer) {
+  observers_.RemoveObserver(observer);
+}
+
 void MediaToolbarButtonView::ButtonPressed(views::Button* sender,
                                            const ui::Event& event) {
   if (MediaDialogView::IsShowing()) {
     MediaDialogView::HideDialog();
   } else {
     MediaDialogView::ShowDialog(this, &controller_, connector_);
-    InformIPHOfDialogShown();
+
+    // Inform observers. Since the promo controller cares about the dialog
+    // showing, we need to ensure that it's created.
+    EnsurePromoController();
+    for (auto& observer : observers_)
+      observer.OnMediaDialogOpened();
   }
 }
 
 void MediaToolbarButtonView::Show() {
   SetVisible(true);
   PreferredSizeChanged();
+
+  for (auto& observer : observers_)
+    observer.OnMediaButtonShown();
 }
 
 void MediaToolbarButtonView::Hide() {
   SetVisible(false);
   PreferredSizeChanged();
-  InformIPHOfButtonDisabledorHidden();
+
+  // Inform observers. Since the promo controller cares about hiding, we need to
+  // ensure that it's created.
+  EnsurePromoController();
+  for (auto& observer : observers_)
+    observer.OnMediaButtonHidden();
 }
 
 void MediaToolbarButtonView::Enable() {
   SetEnabled(true);
-  InformIPHOfButtonEnabled();
+
 #if defined(OS_MACOSX)
   UpdateIcon();
 #endif  // defined(OS_MACOSX)
+
+  for (auto& observer : observers_)
+    observer.OnMediaButtonEnabled();
 }
 
 void MediaToolbarButtonView::Disable() {
   SetEnabled(false);
-  InformIPHOfButtonDisabledorHidden();
+
 #if defined(OS_MACOSX)
   UpdateIcon();
 #endif  // defined(OS_MACOSX)
+
+  // Inform observers. Since the promo controller cares about disabling, we need
+  // to ensure that it's created.
+  EnsurePromoController();
+  for (auto& observer : observers_)
+    observer.OnMediaButtonDisabled();
 }
 
 SkColor MediaToolbarButtonView::GetInkDropBaseColor() const {
@@ -120,7 +154,8 @@
 }
 
 void MediaToolbarButtonView::ShowPromo() {
-  GetPromoController().ShowPromo();
+  EnsurePromoController();
+  promo_controller_->ShowPromo();
   is_promo_showing_ = true;
   GetInkDrop()->AnimateToState(views::InkDropState::ACTIVATED);
 }
@@ -130,30 +165,11 @@
   GetInkDrop()->AnimateToState(views::InkDropState::HIDDEN);
 }
 
-GlobalMediaControlsPromoController&
-MediaToolbarButtonView::GetPromoController() {
-  if (!promo_controller_) {
-    promo_controller_ = std::make_unique<GlobalMediaControlsPromoController>(
-        this, browser_->profile());
-  }
-  return *promo_controller_;
-}
+void MediaToolbarButtonView::EnsurePromoController() {
+  if (promo_controller_)
+    return;
 
-void MediaToolbarButtonView::InformIPHOfDialogShown() {
-  if (in_product_help_)
-    in_product_help_->GlobalMediaControlsOpened();
-
-  GetPromoController().OnMediaDialogOpened();
-}
-
-void MediaToolbarButtonView::InformIPHOfButtonEnabled() {
-  if (in_product_help_)
-    in_product_help_->ToolbarIconEnabled();
-}
-
-void MediaToolbarButtonView::InformIPHOfButtonDisabledorHidden() {
-  if (in_product_help_)
-    in_product_help_->ToolbarIconDisabled();
-
-  GetPromoController().OnMediaToolbarButtonDisabledOrHidden();
+  promo_controller_ = std::make_unique<GlobalMediaControlsPromoController>(
+      this, browser_->profile());
+  AddObserver(promo_controller_.get());
 }
diff --git a/chrome/browser/ui/views/global_media_controls/media_toolbar_button_view.h b/chrome/browser/ui/views/global_media_controls/media_toolbar_button_view.h
index c847391..9ec9cf6 100644
--- a/chrome/browser/ui/views/global_media_controls/media_toolbar_button_view.h
+++ b/chrome/browser/ui/views/global_media_controls/media_toolbar_button_view.h
@@ -19,8 +19,8 @@
 }  // namespace service_manager
 
 class Browser;
-class GlobalMediaControlsInProductHelp;
 class GlobalMediaControlsPromoController;
+class MediaToolbarButtonObserver;
 
 // Media icon shown in the trusted area of toolbar. Its lifetime is tied to that
 // of its parent ToolbarView. The icon is made visible when there is an active
@@ -34,6 +34,9 @@
                          const Browser* browser);
   ~MediaToolbarButtonView() override;
 
+  void AddObserver(MediaToolbarButtonObserver* observer);
+  void RemoveObserver(MediaToolbarButtonObserver* observer);
+
   // MediaToolbarButtonControllerDelegate implementation.
   void Show() override;
   void Hide() override;
@@ -55,12 +58,13 @@
   void OnPromoEnded();
 
   GlobalMediaControlsPromoController* GetPromoControllerForTesting() {
-    return &GetPromoController();
+    EnsurePromoController();
+    return promo_controller_.get();
   }
 
  private:
   // Lazily constructs |promo_controller_| if necessary.
-  GlobalMediaControlsPromoController& GetPromoController();
+  void EnsurePromoController();
 
   // Informs the Global Media Controls in-product help that the GMC dialog was
   // opened.
@@ -80,7 +84,8 @@
   service_manager::Connector* const connector_;
   MediaToolbarButtonController controller_;
   const Browser* const browser_;
-  GlobalMediaControlsInProductHelp* in_product_help_;
+
+  base::ObserverList<MediaToolbarButtonObserver> observers_;
 
   DISALLOW_COPY_AND_ASSIGN(MediaToolbarButtonView);
 };
diff --git a/chrome/browser/ui/views/location_bar/location_bar_view_browsertest.cc b/chrome/browser/ui/views/location_bar/location_bar_view_browsertest.cc
index 22b8092..4478480 100644
--- a/chrome/browser/ui/views/location_bar/location_bar_view_browsertest.cc
+++ b/chrome/browser/ui/views/location_bar/location_bar_view_browsertest.cc
@@ -28,7 +28,6 @@
 #include "components/security_state/core/security_state.h"
 #include "components/zoom/zoom_controller.h"
 #include "content/public/browser/browser_thread.h"
-#include "content/public/common/page_zoom.h"
 #include "content/public/test/url_loader_interceptor.h"
 #include "net/cert/ct_policy_status.h"
 #include "net/ssl/ssl_info.h"
@@ -36,6 +35,7 @@
 #include "net/test/test_data_directory.h"
 #include "services/network/public/cpp/features.h"
 #include "services/network/public/cpp/resource_response.h"
+#include "third_party/blink/public/common/page/page_zoom.h"
 #include "ui/base/test/material_design_controller_test_api.h"
 
 class LocationBarViewBrowserTest : public InProcessBrowserTest {
@@ -73,7 +73,7 @@
 
   // Altering zoom should display a bubble. Note ZoomBubbleView closes
   // asynchronously, so precede checks with a run loop flush.
-  zoom_controller->SetZoomLevel(content::ZoomFactorToZoomLevel(1.5));
+  zoom_controller->SetZoomLevel(blink::PageZoomFactorToZoomLevel(1.5));
   base::RunLoop().RunUntilIdle();
   EXPECT_TRUE(zoom_view->GetVisible());
   EXPECT_TRUE(ZoomBubbleView::GetZoomBubble());
@@ -85,13 +85,13 @@
   EXPECT_FALSE(ZoomBubbleView::GetZoomBubble());
 
   // Show the bubble again.
-  zoom_controller->SetZoomLevel(content::ZoomFactorToZoomLevel(2.0));
+  zoom_controller->SetZoomLevel(blink::PageZoomFactorToZoomLevel(2.0));
   base::RunLoop().RunUntilIdle();
   EXPECT_TRUE(zoom_view->GetVisible());
   EXPECT_TRUE(ZoomBubbleView::GetZoomBubble());
 
   // Remains visible at 100% until the bubble is closed.
-  zoom_controller->SetZoomLevel(content::ZoomFactorToZoomLevel(1.0));
+  zoom_controller->SetZoomLevel(blink::PageZoomFactorToZoomLevel(1.0));
   base::RunLoop().RunUntilIdle();
   EXPECT_TRUE(zoom_view->GetVisible());
   EXPECT_TRUE(ZoomBubbleView::GetZoomBubble());
@@ -114,7 +114,7 @@
   ASSERT_TRUE(zoom_view);
   EXPECT_FALSE(zoom_view->GetVisible());
 
-  zoom_controller->SetZoomLevel(content::ZoomFactorToZoomLevel(1.5));
+  zoom_controller->SetZoomLevel(blink::PageZoomFactorToZoomLevel(1.5));
   base::RunLoop().RunUntilIdle();
   EXPECT_TRUE(zoom_view->GetVisible());
   EXPECT_TRUE(ZoomBubbleView::GetZoomBubble());
diff --git a/chrome/browser/ui/views/status_icons/status_icon_linux_wrapper.cc b/chrome/browser/ui/views/status_icons/status_icon_linux_wrapper.cc
index 070f1cf..e2a284e0 100644
--- a/chrome/browser/ui/views/status_icons/status_icon_linux_wrapper.cc
+++ b/chrome/browser/ui/views/status_icons/status_icon_linux_wrapper.cc
@@ -11,7 +11,6 @@
 #include "base/memory/scoped_refptr.h"
 #include "chrome/browser/ui/ui_features.h"
 #include "ui/message_center/public/cpp/notifier_id.h"
-#include "ui/views/linux_ui/linux_ui.h"
 
 #if defined(USE_DBUS)
 #include "chrome/browser/ui/views/status_icons/status_icon_linux_dbus.h"
@@ -23,9 +22,6 @@
 
 namespace {
 
-// Prefix for app indicator ids
-const char kAppIndicatorIdPrefix[] = "chrome_app_indicator_";
-
 gfx::ImageSkia GetBestImageRep(const gfx::ImageSkia& image) {
   float best_scale = 0.0f;
   SkBitmap best_rep;
@@ -140,12 +136,12 @@
     case kTypeX11:
       status_icon_linux_.reset();
       status_icon_ = nullptr;
-      status_icon_type_ = kTypeOther;
+      status_icon_type_ = kTypeNone;
       if (menu_model_)
         menu_model_->RemoveObserver(this);
       menu_model_ = nullptr;
       return;
-    case kTypeOther:
+    case kTypeNone:
       NOTREACHED();
   }
 }
@@ -159,27 +155,15 @@
 StatusIconLinuxWrapper::CreateWrappedStatusIcon(
     const gfx::ImageSkia& image,
     const base::string16& tool_tip) {
-  if (base::FeatureList::IsEnabled(features::kEnableDbusAndX11StatusIcons)) {
 #if defined(USE_DBUS)
-    return base::WrapUnique(new StatusIconLinuxWrapper(
-        base::MakeRefCounted<StatusIconLinuxDbus>(), image, tool_tip));
+  return base::WrapUnique(new StatusIconLinuxWrapper(
+      base::MakeRefCounted<StatusIconLinuxDbus>(), image, tool_tip));
 #elif defined(USE_X11)
-    return base::WrapUnique(new StatusIconLinuxWrapper(
-        std::make_unique<StatusIconLinuxX11>(), kTypeX11, image, tool_tip));
-#endif
-    return nullptr;
-  }
-  const views::LinuxUI* linux_ui = views::LinuxUI::instance();
-  if (linux_ui) {
-    auto status_icon =
-        linux_ui->CreateLinuxStatusIcon(image, tool_tip, kAppIndicatorIdPrefix);
-    if (status_icon) {
-      return base::WrapUnique(
-          new StatusIconLinuxWrapper(std::move(status_icon), kTypeOther,
-                                     gfx::ImageSkia(), base::string16()));
-    }
-  }
+  return base::WrapUnique(new StatusIconLinuxWrapper(
+      std::make_unique<StatusIconLinuxX11>(), kTypeX11, image, tool_tip));
+#else
   return nullptr;
+#endif
 }
 
 void StatusIconLinuxWrapper::UpdatePlatformContextMenu(
diff --git a/chrome/browser/ui/views/status_icons/status_icon_linux_wrapper.h b/chrome/browser/ui/views/status_icons/status_icon_linux_wrapper.h
index 3e6feee..cfbb48e 100644
--- a/chrome/browser/ui/views/status_icons/status_icon_linux_wrapper.h
+++ b/chrome/browser/ui/views/status_icons/status_icon_linux_wrapper.h
@@ -57,7 +57,7 @@
   enum StatusIconType {
     kTypeDbus,
     kTypeX11,
-    kTypeOther,
+    kTypeNone,
   };
 
   // A status icon wrapper should only be created by calling
diff --git a/chrome/browser/ui/views/status_icons/status_tray_linux.cc b/chrome/browser/ui/views/status_icons/status_tray_linux.cc
index c09220ea..6bba045 100644
--- a/chrome/browser/ui/views/status_icons/status_tray_linux.cc
+++ b/chrome/browser/ui/views/status_icons/status_tray_linux.cc
@@ -10,7 +10,6 @@
 
 #if !defined(OS_CHROMEOS)
 #include "chrome/browser/ui/views/status_icons/status_icon_linux_wrapper.h"
-#include "ui/views/linux_ui/linux_ui.h"
 
 StatusTrayLinux::StatusTrayLinux() {
 }
@@ -26,12 +25,7 @@
 }
 
 std::unique_ptr<StatusTray> StatusTray::Create() {
-  const views::LinuxUI* linux_ui = views::LinuxUI::instance();
-
-  // Only create a status tray if we can actually create status icons.
-  if (linux_ui && linux_ui->IsStatusIconSupported())
-    return std::make_unique<StatusTrayLinux>();
-  return nullptr;
+  return std::make_unique<StatusTrayLinux>();
 }
 #else  // defined(OS_CHROMEOS)
 std::unique_ptr<StatusTray> StatusTray::Create() {
diff --git a/chrome/browser/ui/views/tabs/tab_drag_controller.cc b/chrome/browser/ui/views/tabs/tab_drag_controller.cc
index 0ad542f..7a630df 100644
--- a/chrome/browser/ui/views/tabs/tab_drag_controller.cc
+++ b/chrome/browser/ui/views/tabs/tab_drag_controller.cc
@@ -57,8 +57,8 @@
 #endif
 
 #if defined(USE_AURA)
-#include "ui/aura/env.h"  // nogncheck
-#include "ui/aura/window.h"  // nogncheck
+#include "ui/aura/env.h"                            // nogncheck
+#include "ui/aura/window.h"                         // nogncheck
 #include "ui/wm/core/window_modality_controller.h"  // nogncheck
 #endif
 
@@ -189,10 +189,9 @@
 
 // Returns true if |bounds| contains the y-coordinate |y|. The y-coordinate
 // of |bounds| is adjusted by |vertical_adjustment|.
-bool DoesRectContainVerticalPointExpanded(
-    const gfx::Rect& bounds,
-    int vertical_adjustment,
-    int y) {
+bool DoesRectContainVerticalPointExpanded(const gfx::Rect& bounds,
+                                          int vertical_adjustment,
+                                          int y) {
   int upper_threshold = bounds.bottom() + vertical_adjustment;
   int lower_threshold = bounds.y() - vertical_adjustment;
   return y >= lower_threshold && y <= upper_threshold;
@@ -287,11 +286,9 @@
     : contents(NULL),
       source_model_index(-1),
       attached_tab(NULL),
-      pinned(false) {
-}
+      pinned(false) {}
 
-TabDragController::TabDragData::~TabDragData() {
-}
+TabDragController::TabDragData::~TabDragData() {}
 
 TabDragController::TabDragData::TabDragData(TabDragData&&) = default;
 
@@ -531,8 +528,8 @@
 }
 
 void TabDragController::Drag(const gfx::Point& point_in_screen) {
-  TRACE_EVENT1("views", "TabDragController::Drag",
-               "point_in_screen", point_in_screen.ToString());
+  TRACE_EVENT1("views", "TabDragController::Drag", "point_in_screen",
+               point_in_screen.ToString());
 
   bring_to_front_timer_.Stop();
   move_stacked_timer_.Stop();
@@ -570,8 +567,8 @@
         new_bounds = CalculateDraggedBrowserBounds(
             source_context_, point_in_screen, &drag_bounds);
         new_bounds.Offset(-widget->GetRestoredBounds().x() +
-                          point_in_screen.x() -
-                          mouse_offset_.x(), 0);
+                              point_in_screen.x() - mouse_offset_.x(),
+                          0);
         widget->SetVisibilityChangedAnimationsEnabled(false);
         widget->Restore();
         widget->SetBounds(new_bounds);
@@ -642,8 +639,7 @@
                                                              : NORMAL);
 }
 
-void TabDragController::InitTabDragData(Tab* tab,
-                                        TabDragData* drag_data) {
+void TabDragController::InitTabDragData(Tab* tab, TabDragData* drag_data) {
   TRACE_EVENT0("views", "TabDragController::InitTabDragData");
   const int source_model_index = source_context_->GetIndexOf(tab);
   drag_data->source_model_index = source_model_index;
@@ -967,7 +963,7 @@
   // only if we have moved a minimum distance since the last reorder (to prevent
   // jitter) or if this the first move and the tabs are not consecutive.
   if ((abs(point_in_screen.x() - last_move_screen_loc_) > threshold ||
-        (initial_move_ && !AreTabsConsecutive()))) {
+       (initial_move_ && !AreTabsConsecutive()))) {
     TabStripModel* attached_model = attached_context_->GetTabStripModel();
     int to_index = attached_context_->GetInsertionIndexForDraggedBounds(
         GetDraggedViewTabStripBounds(dragged_view_point), false,
@@ -1134,9 +1130,8 @@
   BrowserView* browser_view =
       BrowserView::GetBrowserViewForNativeWindow(window);
   // We don't allow drops on windows that don't have tabstrips.
-  if (!browser_view ||
-      !browser_view->browser()->SupportsWindowFeature(
-          Browser::FEATURE_TABSTRIP))
+  if (!browser_view || !browser_view->browser()->SupportsWindowFeature(
+                           Browser::FEATURE_TABSTRIP))
     return NULL;
 
   TabDragContext* other_context = browser_view->tabstrip()->GetDragContext();
@@ -1162,8 +1157,8 @@
 void TabDragController::Attach(TabDragContext* attached_context,
                                const gfx::Point& point_in_screen,
                                bool set_capture) {
-  TRACE_EVENT1("views", "TabDragController::Attach",
-               "point_in_screen", point_in_screen.ToString());
+  TRACE_EVENT1("views", "TabDragController::Attach", "point_in_screen",
+               point_in_screen.ToString());
 
   DCHECK(!attached_context_);  // We should already have detached by the time
                                // we get here.
@@ -1256,8 +1251,8 @@
 }
 
 void TabDragController::Detach(ReleaseCapture release_capture) {
-  TRACE_EVENT1("views", "TabDragController::Detach",
-               "release_capture", release_capture);
+  TRACE_EVENT1("views", "TabDragController::Detach", "release_capture",
+               release_capture);
 
   attached_context_tabs_closed_tracker_.reset();
 
@@ -1391,16 +1386,15 @@
     attached_context_->OwnDragController(this);
   }
   const views::Widget::MoveLoopSource move_loop_source =
-      event_source_ == EVENT_SOURCE_MOUSE ?
-      views::Widget::MOVE_LOOP_SOURCE_MOUSE :
-      views::Widget::MOVE_LOOP_SOURCE_TOUCH;
+      event_source_ == EVENT_SOURCE_MOUSE
+          ? views::Widget::MOVE_LOOP_SOURCE_MOUSE
+          : views::Widget::MOVE_LOOP_SOURCE_TOUCH;
   const views::Widget::MoveLoopEscapeBehavior escape_behavior =
-      is_dragging_new_browser_ ?
-          views::Widget::MOVE_LOOP_ESCAPE_BEHAVIOR_HIDE :
-          views::Widget::MOVE_LOOP_ESCAPE_BEHAVIOR_DONT_HIDE;
-  views::Widget::MoveLoopResult result =
-      move_loop_widget_->RunMoveLoop(
-          drag_offset, move_loop_source, escape_behavior);
+      is_dragging_new_browser_
+          ? views::Widget::MOVE_LOOP_ESCAPE_BEHAVIOR_HIDE
+          : views::Widget::MOVE_LOOP_ESCAPE_BEHAVIOR_DONT_HIDE;
+  views::Widget::MoveLoopResult result = move_loop_widget_->RunMoveLoop(
+      drag_offset, move_loop_source, escape_behavior);
   content::NotificationService::current()->Notify(
       chrome::NOTIFICATION_TAB_DRAG_LOOP_DONE,
       content::NotificationService::AllBrowserContextsAndSources(),
@@ -1431,8 +1425,8 @@
       return;
     tab_strip_to_attach_to_after_exit_ = NULL;
   } else if (current_state_ == DragState::kWaitingToStop) {
-    EndDrag(result == views::Widget::MOVE_LOOP_CANCELED ?
-            END_DRAG_CANCEL : END_DRAG_COMPLETE);
+    EndDrag(result == views::Widget::MOVE_LOOP_CANCELED ? END_DRAG_CANCEL
+                                                        : END_DRAG_COMPLETE);
   }
 }
 
@@ -1780,8 +1774,8 @@
     return;
 
   if (window) {
-    views::Widget* widget_window = views::Widget::GetWidgetForNativeWindow(
-        window);
+    views::Widget* widget_window =
+        views::Widget::GetWidgetForNativeWindow(window);
     if (!widget_window)
       return;
 
@@ -1866,10 +1860,8 @@
     // window is at least 50% the size of the old.
     const gfx::Size max_size(
         source->AsView()->GetWidget()->GetWindowBoundsInScreen().size());
-    new_bounds.set_width(
-        std::max(max_size.width() / 2, new_bounds.width()));
-    new_bounds.set_height(
-        std::max(max_size.height() / 2, new_bounds.height()));
+    new_bounds.set_width(std::max(max_size.width() / 2, new_bounds.width()));
+    new_bounds.set_height(std::max(max_size.height() / 2, new_bounds.height()));
   }
 
 #if defined(OS_CHROMEOS)
@@ -1894,7 +1886,7 @@
       break;
     }
     default:
-      break; // Nothing to do for DETACH_ABOVE_OR_BELOW.
+      break;  // Nothing to do for DETACH_ABOVE_OR_BELOW.
   }
 
   // Account for the extra space above the tabstrip on restored windows versus
@@ -1968,9 +1960,8 @@
     const gfx::Point& point_in_screen,
     gfx::Vector2d* drag_offset,
     std::vector<gfx::Rect>* drag_bounds) {
-  gfx::Rect new_bounds(CalculateDraggedBrowserBounds(source,
-                                                     point_in_screen,
-                                                     drag_bounds));
+  gfx::Rect new_bounds(
+      CalculateDraggedBrowserBounds(source, point_in_screen, drag_bounds));
   *drag_offset = point_in_screen - new_bounds.origin();
 
   Profile* profile =
diff --git a/chrome/browser/ui/views/toolbar/browser_app_menu_button.h b/chrome/browser/ui/views/toolbar/browser_app_menu_button.h
index aca48d9..e7eee46 100644
--- a/chrome/browser/ui/views/toolbar/browser_app_menu_button.h
+++ b/chrome/browser/ui/views/toolbar/browser_app_menu_button.h
@@ -22,8 +22,8 @@
 class ToolbarView;
 enum class InProductHelpFeature;
 
-// The app menu button in the main browser window (as opposed to hosted app
-// windows, which is implemented in HostedAppMenuButton).
+// The app menu button in the main browser window (as opposed to web app
+// windows, which is implemented in WebAppMenuButton).
 class BrowserAppMenuButton : public AppMenuButton,
                              public ui::MaterialDesignControllerObserver {
  public:
diff --git a/chrome/browser/ui/webui/chromeos/system_web_dialog_browsertest.cc b/chrome/browser/ui/webui/chromeos/system_web_dialog_browsertest.cc
index ce3b17a..55649387 100644
--- a/chrome/browser/ui/webui/chromeos/system_web_dialog_browsertest.cc
+++ b/chrome/browser/ui/webui/chromeos/system_web_dialog_browsertest.cc
@@ -18,10 +18,10 @@
 #include "components/account_id/account_id.h"
 #include "components/prefs/pref_service.h"
 #include "content/public/browser/render_view_host.h"
-#include "content/public/common/page_zoom.h"
 #include "content/public/common/service_manager_connection.h"
 #include "content/public/common/web_preferences.h"
 #include "services/service_manager/public/cpp/connector.h"
+#include "third_party/blink/public/common/page/page_zoom.h"
 #include "ui/aura/client/aura_constants.h"
 #include "url/gurl.h"
 
@@ -132,7 +132,7 @@
   features.InitAndEnableFeature(features::kSplitSettings);
 
   // Set the default browser page zoom to 150%.
-  double level = content::ZoomFactorToZoomLevel(1.5);
+  double level = blink::PageZoomFactorToZoomLevel(1.5);
   browser()->profile()->GetZoomLevelPrefs()->SetDefaultZoomLevelPref(level);
 
   // Open a system dialog.
@@ -142,8 +142,8 @@
   // Dialog page zoom is still 100%.
   auto* web_contents = dialog->GetWebUIForTest()->GetWebContents();
   double dialog_level = content::HostZoomMap::GetZoomLevel(web_contents);
-  EXPECT_TRUE(content::ZoomValuesEqual(dialog_level,
-                                       content::ZoomFactorToZoomLevel(1.0)))
+  EXPECT_TRUE(blink::PageZoomValuesEqual(dialog_level,
+                                         blink::PageZoomFactorToZoomLevel(1.0)))
       << dialog_level;
 }
 
diff --git a/chrome/browser/ui/webui/chromeos/system_web_dialog_delegate.cc b/chrome/browser/ui/webui/chromeos/system_web_dialog_delegate.cc
index ce6790a..23267bce 100644
--- a/chrome/browser/ui/webui/chromeos/system_web_dialog_delegate.cc
+++ b/chrome/browser/ui/webui/chromeos/system_web_dialog_delegate.cc
@@ -19,7 +19,7 @@
 #include "content/public/browser/render_view_host.h"
 #include "content/public/browser/web_contents.h"
 #include "content/public/browser/web_ui.h"
-#include "content/public/common/page_zoom.h"
+#include "third_party/blink/public/common/page/page_zoom.h"
 #include "ui/aura/window.h"
 
 namespace chromeos {
@@ -137,7 +137,7 @@
     // Temporary means the lifetime of the WebContents.
     zoom_map->SetTemporaryZoomLevel(rvh->GetProcess()->GetID(),
                                     rvh->GetRoutingID(),
-                                    content::ZoomFactorToZoomLevel(1.0));
+                                    blink::PageZoomFactorToZoomLevel(1.0));
   }
 }
 
diff --git a/chrome/browser/ui/webui/crashes_ui.cc b/chrome/browser/ui/webui/crashes_ui.cc
index ade6add..ba48748 100644
--- a/chrome/browser/ui/webui/crashes_ui.cc
+++ b/chrome/browser/ui/webui/crashes_ui.cc
@@ -175,7 +175,9 @@
   bool using_crashpad = false;
 #if defined(OS_WIN) || defined(OS_MACOSX) || defined(OS_ANDROID)
   using_crashpad = true;
-#elif defined(OS_LINUX)
+#elif defined(OS_LINUX) && !defined(OS_CHROMEOS)
+  // ChromeOS uses crash_sender instead of Crashpad for uploads even when
+  // Crashpad is enabled for dump generation.
   using_crashpad = crash_reporter::IsCrashpadEnabled();
 #endif
 
diff --git a/chrome/browser/ui/webui/settings/site_settings_handler.cc b/chrome/browser/ui/webui/settings/site_settings_handler.cc
index 3ece18d..99d86a7 100644
--- a/chrome/browser/ui/webui/settings/site_settings_handler.cc
+++ b/chrome/browser/ui/webui/settings/site_settings_handler.cc
@@ -52,11 +52,11 @@
 #include "content/public/browser/web_contents.h"
 #include "content/public/browser/web_ui.h"
 #include "content/public/common/origin_util.h"
-#include "content/public/common/page_zoom.h"
 #include "content/public/common/url_constants.h"
 #include "extensions/browser/extension_registry.h"
 #include "extensions/common/permissions/api_permission.h"
 #include "extensions/common/permissions/permissions_data.h"
+#include "third_party/blink/public/common/page/page_zoom.h"
 #include "ui/base/l10n/l10n_util.h"
 #include "ui/base/text/bytes_formatting.h"
 
@@ -1269,7 +1269,7 @@
     // Calculate the zoom percent from the factor. Round up to the nearest whole
     // number.
     int zoom_percent = static_cast<int>(
-        content::ZoomLevelToZoomFactor(zoom_level.zoom_level) * 100 + 0.5);
+        blink::PageZoomLevelToZoomFactor(zoom_level.zoom_level) * 100 + 0.5);
     exception->SetString(kZoom, base::FormatPercent(zoom_percent));
     exception->SetString(site_settings::kSource,
                          site_settings::SiteSettingSourceToString(
diff --git a/chrome/browser/ui/zoom/chrome_zoom_level_prefs.cc b/chrome/browser/ui/zoom/chrome_zoom_level_prefs.cc
index bf522660..828d055 100644
--- a/chrome/browser/ui/zoom/chrome_zoom_level_prefs.cc
+++ b/chrome/browser/ui/zoom/chrome_zoom_level_prefs.cc
@@ -24,7 +24,7 @@
 #include "components/zoom/zoom_event_manager.h"
 #include "content/public/browser/browser_thread.h"
 #include "content/public/browser/host_zoom_map.h"
-#include "content/public/common/page_zoom.h"
+#include "third_party/blink/public/common/page/page_zoom.h"
 
 namespace {
 
@@ -81,7 +81,7 @@
 }
 
 void ChromeZoomLevelPrefs::SetDefaultZoomLevelPref(double level) {
-  if (content::ZoomValuesEqual(level, host_zoom_map_->GetDefaultZoomLevel()))
+  if (blink::PageZoomValuesEqual(level, host_zoom_map_->GetDefaultZoomLevel()))
     return;
 
   DictionaryPrefUpdate update(pref_service_, prefs::kPartitionDefaultZoomLevel);
@@ -128,7 +128,7 @@
   DCHECK(host_zoom_dictionaries);
 
   bool modification_is_removal =
-      content::ZoomValuesEqual(level, host_zoom_map_->GetDefaultZoomLevel());
+      blink::PageZoomValuesEqual(level, host_zoom_map_->GetDefaultZoomLevel());
 
   base::DictionaryValue* host_zoom_dictionary_weak = nullptr;
   if (!host_zoom_dictionaries->GetDictionary(partition_key_,
@@ -185,8 +185,8 @@
     // will ignore type B values, thus, to have consistency with HostZoomMap's
     // internal state, these values must also be removed from Prefs.
     if (host.empty() || !has_valid_zoom_level ||
-        content::ZoomValuesEqual(zoom_level,
-                                 host_zoom_map_->GetDefaultZoomLevel())) {
+        blink::PageZoomValuesEqual(zoom_level,
+                                   host_zoom_map_->GetDefaultZoomLevel())) {
       keys_to_remove.push_back(host);
       continue;
     }
diff --git a/chrome/lib/util/public/android/java/src/org/chromium/chrome/browser/util/UrlUtilities.java b/chrome/lib/util/public/android/java/src/org/chromium/chrome/browser/util/UrlUtilities.java
index 753083ae..dca8497 100644
--- a/chrome/lib/util/public/android/java/src/org/chromium/chrome/browser/util/UrlUtilities.java
+++ b/chrome/lib/util/public/android/java/src/org/chromium/chrome/browser/util/UrlUtilities.java
@@ -360,11 +360,6 @@
         return noScheme;
     }
 
-    // TODO(estevenson): Remove this after downstream usages are removed.
-    public static boolean nativeIsGoogleSearchUrl(String url) {
-        return UrlUtilitiesJni.get().isGoogleSearchUrl(url);
-    }
-
     @NativeMethods
     public interface Natives {
         boolean isDownloadable(String url);
diff --git a/chrome/renderer/plugins/chrome_plugin_placeholder.cc b/chrome/renderer/plugins/chrome_plugin_placeholder.cc
index fe074e5..9b3541d 100644
--- a/chrome/renderer/plugins/chrome_plugin_placeholder.cc
+++ b/chrome/renderer/plugins/chrome_plugin_placeholder.cc
@@ -33,6 +33,7 @@
 #include "ipc/ipc_sync_channel.h"
 #include "third_party/blink/public/common/associated_interfaces/associated_interface_provider.h"
 #include "third_party/blink/public/common/associated_interfaces/associated_interface_registry.h"
+#include "third_party/blink/public/common/page/page_zoom.h"
 #include "third_party/blink/public/platform/url_conversion.h"
 #include "third_party/blink/public/platform/web_input_event.h"
 #include "third_party/blink/public/platform/web_mouse_event.h"
@@ -131,7 +132,7 @@
     values.SetString("baseurl", power_saver_info.base_url.spec());
 
     if (!power_saver_info.custom_poster_size.IsEmpty()) {
-      float zoom_factor = blink::WebView::ZoomLevelToZoomFactor(
+      float zoom_factor = blink::PageZoomLevelToZoomFactor(
           render_frame->GetWebFrame()->View()->ZoomLevel());
       int width =
           roundf(power_saver_info.custom_poster_size.width() / zoom_factor);
diff --git a/chrome/renderer/searchbox/searchbox_extension.cc b/chrome/renderer/searchbox/searchbox_extension.cc
index c4e4b476..c293b929 100644
--- a/chrome/renderer/searchbox/searchbox_extension.cc
+++ b/chrome/renderer/searchbox/searchbox_extension.cc
@@ -32,7 +32,6 @@
 #include "components/ntp_tiles/ntp_tile_impression.h"
 #include "components/ntp_tiles/tile_source.h"
 #include "components/ntp_tiles/tile_visual_type.h"
-#include "content/public/common/page_zoom.h"
 #include "content/public/renderer/chrome_object_extensions_utils.h"
 #include "content/public/renderer/render_frame.h"
 #include "content/public/renderer/render_thread.h"
@@ -41,6 +40,7 @@
 #include "gin/handle.h"
 #include "gin/object_template_builder.h"
 #include "gin/wrappable.h"
+#include "third_party/blink/public/common/page/page_zoom.h"
 #include "third_party/blink/public/platform/web_string.h"
 #include "third_party/blink/public/platform/web_url_request.h"
 #include "third_party/blink/public/web/blink.h"
@@ -814,7 +814,7 @@
 
   // This corresponds to "window.devicePixelRatio" in JavaScript.
   float zoom_factor =
-      content::ZoomLevelToZoomFactor(render_view->GetZoomLevel());
+      blink::PageZoomLevelToZoomFactor(render_view->GetZoomLevel());
   float device_pixel_ratio = render_view->GetDeviceScaleFactor() * zoom_factor;
 
   int render_view_id = render_view->GetRoutingID();
diff --git a/chrome/services/cups_proxy/ipp_attribute_validator.cc b/chrome/services/cups_proxy/ipp_attribute_validator.cc
index 11eb5375..16da792 100644
--- a/chrome/services/cups_proxy/ipp_attribute_validator.cc
+++ b/chrome/services/cups_proxy/ipp_attribute_validator.cc
@@ -26,151 +26,151 @@
   ValueType type;
 };
 
-// Definitions of all known attributes.
-const std::map<std::string, AttributeDefinition> kAttributeDefinitions{
-    {"attributes-charset", {false, ValueType::STRING}},
-    {"attributes-natural-language", {false, ValueType::STRING}},
-    {"auth-info", {true, ValueType::STRING}},
-    {"auth-info-required", {true, ValueType::STRING}},
-    {"charset-configured", {false, ValueType::STRING}},
-    {"charset-supported", {true, ValueType::STRING}},
-    {"color-supported", {false, ValueType::BOOLEAN}},
-    {"compression", {false, ValueType::STRING}},
-    {"compression-supported", {true, ValueType::STRING}},
-    {"copies", {false, ValueType::INTEGER}},
-    {"copies-default", {false, ValueType::INTEGER}},
-    {"copies-supported", {true, ValueType::INTEGER}},
-    {"document-format", {false, ValueType::STRING}},
-    {"document-format-default", {false, ValueType::STRING}},
-    {"document-format-supported", {true, ValueType::STRING}},
-    {"document-name", {false, ValueType::STRING}},
-    {"finishings", {true, ValueType::INTEGER}},
-    {"finishings-default", {true, ValueType::INTEGER}},
-    {"finishings-supported", {true, ValueType::INTEGER}},
-    {"generated-natural-language-supported", {true, ValueType::STRING}},
-    {"ipp-attribute-fidelity", {false, ValueType::BOOLEAN}},
-    {"ipp-versions-supported", {true, ValueType::STRING}},
-    {"job-cancel-after", {false, ValueType::INTEGER}},
-    {"job-hold-until", {false, ValueType::STRING}},
-    {"job-hold-until-default", {false, ValueType::STRING}},
-    {"job-hold-until-supported", {true, ValueType::STRING}},
-    {"job-id", {false, ValueType::INTEGER}},
-    {"job-k-limit", {false, ValueType::INTEGER}},
-    {"job-media-progress", {false, ValueType::INTEGER}},
-    {"job-name", {false, ValueType::STRING}},
-    {"job-originating-host-name", {false, ValueType::STRING}},
-    {"job-originating-user-name", {false, ValueType::STRING}},
-    {"job-page-limit", {false, ValueType::INTEGER}},
-    {"job-printer-state-message", {false, ValueType::STRING}},
-    {"job-printer-state-reasons", {true, ValueType::STRING}},
-    {"job-printer-up-time", {false, ValueType::INTEGER}},
-    {"job-printer-uri", {false, ValueType::STRING}},
-    {"job-priority", {false, ValueType::INTEGER}},
-    {"job-priority-default", {false, ValueType::INTEGER}},
-    {"job-priority-supported", {false, ValueType::INTEGER}},
-    {"job-quota-period", {false, ValueType::INTEGER}},
-    {"job-sheets", {false, ValueType::STRING}},
-    {"job-sheets-default", {false, ValueType::STRING}},
-    {"job-sheets-supported", {true, ValueType::STRING}},
-    {"job-state", {false, ValueType::INTEGER}},
-    {"job-state-reasons", {true, ValueType::STRING}},
-    {"job-uri", {false, ValueType::STRING}},
-    {"last-document", {false, ValueType::BOOLEAN}},
-    {"limit", {false, ValueType::INTEGER}},
-    {"marker-change-time", {false, ValueType::INTEGER}},
-    {"marker-colors", {true, ValueType::STRING}},
-    {"marker-high-levels", {true, ValueType::INTEGER}},
-    {"marker-levels", {true, ValueType::INTEGER}},
-    {"marker-low-levels", {true, ValueType::INTEGER}},
-    {"marker-message", {false, ValueType::STRING}},
-    {"marker-names", {true, ValueType::STRING}},
-    {"marker-types", {true, ValueType::STRING}},
-    {"media", {false, ValueType::STRING}},
-    {"media-default", {false, ValueType::STRING}},
-    {"media-ready", {true, ValueType::STRING}},
-    {"media-supported", {true, ValueType::STRING}},
-    {"member-names", {true, ValueType::STRING}},
-    {"member-uris", {true, ValueType::STRING}},
-    {"multiple-document-handling", {false, ValueType::STRING}},
-    {"multiple-document-handling-default", {false, ValueType::STRING}},
-    {"multiple-document-handling-supported", {true, ValueType::STRING}},
-    {"my-jobs", {false, ValueType::BOOLEAN}},
-    {"natural-language-configured", {false, ValueType::STRING}},
-    {"number-up", {false, ValueType::INTEGER}},
-    {"number-up-default", {false, ValueType::INTEGER}},
-    {"number-up-supported", {true, ValueType::INTEGER}},
-    {"operations-supported", {true, ValueType::INTEGER}},
-    {"orientation-requested", {false, ValueType::INTEGER}},
-    {"orientation-requested-default", {false, ValueType::INTEGER}},
-    {"orientation-requested-supported", {true, ValueType::INTEGER}},
-    {"output-bin", {false, ValueType::STRING}},
-    {"output-bin-default", {false, ValueType::STRING}},
-    {"output-bin-supported", {true, ValueType::STRING}},
-    {"page-border", {false, ValueType::STRING}},
-    {"page-ranges", {true, ValueType::INTEGER}},
-    {"page-ranges-supported", {false, ValueType::BOOLEAN}},
-    {"pages-per-minute", {false, ValueType::INTEGER}},
-    {"pages-per-minute-color", {false, ValueType::INTEGER}},
-    {"pdl-override-supported", {false, ValueType::STRING}},
-    {"print-quality", {false, ValueType::INTEGER}},
-    {"print-quality-default", {false, ValueType::INTEGER}},
-    {"print-quality-supported", {true, ValueType::INTEGER}},
-    {"printer-alert", {true, ValueType::STRING}},
-    {"printer-alert-description", {true, ValueType::STRING}},
-    {"printer-device-id", {false, ValueType::STRING}},
-    {"printer-dns-sd-name", {false, ValueType::STRING}},
-    {"printer-id", {false, ValueType::INTEGER}},
-    {"printer-info", {false, ValueType::STRING}},
-    {"printer-is-accepting-jobs", {false, ValueType::BOOLEAN}},
-    {"printer-location", {false, ValueType::STRING}},
-    {"printer-make-and-model", {false, ValueType::STRING}},
-    {"printer-more-info", {false, ValueType::STRING}},
-    {"printer-name", {false, ValueType::STRING}},
-    {"printer-resolution", {true, ValueType::INTEGER}},
-    {"printer-resolution-default", {true, ValueType::INTEGER}},
-    {"printer-resolution-supported", {true, ValueType::INTEGER}},
-    {"printer-state", {false, ValueType::INTEGER}},
-    {"printer-state-reasons", {true, ValueType::STRING}},
-    {"printer-type", {false, ValueType::INTEGER}},
-    {"printer-type-mask", {false, ValueType::INTEGER}},
-    {"printer-up-time", {false, ValueType::INTEGER}},
-    {"printer-uri", {false, ValueType::STRING}},
-    {"printer-uri-supported", {true, ValueType::STRING}},
-    {"queued-job-count", {false, ValueType::INTEGER}},
-    {"requested-attributes", {true, ValueType::STRING}},
-    {"requesting-user-name", {false, ValueType::STRING}},
-    {"requesting-user-name-allowed", {false, ValueType::STRING}},
-    {"requesting-user-name-denied", {false, ValueType::STRING}},
-    {"sides", {false, ValueType::STRING}},
-    {"sides-default", {false, ValueType::STRING}},
-    {"sides-supported", {true, ValueType::STRING}},
-    {"status-message", {false, ValueType::STRING}},
-    {"time-at-completed", {false, ValueType::INTEGER}},
-    {"time-at-creation", {false, ValueType::INTEGER}},
-    {"time-at-processing", {false, ValueType::INTEGER}},
-    {"uri-authentication-supported", {true, ValueType::STRING}},
-    {"uri-security-supported", {true, ValueType::STRING}},
-    {"which-jobs", {false, ValueType::STRING}}};
-
-// Allowed IPP Operations.
-const std::set<ipp_op_t> kOperationIds{
-    IPP_OP_CANCEL_JOB,         IPP_OP_CREATE_JOB,
-    IPP_OP_CUPS_GET_DEFAULT,   IPP_OP_CUPS_GET_PPD,
-    IPP_OP_CUPS_GET_PRINTERS,  IPP_OP_GET_JOBS,
-    IPP_OP_GET_JOB_ATTRIBUTES, IPP_OP_GET_PRINTER_ATTRIBUTES,
-    IPP_OP_HOLD_JOB,           IPP_OP_PAUSE_PRINTER,
-    IPP_OP_PRINT_JOB,          IPP_OP_PRINT_URI,
-    IPP_OP_RELEASE_JOB,        IPP_OP_RESUME_PRINTER,
-    IPP_OP_SEND_DOCUMENT,      IPP_OP_SEND_URI,
-    IPP_OP_VALIDATE_JOB,
-};
-
 }  // namespace
 
 ValidateAttributeResult ValidateAttribute(ipp_op_t ipp_oper_id,
                                           const std::string& name,
                                           ValueType type,
                                           size_t values_count) {
+  // Definitions of all known attributes.
+  static const std::map<std::string, AttributeDefinition> kAttributeDefinitions{
+      {"attributes-charset", {false, ValueType::STRING}},
+      {"attributes-natural-language", {false, ValueType::STRING}},
+      {"auth-info", {true, ValueType::STRING}},
+      {"auth-info-required", {true, ValueType::STRING}},
+      {"charset-configured", {false, ValueType::STRING}},
+      {"charset-supported", {true, ValueType::STRING}},
+      {"color-supported", {false, ValueType::BOOLEAN}},
+      {"compression", {false, ValueType::STRING}},
+      {"compression-supported", {true, ValueType::STRING}},
+      {"copies", {false, ValueType::INTEGER}},
+      {"copies-default", {false, ValueType::INTEGER}},
+      {"copies-supported", {true, ValueType::INTEGER}},
+      {"document-format", {false, ValueType::STRING}},
+      {"document-format-default", {false, ValueType::STRING}},
+      {"document-format-supported", {true, ValueType::STRING}},
+      {"document-name", {false, ValueType::STRING}},
+      {"finishings", {true, ValueType::INTEGER}},
+      {"finishings-default", {true, ValueType::INTEGER}},
+      {"finishings-supported", {true, ValueType::INTEGER}},
+      {"generated-natural-language-supported", {true, ValueType::STRING}},
+      {"ipp-attribute-fidelity", {false, ValueType::BOOLEAN}},
+      {"ipp-versions-supported", {true, ValueType::STRING}},
+      {"job-cancel-after", {false, ValueType::INTEGER}},
+      {"job-hold-until", {false, ValueType::STRING}},
+      {"job-hold-until-default", {false, ValueType::STRING}},
+      {"job-hold-until-supported", {true, ValueType::STRING}},
+      {"job-id", {false, ValueType::INTEGER}},
+      {"job-k-limit", {false, ValueType::INTEGER}},
+      {"job-media-progress", {false, ValueType::INTEGER}},
+      {"job-name", {false, ValueType::STRING}},
+      {"job-originating-host-name", {false, ValueType::STRING}},
+      {"job-originating-user-name", {false, ValueType::STRING}},
+      {"job-page-limit", {false, ValueType::INTEGER}},
+      {"job-printer-state-message", {false, ValueType::STRING}},
+      {"job-printer-state-reasons", {true, ValueType::STRING}},
+      {"job-printer-up-time", {false, ValueType::INTEGER}},
+      {"job-printer-uri", {false, ValueType::STRING}},
+      {"job-priority", {false, ValueType::INTEGER}},
+      {"job-priority-default", {false, ValueType::INTEGER}},
+      {"job-priority-supported", {false, ValueType::INTEGER}},
+      {"job-quota-period", {false, ValueType::INTEGER}},
+      {"job-sheets", {false, ValueType::STRING}},
+      {"job-sheets-default", {false, ValueType::STRING}},
+      {"job-sheets-supported", {true, ValueType::STRING}},
+      {"job-state", {false, ValueType::INTEGER}},
+      {"job-state-reasons", {true, ValueType::STRING}},
+      {"job-uri", {false, ValueType::STRING}},
+      {"last-document", {false, ValueType::BOOLEAN}},
+      {"limit", {false, ValueType::INTEGER}},
+      {"marker-change-time", {false, ValueType::INTEGER}},
+      {"marker-colors", {true, ValueType::STRING}},
+      {"marker-high-levels", {true, ValueType::INTEGER}},
+      {"marker-levels", {true, ValueType::INTEGER}},
+      {"marker-low-levels", {true, ValueType::INTEGER}},
+      {"marker-message", {false, ValueType::STRING}},
+      {"marker-names", {true, ValueType::STRING}},
+      {"marker-types", {true, ValueType::STRING}},
+      {"media", {false, ValueType::STRING}},
+      {"media-default", {false, ValueType::STRING}},
+      {"media-ready", {true, ValueType::STRING}},
+      {"media-supported", {true, ValueType::STRING}},
+      {"member-names", {true, ValueType::STRING}},
+      {"member-uris", {true, ValueType::STRING}},
+      {"multiple-document-handling", {false, ValueType::STRING}},
+      {"multiple-document-handling-default", {false, ValueType::STRING}},
+      {"multiple-document-handling-supported", {true, ValueType::STRING}},
+      {"my-jobs", {false, ValueType::BOOLEAN}},
+      {"natural-language-configured", {false, ValueType::STRING}},
+      {"number-up", {false, ValueType::INTEGER}},
+      {"number-up-default", {false, ValueType::INTEGER}},
+      {"number-up-supported", {true, ValueType::INTEGER}},
+      {"operations-supported", {true, ValueType::INTEGER}},
+      {"orientation-requested", {false, ValueType::INTEGER}},
+      {"orientation-requested-default", {false, ValueType::INTEGER}},
+      {"orientation-requested-supported", {true, ValueType::INTEGER}},
+      {"output-bin", {false, ValueType::STRING}},
+      {"output-bin-default", {false, ValueType::STRING}},
+      {"output-bin-supported", {true, ValueType::STRING}},
+      {"page-border", {false, ValueType::STRING}},
+      {"page-ranges", {true, ValueType::INTEGER}},
+      {"page-ranges-supported", {false, ValueType::BOOLEAN}},
+      {"pages-per-minute", {false, ValueType::INTEGER}},
+      {"pages-per-minute-color", {false, ValueType::INTEGER}},
+      {"pdl-override-supported", {false, ValueType::STRING}},
+      {"print-quality", {false, ValueType::INTEGER}},
+      {"print-quality-default", {false, ValueType::INTEGER}},
+      {"print-quality-supported", {true, ValueType::INTEGER}},
+      {"printer-alert", {true, ValueType::STRING}},
+      {"printer-alert-description", {true, ValueType::STRING}},
+      {"printer-device-id", {false, ValueType::STRING}},
+      {"printer-dns-sd-name", {false, ValueType::STRING}},
+      {"printer-id", {false, ValueType::INTEGER}},
+      {"printer-info", {false, ValueType::STRING}},
+      {"printer-is-accepting-jobs", {false, ValueType::BOOLEAN}},
+      {"printer-location", {false, ValueType::STRING}},
+      {"printer-make-and-model", {false, ValueType::STRING}},
+      {"printer-more-info", {false, ValueType::STRING}},
+      {"printer-name", {false, ValueType::STRING}},
+      {"printer-resolution", {true, ValueType::INTEGER}},
+      {"printer-resolution-default", {true, ValueType::INTEGER}},
+      {"printer-resolution-supported", {true, ValueType::INTEGER}},
+      {"printer-state", {false, ValueType::INTEGER}},
+      {"printer-state-reasons", {true, ValueType::STRING}},
+      {"printer-type", {false, ValueType::INTEGER}},
+      {"printer-type-mask", {false, ValueType::INTEGER}},
+      {"printer-up-time", {false, ValueType::INTEGER}},
+      {"printer-uri", {false, ValueType::STRING}},
+      {"printer-uri-supported", {true, ValueType::STRING}},
+      {"queued-job-count", {false, ValueType::INTEGER}},
+      {"requested-attributes", {true, ValueType::STRING}},
+      {"requesting-user-name", {false, ValueType::STRING}},
+      {"requesting-user-name-allowed", {false, ValueType::STRING}},
+      {"requesting-user-name-denied", {false, ValueType::STRING}},
+      {"sides", {false, ValueType::STRING}},
+      {"sides-default", {false, ValueType::STRING}},
+      {"sides-supported", {true, ValueType::STRING}},
+      {"status-message", {false, ValueType::STRING}},
+      {"time-at-completed", {false, ValueType::INTEGER}},
+      {"time-at-creation", {false, ValueType::INTEGER}},
+      {"time-at-processing", {false, ValueType::INTEGER}},
+      {"uri-authentication-supported", {true, ValueType::STRING}},
+      {"uri-security-supported", {true, ValueType::STRING}},
+      {"which-jobs", {false, ValueType::STRING}}};
+
+  // Allowed IPP Operations.
+  static const std::set<ipp_op_t> kOperationIds{
+      IPP_OP_CANCEL_JOB,         IPP_OP_CREATE_JOB,
+      IPP_OP_CUPS_GET_DEFAULT,   IPP_OP_CUPS_GET_PPD,
+      IPP_OP_CUPS_GET_PRINTERS,  IPP_OP_GET_JOBS,
+      IPP_OP_GET_JOB_ATTRIBUTES, IPP_OP_GET_PRINTER_ATTRIBUTES,
+      IPP_OP_HOLD_JOB,           IPP_OP_PAUSE_PRINTER,
+      IPP_OP_PRINT_JOB,          IPP_OP_PRINT_URI,
+      IPP_OP_RELEASE_JOB,        IPP_OP_RESUME_PRINTER,
+      IPP_OP_SEND_DOCUMENT,      IPP_OP_SEND_URI,
+      IPP_OP_VALIDATE_JOB,
+  };
+
   // Ensure |ipp_oper_id| is an allowed operation.
   if (!base::Contains(kOperationIds, ipp_oper_id)) {
     return ValidateAttributeResult::kFatalError;
diff --git a/chrome/test/BUILD.gn b/chrome/test/BUILD.gn
index 4e2c2aa3..96c58a1 100644
--- a/chrome/test/BUILD.gn
+++ b/chrome/test/BUILD.gn
@@ -1001,7 +1001,7 @@
       "../browser/ntp_snippets/content_suggestions_service_factory_browsertest.cc",
       "../browser/ntp_tiles/ntp_tiles_browsertest.cc",
       "../browser/optimization_guide/optimization_guide_keyed_service_browsertest.cc",
-      "../browser/optimization_guide/optimization_guide_prediction_manager_browsertest.cc",
+      "../browser/optimization_guide/prediction/prediction_manager_browsertest.cc",
       "../browser/page_load_metrics/observers/ad_metrics/ads_page_load_metrics_observer_browsertest.cc",
       "../browser/page_load_metrics/observers/amp_page_load_metrics_observer_browsertest.cc",
       "../browser/page_load_metrics/observers/data_saver_site_breakdown_metrics_observer_browsertest.cc",
@@ -3038,6 +3038,8 @@
     "../browser/optimization_guide/optimization_guide_navigation_data_unittest.cc",
     "../browser/optimization_guide/optimization_guide_session_statistic_unittest.cc",
     "../browser/optimization_guide/optimization_guide_top_host_provider_unittest.cc",
+    "../browser/optimization_guide/prediction/decision_tree_prediction_model_unittest.cc",
+    "../browser/optimization_guide/prediction/prediction_model_unittest.cc",
     "../browser/page_load_metrics/metrics_web_contents_observer_unittest.cc",
     "../browser/page_load_metrics/observers/aborts_page_load_metrics_observer_unittest.cc",
     "../browser/page_load_metrics/observers/ad_metrics/ads_page_load_metrics_observer_unittest.cc",
diff --git a/chrome/test/base/browser_with_test_window_test.cc b/chrome/test/base/browser_with_test_window_test.cc
index 9fa8ff6..b54a78f 100644
--- a/chrome/test/base/browser_with_test_window_test.cc
+++ b/chrome/test/base/browser_with_test_window_test.cc
@@ -9,6 +9,7 @@
 #include "base/single_thread_task_runner.h"
 #include "base/threading/thread_task_runner_handle.h"
 #include "build/build_config.h"
+#include "chrome/browser/net/system_network_context_manager.h"
 #include "chrome/browser/profiles/profile_destroyer.h"
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/browser_navigator.h"
@@ -104,6 +105,12 @@
 
   profile_manager_->DeleteAllTestingProfiles();
   profile_ = nullptr;
+
+  // Depends on LocalState owned by |profile_manager_|.
+  if (SystemNetworkContextManager::GetInstance()) {
+    SystemNetworkContextManager::DeleteInstance();
+  }
+
   profile_manager_.reset();
 
 #if defined(OS_CHROMEOS)
diff --git a/chrome/test/base/in_process_browser_test.cc b/chrome/test/base/in_process_browser_test.cc
index d3b9be4e..28dbb2f 100644
--- a/chrome/test/base/in_process_browser_test.cc
+++ b/chrome/test/base/in_process_browser_test.cc
@@ -164,6 +164,14 @@
 }
 #endif
 
+std::unique_ptr<storage::QuotaSettings>
+InProcessBrowserTest::CreateQuotaSettings() {
+  // By default use hardcoded quota settings to have a consistent testing
+  // environment.
+  const int kQuota = 5 * 1024 * 1024;
+  return std::make_unique<storage::QuotaSettings>(kQuota * 5, kQuota, 0, 0);
+}
+
 void InProcessBrowserTest::Initialize() {
   CreateTestServer(GetChromeTestDataDir());
   base::FilePath src_dir;
@@ -277,11 +285,9 @@
   ash::ShellTestApi::SetTabletControllerUseScreenshotForTest(false);
 #endif  // defined(OS_CHROMEOS)
 
-  // Use hardcoded quota settings to have a consistent testing environment.
-  const int kQuota = 5 * 1024 * 1024;
-  quota_settings_ = storage::QuotaSettings(kQuota * 5, kQuota, 0, 0);
+  quota_settings_ = CreateQuotaSettings();
   ChromeContentBrowserClient::SetDefaultQuotaSettingsForTesting(
-      &quota_settings_);
+      quota_settings_.get());
 
   // Redirect the default download directory to a temporary directory.
   ASSERT_TRUE(default_download_dir_.CreateUniqueTempDir());
diff --git a/chrome/test/base/in_process_browser_test.h b/chrome/test/base/in_process_browser_test.h
index dd88908..0cc16a2 100644
--- a/chrome/test/base/in_process_browser_test.h
+++ b/chrome/test/base/in_process_browser_test.h
@@ -264,6 +264,8 @@
     open_about_blank_on_browser_launch_ = value;
   }
 
+  virtual std::unique_ptr<storage::QuotaSettings> CreateQuotaSettings();
+
  private:
   void Initialize();
 
@@ -291,8 +293,10 @@
   // True if the about:blank tab should be opened when the browser is launched.
   bool open_about_blank_on_browser_launch_ = true;
 
-  // We use hardcoded quota settings to have a consistent testing environment.
-  storage::QuotaSettings quota_settings_;
+  // We use fake quota settings by default to have a consistent testing
+  // environment.  These can be overridden by subclasses via the
+  // CreateQuotaSettings() method.
+  std::unique_ptr<storage::QuotaSettings> quota_settings_;
 
   // Use a default download directory to make sure downloads don't end up in the
   // system default location.
diff --git a/chrome/test/data/chromeos/file_manager/archive.tar.gz b/chrome/test/data/chromeos/file_manager/archive.tar.gz
new file mode 100644
index 0000000..09a42ce
--- /dev/null
+++ b/chrome/test/data/chromeos/file_manager/archive.tar.gz
Binary files differ
diff --git a/chrome/test/data/local_ntp/customize_menu_browsertest.js b/chrome/test/data/local_ntp/customize_menu_browsertest.js
index 663d3bd46..f71392a 100644
--- a/chrome/test/data/local_ntp/customize_menu_browsertest.js
+++ b/chrome/test/data/local_ntp/customize_menu_browsertest.js
@@ -345,7 +345,6 @@
 
   // Check that correct styling is applied to the page.
   assertTrue(document.body.classList.contains('alternate-logo'));
-  assertTrue(document.body.classList.contains('non-white-bg'));
 };
 
 /**
diff --git a/chrome/test/data/pdf/bookmarks_test.js b/chrome/test/data/pdf/bookmarks_test.js
index cf6ba76..a3288e4 100644
--- a/chrome/test/data/pdf/bookmarks_test.js
+++ b/chrome/test/data/pdf/bookmarks_test.js
@@ -29,18 +29,22 @@
 
     // Check bookmark fields.
     chrome.test.assertEq(0, firstBookmark.page);
+    chrome.test.assertEq(133, firstBookmark.x);
     chrome.test.assertEq(667, firstBookmark.y);
     chrome.test.assertEq(undefined, firstBookmark.uri);
 
     chrome.test.assertEq(1, firstNestedBookmark.page);
+    chrome.test.assertEq(133, firstNestedBookmark.x);
     chrome.test.assertEq(667, firstNestedBookmark.y);
     chrome.test.assertEq(undefined, firstNestedBookmark.uri);
 
     chrome.test.assertEq(2, secondBookmark.page);
+    chrome.test.assertEq(133, secondBookmark.x);
     chrome.test.assertEq(667, secondBookmark.y);
     chrome.test.assertEq(undefined, secondBookmark.uri);
 
     chrome.test.assertEq(undefined, uriBookmark.page);
+    chrome.test.assertEq(undefined, uriBookmark.x);
     chrome.test.assertEq(undefined, uriBookmark.y);
     chrome.test.assertEq('http://www.chromium.org', uriBookmark.uri);
 
@@ -103,9 +107,9 @@
       chrome.test.assertEq(expectedEvent.uri, lastUriNavigation);
     }
 
-    testTapTarget(rootBookmarks[0].$.item, {page: 0, x: 0, y: 667})
-    testTapTarget(subBookmarks[0].$.item, {page: 1, x: 0, y: 667})
-    testTapTarget(rootBookmarks[1].$.item, {page: 2, x: 0, y: 667})
+    testTapTarget(rootBookmarks[0].$.item, {page: 0, x: 133, y: 667})
+    testTapTarget(subBookmarks[0].$.item, {page: 1, x: 133, y: 667})
+    testTapTarget(rootBookmarks[1].$.item, {page: 2, x: 133, y: 667})
     testTapTarget(rootBookmarks[2].$.item, {uri: "http://www.chromium.org"})
 
     chrome.test.succeed();
diff --git a/chrome/test/data/policy/policy_test_cases.json b/chrome/test/data/policy/policy_test_cases.json
index 7e5d62d..a5f1a416 100644
--- a/chrome/test/data/policy/policy_test_cases.json
+++ b/chrome/test/data/policy/policy_test_cases.json
@@ -4942,6 +4942,8 @@
     ]
   },
 
+  "CloudExtensionRequestEnabled": {},
+
   "CloudReportingEnabled": {
     "os": ["win", "linux", "mac"],
     "test_policy": { "CloudReportingEnabled": true },
diff --git a/chrome/test/data/webui/chromeos/fake_network_config_mojom.js b/chrome/test/data/webui/chromeos/fake_network_config_mojom.js
index 3c45ff0..270f758a 100644
--- a/chrome/test/data/webui/chromeos/fake_network_config_mojom.js
+++ b/chrome/test/data/webui/chromeos/fake_network_config_mojom.js
@@ -79,7 +79,7 @@
 
     ['getNetworkState', 'getNetworkStateList', 'getDeviceStateList',
      'getManagedProperties', 'setNetworkTypeEnabledState', 'requestNetworkScan',
-     'getGlobalPolicy', 'getVpnProviders']
+     'getGlobalPolicy', 'getVpnProviders', 'getNetworkCertificates']
         .forEach((methodName) => {
           this.resolverMap_.set(methodName, new PromiseResolver());
         });
@@ -213,7 +213,7 @@
   // networkConfig methods
 
   /**
-   * @param { !chromeos.networkConfig.mojom.CrosNetworkConfigObserverProxy }
+   * @param {!chromeos.networkConfig.mojom.CrosNetworkConfigObserverProxy }
    *     observer
    */
   addObserver(observer) {
@@ -314,13 +314,13 @@
     });
   }
 
-  /** @param { !chromeos.networkConfig.mojom.NetworkType } type */
+  /** @param {!chromeos.networkConfig.mojom.NetworkType } type */
   requestNetworkScan(type) {
     this.methodCalled('requestNetworkScan');
   }
 
   /**
-   * @return { !Promise<{result: !chromeos.networkConfig.mojom.GlobalPolicy}>}
+   * @return {!Promise<{result: !chromeos.networkConfig.mojom.GlobalPolicy}>}
    */
   getGlobalPolicy() {
     return new Promise(resolve => {
@@ -330,7 +330,7 @@
   }
 
   /**
-   * @return { !Promise<{
+   * @return {!Promise<{
    *     result: !Array<!chromeos.networkConfig.mojom.VpnProvider>}>}
    */
   getVpnProviders() {
@@ -339,4 +339,16 @@
       resolve({providers: this.vpnProviders_});
     });
   }
+
+  /**
+   * @return {!Promise<{
+   *     serverCas: !Array<!chromeos.networkConfig.mojom.NetworkCertificate>,
+   *     userCerts: !Array<!chromeos.networkConfig.mojom.NetworkCertificate>}>}
+   */
+  getNetworkCertificates() {
+    return new Promise(resolve => {
+      this.methodCalled('getNetworkCertificates');
+      resolve({serverCas: [], userCerts: []});
+    });
+  }
 }
diff --git a/chrome/test/data/webui/cr_elements/cr_toolbar_search_field_tests.js b/chrome/test/data/webui/cr_elements/cr_toolbar_search_field_tests.js
index 3907a0a..2512a4e 100644
--- a/chrome/test/data/webui/cr_elements/cr_toolbar_search_field_tests.js
+++ b/chrome/test/data/webui/cr_elements/cr_toolbar_search_field_tests.js
@@ -86,7 +86,9 @@
     const clearSearch = field.$$('#clearSearch');
     clearSearch.focus();
     clearSearch.click();
-    assertTrue(field.showingSearch);
+    Polymer.dom.flush();
+
+    assertFalse(field.showingSearch);
     assertEquals('', field.getValue());
     assertEquals(field.$.searchInput, field.root.activeElement);
     assertFalse(field.hasSearchText);
@@ -99,7 +101,8 @@
     assertEquals('query1', field.getValue());
 
     field.$$('#clearSearch').click();
-    assertTrue(field.showingSearch);
+    Polymer.dom.flush();
+    assertFalse(field.showingSearch);
     assertEquals('', field.getValue());
 
     simulateSearch('query2');
diff --git a/chrome/test/delayload/delayloads_unittest.cc b/chrome/test/delayload/delayloads_unittest.cc
index fb6c0320..aec51a7 100644
--- a/chrome/test/delayload/delayloads_unittest.cc
+++ b/chrome/test/delayload/delayloads_unittest.cc
@@ -91,12 +91,30 @@
          "target was built, instead of delayloads_unittests.exe";
 
   static const char* const kValidFilePatterns[] = {
-      "KERNEL32.dll",
-      "chrome_elf.dll",
-      // On 64 bit the Version API's like VerQueryValue come from VERSION.dll.
-      // It depends on kernel32, advapi32 and api-ms-win-crt*.dll. This should
-      // be ok.
-      "VERSION.dll",
+    "KERNEL32.dll",
+    "chrome_elf.dll",
+#if !defined(CHROME_MULTIPLE_DLL_BROWSER)
+    "DWrite.dll",
+    "ADVAPI32.dll",
+    "CRYPT32.dll",
+    "dbghelp.dll",
+    "dhcpcsvc.DLL",
+    "IPHLPAPI.DLL",
+    "ntdll.dll",
+    "OLEAUT32.dll",
+    "Secur32.dll",
+    "UIAutomationCore.DLL",
+    "USERENV.dll",
+    "WINHTTP.dll",
+    "WINMM.dll",
+    "WINSPOOL.DRV",
+    "WINTRUST.dll",
+    "WS2_32.dll",
+#endif  //  CHROME_MULTIPLE_DLL_BROWSER
+    // On 64 bit the Version API's like VerQueryValue come from VERSION.dll.
+    // It depends on kernel32, advapi32 and api-ms-win-crt*.dll. This should
+    // be ok.
+    "VERSION.dll",
   };
 
   // Make sure all of chrome.dll's imports are in the valid imports list.
@@ -156,6 +174,8 @@
   EXPECT_EQ(nullptr, ::GetModuleHandle(L"user32.dll"));
 }
 
+#if defined(CHROME_MULTIPLE_DLL_BROWSER)
+
 TEST_F(DelayloadsTest, ChromeChildDllDelayloadsCheck) {
   base::FilePath dll;
   ASSERT_TRUE(base::PathService::Get(base::DIR_EXE, &dll));
@@ -255,6 +275,8 @@
   }
 }
 
+#endif  // CHROME_MULTIPLE_DLL_BROWSER
+
 TEST_F(DelayloadsTest, ChromeElfDllDelayloadsCheck) {
   base::FilePath dll;
   ASSERT_TRUE(base::PathService::Get(base::DIR_EXE, &dll));
diff --git a/chromecast/browser/extensions/api/tabs/tabs_api.cc b/chromecast/browser/extensions/api/tabs/tabs_api.cc
index 039e255..e9dd207c 100644
--- a/chromecast/browser/extensions/api/tabs/tabs_api.cc
+++ b/chromecast/browser/extensions/api/tabs/tabs_api.cc
@@ -31,7 +31,6 @@
 #include "content/public/browser/browser_context.h"
 #include "content/public/browser/navigation_controller.h"
 #include "content/public/browser/navigation_entry.h"
-#include "content/public/common/page_zoom.h"
 #include "extensions/browser/extension_api_frame_id_map.h"
 #include "extensions/browser/extension_zoom_request_client.h"
 #include "extensions/common/api/extension_types.h"
@@ -42,6 +41,7 @@
 #include "extensions/common/manifest_constants.h"
 #include "extensions/common/message_bundle.h"
 #include "extensions/common/permissions/permissions_data.h"
+#include "third_party/blink/public/common/page/page_zoom.h"
 #include "ui/base/models/list_selection_model.h"
 #include "ui/base/ui_base_types.h"
 
@@ -863,9 +863,10 @@
 
   ZoomController* zoom_controller =
       ZoomController::FromWebContents(web_contents);
-  double zoom_level = params->zoom_factor > 0
-                          ? content::ZoomFactorToZoomLevel(params->zoom_factor)
-                          : zoom_controller->GetDefaultZoomLevel();
+  double zoom_level =
+      params->zoom_factor > 0
+          ? blink::PageZoomFactorToZoomLevel(params->zoom_factor)
+          : zoom_controller->GetDefaultZoomLevel();
 
   scoped_refptr<ExtensionZoomRequestClient> client(
       new ExtensionZoomRequestClient(extension()));
@@ -892,7 +893,7 @@
   WebContents* web_contents = contents->web_contents();
   double zoom_level =
       ZoomController::FromWebContents(web_contents)->GetZoomLevel();
-  double zoom_factor = content::ZoomLevelToZoomFactor(zoom_level);
+  double zoom_factor = blink::PageZoomLevelToZoomFactor(zoom_level);
   return RespondNow(ArgumentList(tabs::GetZoom::Results::Create(zoom_factor)));
 }
 
@@ -970,8 +971,9 @@
   ZoomController::ZoomMode zoom_mode = zoom_controller->zoom_mode();
   api::tabs::ZoomSettings zoom_settings;
   ZoomModeToZoomSettings(zoom_mode, &zoom_settings);
-  zoom_settings.default_zoom_factor.reset(new double(
-      content::ZoomLevelToZoomFactor(zoom_controller->GetDefaultZoomLevel())));
+  zoom_settings.default_zoom_factor.reset(
+      new double(blink::PageZoomLevelToZoomFactor(
+          zoom_controller->GetDefaultZoomLevel())));
 
   return RespondNow(
       ArgumentList(api::tabs::GetZoomSettings::Results::Create(zoom_settings)));
diff --git a/chromeos/network/network_certificate_handler.cc b/chromeos/network/network_certificate_handler.cc
index c251f29..a317a84 100644
--- a/chromeos/network/network_certificate_handler.cc
+++ b/chromeos/network/network_certificate_handler.cc
@@ -64,16 +64,18 @@
   NetworkCertLoader::Get()->RemoveObserver(this);
 }
 
-void NetworkCertificateHandler::AddObserver(
-    NetworkCertificateHandler::Observer* observer) {
+void NetworkCertificateHandler::AddObserver(Observer* observer) {
   observer_list_.AddObserver(observer);
 }
 
-void NetworkCertificateHandler::RemoveObserver(
-    NetworkCertificateHandler::Observer* observer) {
+void NetworkCertificateHandler::RemoveObserver(Observer* observer) {
   observer_list_.RemoveObserver(observer);
 }
 
+bool NetworkCertificateHandler::HasObserver(Observer* observer) {
+  return observer_list_.HasObserver(observer);
+}
+
 void NetworkCertificateHandler::AddAuthorityCertificateForTest(
     const std::string& issued_to) {
   Certificate cert;
diff --git a/chromeos/network/network_certificate_handler.h b/chromeos/network/network_certificate_handler.h
index d867ff7..732de1b5 100644
--- a/chromeos/network/network_certificate_handler.h
+++ b/chromeos/network/network_certificate_handler.h
@@ -69,6 +69,7 @@
 
   void AddObserver(Observer* observer);
   void RemoveObserver(Observer* observer);
+  bool HasObserver(Observer* observer);
 
   const std::vector<Certificate>& server_ca_certificates() const {
     return server_ca_certificates_;
diff --git a/chromeos/network/network_state.cc b/chromeos/network/network_state.cc
index 24ab90f..2263dc0 100644
--- a/chromeos/network/network_state.cc
+++ b/chromeos/network/network_state.cc
@@ -411,6 +411,7 @@
          connection_state_ == shill::kStateOffline ||
          connection_state_ == shill::kStateOnline ||
          connection_state_ == shill::kStateFailure ||
+         connection_state_ == shill::kStateDisconnect ||
          // TODO(https://crbug.com/552190): Remove kStateActivationFailure from
          // this list when occurrences in chromium code have been eliminated.
          connection_state_ == shill::kStateActivationFailure ||
diff --git a/chromeos/services/assistant/platform/network_provider_impl.h b/chromeos/services/assistant/platform/network_provider_impl.h
index bc509d45..57f65c5 100644
--- a/chromeos/services/assistant/platform/network_provider_impl.h
+++ b/chromeos/services/assistant/platform/network_provider_impl.h
@@ -35,6 +35,7 @@
   void OnNetworkStateListChanged() override {}
   void OnDeviceStateListChanged() override {}
   void OnVpnProvidersChanged() override {}
+  void OnNetworkCertificatesChanged() override {}
 
  private:
   ConnectionStatus connection_status_;
diff --git a/chromeos/services/network_config/cros_network_config.cc b/chromeos/services/network_config/cros_network_config.cc
index 6d2a3ce..e7d389f 100644
--- a/chromeos/services/network_config/cros_network_config.cc
+++ b/chromeos/services/network_config/cros_network_config.cc
@@ -1595,6 +1595,23 @@
   return onc;
 }
 
+mojom::NetworkCertificatePtr GetMojoCert(
+    const NetworkCertificateHandler::Certificate& cert,
+    mojom::CertificateType type) {
+  auto result = mojom::NetworkCertificate::New();
+  result->type = type;
+  result->hash = cert.hash;
+  result->issued_by = cert.issued_by;
+  result->issued_to = cert.issued_to;
+  result->hardware_backed = cert.hardware_backed;
+  result->device_wide = cert.device_wide;
+  if (type == mojom::CertificateType::kServerCA)
+    result->pem_or_id = cert.pem;
+  if (type == mojom::CertificateType::kUserCert)
+    result->pem_or_id = cert.pkcs11_id;
+  return result;
+}
+
 }  // namespace
 
 CrosNetworkConfig::CrosNetworkConfig()
@@ -1602,23 +1619,30 @@
           NetworkHandler::Get()->network_state_handler(),
           NetworkHandler::Get()->network_device_handler(),
           NetworkHandler::Get()->managed_network_configuration_handler(),
-          NetworkHandler::Get()->network_connection_handler()) {}
+          NetworkHandler::Get()->network_connection_handler(),
+          NetworkHandler::Get()->network_certificate_handler()) {}
 
 CrosNetworkConfig::CrosNetworkConfig(
     NetworkStateHandler* network_state_handler,
     NetworkDeviceHandler* network_device_handler,
     ManagedNetworkConfigurationHandler* network_configuration_handler,
-    NetworkConnectionHandler* network_connection_handler)
+    NetworkConnectionHandler* network_connection_handler,
+    NetworkCertificateHandler* network_certificate_handler)
     : network_state_handler_(network_state_handler),
       network_device_handler_(network_device_handler),
       network_configuration_handler_(network_configuration_handler),
-      network_connection_handler_(network_connection_handler) {
+      network_connection_handler_(network_connection_handler),
+      network_certificate_handler_(network_certificate_handler) {
   CHECK(network_state_handler);
 }
 
 CrosNetworkConfig::~CrosNetworkConfig() {
   if (network_state_handler_->HasObserver(this))
     network_state_handler_->RemoveObserver(this, FROM_HERE);
+  if (network_certificate_handler_ &&
+      network_certificate_handler_->HasObserver(this)) {
+    network_certificate_handler_->RemoveObserver(this);
+  }
 }
 
 void CrosNetworkConfig::BindRequest(mojom::CrosNetworkConfigRequest request) {
@@ -1630,6 +1654,10 @@
     mojom::CrosNetworkConfigObserverPtr observer) {
   if (!network_state_handler_->HasObserver(this))
     network_state_handler_->AddObserver(this, FROM_HERE);
+  if (network_certificate_handler_ &&
+      !network_certificate_handler_->HasObserver(this)) {
+    network_certificate_handler_->AddObserver(this);
+  }
   observers_.AddPtr(std::move(observer));
 }
 
@@ -2372,6 +2400,24 @@
   std::move(callback).Run(mojo::Clone(vpn_providers_));
 }
 
+void CrosNetworkConfig::GetNetworkCertificates(
+    GetNetworkCertificatesCallback callback) {
+  const std::vector<NetworkCertificateHandler::Certificate>&
+      handler_server_cas =
+          network_certificate_handler_->server_ca_certificates();
+  std::vector<mojom::NetworkCertificatePtr> server_cas;
+  for (const auto& cert : handler_server_cas)
+    server_cas.push_back(GetMojoCert(cert, mojom::CertificateType::kServerCA));
+
+  std::vector<mojom::NetworkCertificatePtr> user_certs;
+  const std::vector<NetworkCertificateHandler::Certificate>&
+      handler_user_certs = network_certificate_handler_->client_certificates();
+  for (const auto& cert : handler_user_certs)
+    user_certs.push_back(GetMojoCert(cert, mojom::CertificateType::kUserCert));
+
+  std::move(callback).Run(std::move(server_cas), std::move(user_certs));
+}
+
 // NetworkStateHandlerObserver
 void CrosNetworkConfig::NetworkListChanged() {
   observers_.ForAllPtrs([](mojom::CrosNetworkConfigObserver* observer) {
@@ -2422,6 +2468,12 @@
   network_state_handler_ = nullptr;
 }
 
+void CrosNetworkConfig::OnCertificatesChanged() {
+  observers_.ForAllPtrs([](mojom::CrosNetworkConfigObserver* observer) {
+    observer->OnNetworkCertificatesChanged();
+  });
+}
+
 const std::string& CrosNetworkConfig::GetServicePathFromGuid(
     const std::string& guid) {
   const chromeos::NetworkState* network =
diff --git a/chromeos/services/network_config/cros_network_config.h b/chromeos/services/network_config/cros_network_config.h
index e7e95a3..723e9b7 100644
--- a/chromeos/services/network_config/cros_network_config.h
+++ b/chromeos/services/network_config/cros_network_config.h
@@ -7,6 +7,7 @@
 
 #include "base/containers/flat_map.h"
 #include "base/memory/weak_ptr.h"
+#include "chromeos/network/network_certificate_handler.h"
 #include "chromeos/network/network_state_handler_observer.h"
 #include "chromeos/services/network_config/public/mojom/cros_network_config.mojom.h"
 #include "mojo/public/cpp/bindings/binding_set.h"
@@ -26,7 +27,8 @@
 namespace network_config {
 
 class CrosNetworkConfig : public mojom::CrosNetworkConfig,
-                          public NetworkStateHandlerObserver {
+                          public NetworkStateHandlerObserver,
+                          public NetworkCertificateHandler::Observer {
  public:
   // Constructs an instance of CrosNetworkConfig with default network subsystem
   // dependencies appropriate for a production environment.
@@ -38,7 +40,8 @@
       NetworkStateHandler* network_state_handler,
       NetworkDeviceHandler* network_device_handler,
       ManagedNetworkConfigurationHandler* network_configuration_handler,
-      NetworkConnectionHandler* network_connection_handler);
+      NetworkConnectionHandler* network_connection_handler,
+      NetworkCertificateHandler* network_certificate_handler);
   ~CrosNetworkConfig() override;
 
   void BindRequest(mojom::CrosNetworkConfigRequest request);
@@ -78,6 +81,7 @@
                        StartDisconnectCallback callback) override;
   void SetVpnProviders(std::vector<mojom::VpnProviderPtr> providers) override;
   void GetVpnProviders(GetVpnProvidersCallback callback) override;
+  void GetNetworkCertificates(GetNetworkCertificatesCallback callback) override;
 
  private:
   void GetManagedPropertiesSuccess(int callback_id,
@@ -146,13 +150,17 @@
   void DevicePropertiesUpdated(const DeviceState* device) override;
   void OnShuttingDown() override;
 
+  // NetworkCertificateHandler::Observer
+  void OnCertificatesChanged() override;
+
   const std::string& GetServicePathFromGuid(const std::string& guid);
 
   NetworkStateHandler* network_state_handler_;    // Unowned
   NetworkDeviceHandler* network_device_handler_;  // Unowned
   ManagedNetworkConfigurationHandler*
-      network_configuration_handler_;                     // Unowned
-  NetworkConnectionHandler* network_connection_handler_;  // Unowned
+      network_configuration_handler_;                       // Unowned
+  NetworkConnectionHandler* network_connection_handler_;    // Unowned
+  NetworkCertificateHandler* network_certificate_handler_;  // Unowned
 
   mojo::InterfacePtrSet<mojom::CrosNetworkConfigObserver> observers_;
   mojo::BindingSet<mojom::CrosNetworkConfig> bindings_;
diff --git a/chromeos/services/network_config/cros_network_config_unittest.cc b/chromeos/services/network_config/cros_network_config_unittest.cc
index f34dade..7ede5ef 100644
--- a/chromeos/services/network_config/cros_network_config_unittest.cc
+++ b/chromeos/services/network_config/cros_network_config_unittest.cc
@@ -14,6 +14,8 @@
 #include "chromeos/dbus/shill/fake_shill_device_client.h"
 #include "chromeos/login/login_state/login_state.h"
 #include "chromeos/network/managed_network_configuration_handler.h"
+#include "chromeos/network/network_cert_loader.h"
+#include "chromeos/network/network_certificate_handler.h"
 #include "chromeos/network/network_configuration_handler.h"
 #include "chromeos/network/network_connection_handler.h"
 #include "chromeos/network/network_device_handler.h"
@@ -42,6 +44,7 @@
  public:
   CrosNetworkConfigTest() {
     LoginState::Initialize();
+    NetworkCertLoader::Initialize();
     network_profile_handler_ = NetworkProfileHandler::InitializeForTesting();
     network_device_handler_ = NetworkDeviceHandler::InitializeForTesting(
         helper_.network_state_handler());
@@ -60,20 +63,25 @@
             helper_.network_state_handler(),
             network_configuration_handler_.get(),
             managed_network_configuration_handler_.get());
+    network_certificate_handler_ =
+        std::make_unique<NetworkCertificateHandler>();
     cros_network_config_ = std::make_unique<CrosNetworkConfig>(
         helper_.network_state_handler(), network_device_handler_.get(),
         managed_network_configuration_handler_.get(),
-        network_connection_handler_.get());
+        network_connection_handler_.get(), network_certificate_handler_.get());
     SetupPolicy();
     SetupNetworks();
   }
 
   ~CrosNetworkConfigTest() override {
     cros_network_config_.reset();
+    network_certificate_handler_.reset();
+    network_connection_handler_.reset();
     managed_network_configuration_handler_.reset();
     network_configuration_handler_.reset();
     network_device_handler_.reset();
     network_profile_handler_.reset();
+    NetworkCertLoader::Shutdown();
     LoginState::Shutdown();
   }
 
@@ -386,6 +394,24 @@
     return result;
   }
 
+  void GetNetworkCertificates(
+      std::vector<mojom::NetworkCertificatePtr>* server_cas,
+      std::vector<mojom::NetworkCertificatePtr>* user_certs) {
+    base::RunLoop run_loop;
+    cros_network_config()->GetNetworkCertificates(base::BindOnce(
+        [](std::vector<mojom::NetworkCertificatePtr>* server_cas_result,
+           std::vector<mojom::NetworkCertificatePtr>* user_certs_result,
+           base::OnceClosure quit_closure,
+           std::vector<mojom::NetworkCertificatePtr> server_cas,
+           std::vector<mojom::NetworkCertificatePtr> user_certs) {
+          *server_cas_result = std::move(server_cas);
+          *user_certs_result = std::move(user_certs);
+          std::move(quit_closure).Run();
+        },
+        server_cas, user_certs, run_loop.QuitClosure()));
+    run_loop.Run();
+  }
+
   NetworkStateTestHelper& helper() { return helper_; }
   CrosNetworkConfigTestObserver* observer() { return observer_.get(); }
   CrosNetworkConfig* cros_network_config() {
@@ -394,11 +420,15 @@
   ManagedNetworkConfigurationHandler* managed_network_configuration_handler() {
     return managed_network_configuration_handler_.get();
   }
+  NetworkCertificateHandler* network_certificate_handler() {
+    return network_certificate_handler_.get();
+  }
   std::string wifi1_path() { return wifi1_path_; }
 
  private:
   base::test::SingleThreadTaskEnvironment task_environment_;
   NetworkStateTestHelper helper_{false /* use_default_devices_and_services */};
+  std::unique_ptr<NetworkCertificateHandler> network_certificate_handler_;
   std::unique_ptr<NetworkProfileHandler> network_profile_handler_;
   std::unique_ptr<NetworkDeviceHandler> network_device_handler_;
   std::unique_ptr<NetworkConfigurationHandler> network_configuration_handler_;
@@ -1012,6 +1042,26 @@
   ASSERT_EQ(1, observer()->vpn_providers_changed());
 }
 
+TEST_F(CrosNetworkConfigTest, NetworkCertificates) {
+  SetupObserver();
+  ASSERT_EQ(0, observer()->network_certificates_changed());
+
+  std::vector<mojom::NetworkCertificatePtr> server_cas;
+  std::vector<mojom::NetworkCertificatePtr> user_certs;
+  GetNetworkCertificates(&server_cas, &user_certs);
+  EXPECT_EQ(0u, server_cas.size());
+  EXPECT_EQ(0u, user_certs.size());
+
+  network_certificate_handler()->AddAuthorityCertificateForTest(
+      "authority_cert");
+  base::RunLoop().RunUntilIdle();  // Ensure observers run.
+  ASSERT_EQ(1, observer()->network_certificates_changed());
+
+  GetNetworkCertificates(&server_cas, &user_certs);
+  EXPECT_EQ(1u, server_cas.size());
+  EXPECT_EQ(0u, user_certs.size());
+}
+
 TEST_F(CrosNetworkConfigTest, NetworkListChanged) {
   SetupObserver();
   base::RunLoop().RunUntilIdle();
diff --git a/chromeos/services/network_config/public/cpp/cros_network_config_test_helper.cc b/chromeos/services/network_config/public/cpp/cros_network_config_test_helper.cc
index ed9ec0be..477ccbb 100644
--- a/chromeos/services/network_config/public/cpp/cros_network_config_test_helper.cc
+++ b/chromeos/services/network_config/public/cpp/cros_network_config_test_helper.cc
@@ -20,7 +20,8 @@
           network_state_helper_.network_state_handler(),
           network_device_handler_.get(),
           /*network_configuration_handler=*/nullptr,
-          /*network_connection_handler=*/nullptr);
+          /*network_connection_handler=*/nullptr,
+          /*network_certificate_handler=*/nullptr);
   OverrideInProcessInstanceForTesting(cros_network_config_impl_.get());
 }
 
diff --git a/chromeos/services/network_config/public/cpp/cros_network_config_test_observer.cc b/chromeos/services/network_config/public/cpp/cros_network_config_test_observer.cc
index f58ceab7..fa79c7a 100644
--- a/chromeos/services/network_config/public/cpp/cros_network_config_test_observer.cc
+++ b/chromeos/services/network_config/public/cpp/cros_network_config_test_observer.cc
@@ -50,12 +50,17 @@
   vpn_providers_changed_++;
 }
 
+void CrosNetworkConfigTestObserver::OnNetworkCertificatesChanged() {
+  network_certificates_changed_++;
+}
+
 void CrosNetworkConfigTestObserver::ResetNetworkChanges() {
   active_networks_changed_ = 0;
   networks_changed_.clear();
   network_state_list_changed_ = 0;
   device_state_list_changed_ = 0;
   vpn_providers_changed_ = 0;
+  network_certificates_changed_ = 0;
 }
 
 void CrosNetworkConfigTestObserver::FlushForTesting() {
diff --git a/chromeos/services/network_config/public/cpp/cros_network_config_test_observer.h b/chromeos/services/network_config/public/cpp/cros_network_config_test_observer.h
index 17d2ce42..5f95827 100644
--- a/chromeos/services/network_config/public/cpp/cros_network_config_test_observer.h
+++ b/chromeos/services/network_config/public/cpp/cros_network_config_test_observer.h
@@ -31,11 +31,15 @@
   void OnNetworkStateListChanged() override;
   void OnDeviceStateListChanged() override;
   void OnVpnProvidersChanged() override;
+  void OnNetworkCertificatesChanged() override;
 
   int active_networks_changed() const { return active_networks_changed_; }
   int network_state_list_changed() const { return network_state_list_changed_; }
   int device_state_list_changed() const { return device_state_list_changed_; }
   int vpn_providers_changed() const { return vpn_providers_changed_; }
+  int network_certificates_changed() const {
+    return network_certificates_changed_;
+  }
 
   int GetNetworkChangedCount(const std::string& guid) const;
   void ResetNetworkChanges();
@@ -53,6 +57,7 @@
   int network_state_list_changed_ = 0;
   int device_state_list_changed_ = 0;
   int vpn_providers_changed_ = 0;
+  int network_certificates_changed_ = 0;
 
   DISALLOW_COPY_AND_ASSIGN(CrosNetworkConfigTestObserver);
 };
diff --git a/chromeos/services/network_config/public/mojom/cros_network_config.mojom b/chromeos/services/network_config/public/mojom/cros_network_config.mojom
index 2c960be..f5bd8e5c 100644
--- a/chromeos/services/network_config/public/mojom/cros_network_config.mojom
+++ b/chromeos/services/network_config/public/mojom/cros_network_config.mojom
@@ -165,6 +165,11 @@
   kUnknown,
 };
 
+enum CertificateType {
+  kServerCA,
+  kUserCert,
+};
+
 struct CaptivePortalProvider {
   // Id used to identify the captive portal provider (i.e. an extension id).
   string id;
@@ -807,6 +812,32 @@
   mojo_base.mojom.Time last_launch_time;
 };
 
+// Information about a network certificate for the purpose of selecting an
+// available certificate in a UI and providing information in a ConfigProperties
+// struct to SetProperties or ConfigureNetwork. No private information should
+// be included here.
+struct NetworkCertificate {
+  CertificateType type;
+  // Unique hash for the certificate, used to uniquely identify certificates.
+  string hash;
+  // Certificate issuer common name for display in a UI.
+  string issued_by;
+  // Certificate name or nickname for display in a UI.
+  string issued_to;
+  // For server certificate authorities (type == kServerCA), this contains the
+  // public certificate in PEM format.
+  // For user certificates, this contains the  PKCS#11 id to be passed to the
+  // configuration manager (Shill) for retrieving the encrypted certificate.
+  // This will be used in the appropriate ConfigProperties dictionary.
+  // TODO(1006901): Use a union here instead once the issue is fixed.
+  string pem_or_id;
+  // Whether the certificate is hardware backed.
+  bool hardware_backed;
+  // Whether the certificate is device wide (i.e. stored in a shared profile,
+  // not a user specific profile).
+  bool device_wide;
+};
+
 // Interface for fetching and setting network configuration properties, e.g.
 // from Settings WebUI or the SystemTray.
 interface CrosNetworkConfig {
@@ -905,6 +936,11 @@
 
   // Returns the list of external VPN providers.
   GetVpnProviders() => (array<VpnProvider> providers);
+
+  // Returns the lists of server certificate authorities and user certificates
+  // available for network configuration. See NetworkCerificate for more info.
+  GetNetworkCertificates() => (array<NetworkCertificate> server_cas,
+                               array<NetworkCertificate> user_certs);
 };
 
 interface CrosNetworkConfigObserver {
@@ -931,4 +967,8 @@
   // Fired when the list of VPN providers changes. Use GetVpnProviders if the
   // updated provider list is required.
   OnVpnProvidersChanged();
+
+  // Fired when the server CA or user certificate lists change. Use
+  // GetNetworkCertificates if the updated certificate lists are required.
+  OnNetworkCertificatesChanged();
 };
diff --git a/cloud_print/virtual_driver/win/install/setup.cc b/cloud_print/virtual_driver/win/install/setup.cc
index a68762d..4901494 100644
--- a/cloud_print/virtual_driver/win/install/setup.cc
+++ b/cloud_print/virtual_driver/win/install/setup.cc
@@ -408,26 +408,27 @@
                    __in HINSTANCE hPrevInstance,
                    __in LPSTR lpCmdLine,
                    __in int nCmdShow) {
-  using namespace cloud_print;
-
   base::AtExitManager at_exit_manager;
-  base::CommandLine::Init(0, NULL);
+  base::CommandLine::Init(0, nullptr);
 
-  HRESULT retval = ExecuteCommands();
+  HRESULT retval = cloud_print::ExecuteCommands();
 
   if (retval == HRESULT_FROM_WIN32(ERROR_BAD_DRIVER)) {
-    SetGoogleUpdateError(kGoogleUpdateProductId,
-                         LoadLocalString(IDS_ERROR_NO_XPS));
+    cloud_print::SetGoogleUpdateError(
+        cloud_print::kGoogleUpdateProductId,
+        cloud_print::LoadLocalString(IDS_ERROR_NO_XPS));
   } else if (FAILED(retval)) {
-    SetGoogleUpdateError(kGoogleUpdateProductId, retval);
+    cloud_print::SetGoogleUpdateError(cloud_print::kGoogleUpdateProductId,
+                                      retval);
   }
 
-  VLOG(0) << GetErrorMessage(retval) << " HRESULT=0x" << std::setbase(16)
-          << retval;
+  VLOG(0) << cloud_print::GetErrorMessage(retval) << " HRESULT=0x"
+          << std::setbase(16) << retval;
 
   // Installer is silent by default as required by Google Update.
   if (base::CommandLine::ForCurrentProcess()->HasSwitch("verbose")) {
-    DisplayWindowsMessage(NULL, retval, LoadLocalString(IDS_DRIVER_NAME));
+    cloud_print::DisplayWindowsMessage(
+        nullptr, retval, cloud_print::LoadLocalString(IDS_DRIVER_NAME));
   }
   return retval;
 }
diff --git a/components/cronet/android/test/cronet_test_util.cc b/components/cronet/android/test/cronet_test_util.cc
index 693a4140..05fa751 100644
--- a/components/cronet/android/test/cronet_test_util.cc
+++ b/components/cronet/android/test/cronet_test_util.cc
@@ -7,7 +7,9 @@
 #include "base/android/jni_android.h"
 #include "base/android/jni_string.h"
 #include "base/bind.h"
-#include "base/message_loop/message_loop.h"
+#include "base/message_loop/message_pump.h"
+#include "base/message_loop/message_pump_type.h"
+#include "base/task/sequence_manager/sequence_manager.h"
 #include "base/threading/thread_task_runner_handle.h"
 #include "components/cronet/android/cronet_tests_jni_headers/CronetTestUtil_jni.h"
 #include "components/cronet/android/cronet_url_request_adapter.h"
@@ -17,13 +19,15 @@
 #include "net/socket/socket_test_util.h"
 #include "net/url_request/url_request.h"
 
-using base::android::JavaParamRef;
-
 namespace cronet {
-
 namespace {
 
-base::MessageLoop* g_message_loop = nullptr;
+using ::base::MessagePump;
+using ::base::MessagePumpType;
+using ::base::android::JavaParamRef;
+using ::base::sequence_manager::SequenceManager;
+
+SequenceManager* g_sequence_manager = nullptr;
 
 }  // namespace
 
@@ -77,8 +81,15 @@
 }
 
 static void PrepareNetworkThreadOnNetworkThread(jlong jcontext_adapter) {
-  g_message_loop = new base::MessageLoopForIO();
-  g_message_loop->SetTaskRunner(TestUtil::GetTaskRunner(jcontext_adapter));
+  g_sequence_manager =
+      base::sequence_manager::CreateSequenceManagerOnCurrentThreadWithPump(
+          MessagePump::Create(MessagePumpType::IO),
+          SequenceManager::Settings::Builder()
+              .SetMessagePumpType(MessagePumpType::IO)
+              .Build())
+          .release();
+  g_sequence_manager->SetDefaultTaskRunner(
+      TestUtil::GetTaskRunner(jcontext_adapter));
 }
 
 // Tests need to call into libcronet.so code on libcronet.so threads.
@@ -97,10 +108,9 @@
 }
 
 static void CleanupNetworkThreadOnNetworkThread() {
-  DCHECK(g_message_loop);
-  DCHECK(g_message_loop->task_runner()->RunsTasksInCurrentSequence());
-  delete g_message_loop;
-  g_message_loop = nullptr;
+  DCHECK(g_sequence_manager);
+  delete g_sequence_manager;
+  g_sequence_manager = nullptr;
 }
 
 // Called from Java CronetTestUtil class.
diff --git a/components/download/internal/common/download_item_impl.cc b/components/download/internal/common/download_item_impl.cc
index cd7b5df..062bbbc 100644
--- a/components/download/internal/common/download_item_impl.cc
+++ b/components/download/internal/common/download_item_impl.cc
@@ -878,7 +878,8 @@
 }
 
 base::FilePath DownloadItemImpl::GetTemporaryFilePath() const {
-  if (state_ == TARGET_PENDING_INTERNAL || INTERRUPTED_TARGET_PENDING_INTERNAL)
+  if (state_ == TARGET_PENDING_INTERNAL ||
+      state_ == INTERRUPTED_TARGET_PENDING_INTERNAL)
     return download_file_ ? download_file_->FullPath() : base::FilePath();
   return base::FilePath();
 }
diff --git a/components/feed/feed_feature_list.cc b/components/feed/feed_feature_list.cc
index 0e48a1ce..25e1493 100644
--- a/components/feed/feed_feature_list.cc
+++ b/components/feed/feed_feature_list.cc
@@ -7,7 +7,7 @@
 namespace feed {
 
 const base::Feature kInterestFeedContentSuggestions{
-    "InterestFeedContentSuggestions", base::FEATURE_DISABLED_BY_DEFAULT};
+    "InterestFeedContentSuggestions", base::FEATURE_ENABLED_BY_DEFAULT};
 
 const base::FeatureParam<std::string> kDisableTriggerTypes{
     &kInterestFeedContentSuggestions, "disable_trigger_types", ""};
diff --git a/components/guest_view/browser/BUILD.gn b/components/guest_view/browser/BUILD.gn
index 00bd7eb..c13fb5b 100644
--- a/components/guest_view/browser/BUILD.gn
+++ b/components/guest_view/browser/BUILD.gn
@@ -36,6 +36,7 @@
     "//content/public/browser",
     "//content/public/common",
     "//ipc",
+    "//third_party/blink/public/common",
     "//url",
   ]
 }
diff --git a/components/guest_view/browser/DEPS b/components/guest_view/browser/DEPS
index 0912edf..e56325b 100644
--- a/components/guest_view/browser/DEPS
+++ b/components/guest_view/browser/DEPS
@@ -3,8 +3,14 @@
   "+components/zoom",
   "+content/public/browser",
   "+content/public/common",
-  "+content/public/test",
   "+ipc",
-  '+third_party/blink/public/platform/web_gesture_event.h',
-  '+third_party/blink/public/platform/web_input_event.h',
+  "+third_party/blink/public/common/page/page_zoom.h",
+  "+third_party/blink/public/platform/web_gesture_event.h",
+  "+third_party/blink/public/platform/web_input_event.h",
 ]
+
+specific_include_rules = {
+  "^(test_|.*_(unit|pixel|perf)?test).*\.(cc|h)": [
+    "+content/public/test",
+  ],
+}
diff --git a/components/guest_view/browser/guest_view_base.cc b/components/guest_view/browser/guest_view_base.cc
index f9dbfc0..5d58f97 100644
--- a/components/guest_view/browser/guest_view_base.cc
+++ b/components/guest_view/browser/guest_view_base.cc
@@ -27,8 +27,8 @@
 #include "content/public/browser/render_widget_host_view.h"
 #include "content/public/browser/site_instance.h"
 #include "content/public/browser/web_contents.h"
-#include "content/public/common/page_zoom.h"
 #include "content/public/common/url_constants.h"
+#include "third_party/blink/public/common/page/page_zoom.h"
 #include "third_party/blink/public/platform/web_gesture_event.h"
 
 using content::WebContents;
@@ -769,8 +769,8 @@
     // The embedder's zoom level has changed.
     auto* guest_zoom_controller =
         zoom::ZoomController::FromWebContents(web_contents());
-    if (content::ZoomValuesEqual(data.new_zoom_level,
-                                 guest_zoom_controller->GetZoomLevel())) {
+    if (blink::PageZoomValuesEqual(data.new_zoom_level,
+                                   guest_zoom_controller->GetZoomLevel())) {
       return;
     }
     // When the embedder's zoom level doesn't match the guest's, then update the
@@ -830,7 +830,7 @@
   if (!embedder_web_contents())
     return 1.0;
 
-  return content::ZoomLevelToZoomFactor(
+  return blink::PageZoomLevelToZoomFactor(
       zoom::ZoomController::GetZoomLevelForWebContents(
           embedder_web_contents()));
 }
diff --git a/components/omnibox/browser/autocomplete_controller.cc b/components/omnibox/browser/autocomplete_controller.cc
index f6a540e..4702bdff 100644
--- a/components/omnibox/browser/autocomplete_controller.cc
+++ b/components/omnibox/browser/autocomplete_controller.cc
@@ -370,6 +370,8 @@
         name, 1, 1000, 50, base::Histogram::kUmaTargetedHistogramFlag);
     counter->Add(static_cast<int>((end_time - start_time).InMilliseconds()));
   }
+  base::UmaHistogramBoolean("Omnibox.Start.WantAsyncMatches",
+                            input.want_asynchronous_matches());
 
   // This will usually set |done_| to false, unless all of the providers are
   // are finished after the synchronous pass we just completed.
@@ -597,6 +599,10 @@
     result_.CopyOldMatches(input_, &last_result, template_url_service_);
   }
 
+  // Log metrics for how many matches are asynchronously changed.
+  if (!in_start_)
+    AutocompleteResult::LogAsynchronousUpdateMetrics(last_result, result_);
+
   UpdateKeywordDescriptions(&result_);
   UpdateAssociatedKeywords(&result_);
   UpdateAssistedQueryStats(&result_);
diff --git a/components/omnibox/browser/autocomplete_result.cc b/components/omnibox/browser/autocomplete_result.cc
index 926f8e9..4b50413 100644
--- a/components/omnibox/browser/autocomplete_result.cc
+++ b/components/omnibox/browser/autocomplete_result.cc
@@ -14,6 +14,7 @@
 #include "base/command_line.h"
 #include "base/logging.h"
 #include "base/metrics/field_trial_params.h"
+#include "base/metrics/histogram_functions.h"
 #include "base/stl_util.h"
 #include "base/strings/string_number_conversions.h"
 #include "base/strings/utf_string_conversions.h"
@@ -56,6 +57,10 @@
   std::rotate(matches->begin(), it, next);
 }
 
+// This value should be comfortably larger than any max-autocomplete-matches
+// under consideration.
+constexpr size_t kMaxAutocompletePositionValue = 30;
+
 }  // namespace
 
 struct MatchGURLHash {
@@ -75,11 +80,16 @@
 #else
   constexpr size_t kDefaultMaxAutocompleteMatches = 6;
 #endif  // defined(OS_ANDROID)
+  static_assert(kMaxAutocompletePositionValue > kDefaultMaxAutocompleteMatches,
+                "kMaxAutocompletePositionValue must be larger than the largest "
+                "possible autocomplete result size.");
 
-  return base::GetFieldTrialParamByFeatureAsInt(
+  size_t field_trial_value = base::GetFieldTrialParamByFeatureAsInt(
       omnibox::kUIExperimentMaxAutocompleteMatches,
       OmniboxFieldTrial::kUIMaxAutocompleteMatchesParam,
       kDefaultMaxAutocompleteMatches);
+  DCHECK(kMaxAutocompletePositionValue > field_trial_value);
+  return field_trial_value;
 }
 
 AutocompleteResult::AutocompleteResult() {
@@ -715,6 +725,30 @@
 }
 
 // static
+void AutocompleteResult::LogAsynchronousUpdateMetrics(
+    const AutocompleteResult& old_result,
+    const AutocompleteResult& new_result) {
+  constexpr char kAsyncMatchChangeHistogramName[] =
+      "Omnibox.MatchStability.AsyncMatchChange";
+
+  size_t min_size = std::min(old_result.size(), new_result.size());
+  for (size_t i = 0; i < min_size; ++i) {
+    if (GetMatchComparisonFields(old_result.match_at(i)) !=
+        GetMatchComparisonFields(new_result.match_at(i))) {
+      base::UmaHistogramExactLinear(kAsyncMatchChangeHistogramName, i,
+                                    kMaxAutocompletePositionValue);
+    }
+  }
+
+  // Also log a change for when the match count decreases. But don't make a log
+  // for appending new matches on the bottom, since that's less disruptive.
+  for (size_t i = new_result.size(); i < old_result.size(); ++i) {
+    base::UmaHistogramExactLinear(kAsyncMatchChangeHistogramName, i,
+                                  kMaxAutocompletePositionValue);
+  }
+}
+
+// static
 bool AutocompleteResult::HasMatchByDestination(const AutocompleteMatch& match,
                                                const ACMatches& matches) {
   for (auto i(matches.begin()); i != matches.end(); ++i) {
diff --git a/components/omnibox/browser/autocomplete_result.h b/components/omnibox/browser/autocomplete_result.h
index 3d68c5e..b59b4b4 100644
--- a/components/omnibox/browser/autocomplete_result.h
+++ b/components/omnibox/browser/autocomplete_result.h
@@ -141,6 +141,11 @@
   // See base/trace_event/memory_usage_estimator.h for more info.
   size_t EstimateMemoryUsage() const;
 
+  // Logs metrics for when |new_result| replaces |old_result| asynchronously.
+  static void LogAsynchronousUpdateMetrics(
+      const AutocompleteResult& old_result,
+      const AutocompleteResult& new_result);
+
  private:
   FRIEND_TEST_ALL_PREFIXES(AutocompleteResultTest, ConvertsOpenTabsCorrectly);
   FRIEND_TEST_ALL_PREFIXES(AutocompleteResultTest,
diff --git a/components/omnibox/browser/autocomplete_result_unittest.cc b/components/omnibox/browser/autocomplete_result_unittest.cc
index e4535bc..cafbaa4 100644
--- a/components/omnibox/browser/autocomplete_result_unittest.cc
+++ b/components/omnibox/browser/autocomplete_result_unittest.cc
@@ -16,6 +16,7 @@
 #include "base/strings/string_number_conversions.h"
 #include "base/strings/string_util.h"
 #include "base/strings/utf_string_conversions.h"
+#include "base/test/metrics/histogram_tester.h"
 #include "base/test/scoped_feature_list.h"
 #include "base/test/task_environment.h"
 #include "build/build_config.h"
@@ -31,6 +32,7 @@
 #include "components/search_engines/template_url_service.h"
 #include "components/variations/entropy_provider.h"
 #include "components/variations/variations_associated_data.h"
+#include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/metrics_proto/omnibox_event.pb.h"
 
@@ -790,6 +792,54 @@
   AssertResultMatches(current_result, result, base::size(result));
 }
 
+// Verify metrics logged for asynchronous result updates.
+TEST_F(AutocompleteResultTest, LogAsynchronousUpdateMetrics) {
+  TestData last[] = {
+      {0, 1, 600, true}, {1, 1, 500, true}, {2, 1, 400, true},
+      {3, 1, 300, true}, {4, 1, 200, true},
+  };
+  // Same as |last|, but with these changes:
+  //  - Last two matches removed.
+  //  - Default match updated to a new URL.
+  //  - Third match updated to a new URL.
+  TestData current[] = {
+      {10, 1, 400, true},
+      {1, 1, 300, true},
+      {11, 1, 200, true},
+  };
+
+  AutocompleteInput input(base::ASCIIToUTF16("a"),
+                          metrics::OmniboxEventProto::OTHER,
+                          TestSchemeClassifier());
+
+  ACMatches last_matches;
+  PopulateAutocompleteMatches(last, base::size(last), &last_matches);
+  AutocompleteResult last_result;
+  last_result.AppendMatches(input, last_matches);
+  for (auto& match : last_result)
+    match.ComputeStrippedDestinationURL(input, template_url_service_.get());
+
+  ACMatches current_matches;
+  PopulateAutocompleteMatches(current, base::size(current), &current_matches);
+  AutocompleteResult current_result;
+  current_result.AppendMatches(input, current_matches);
+  for (auto& match : current_result)
+    match.ComputeStrippedDestinationURL(input, template_url_service_.get());
+
+  // Constructor takes the snapshot of the current histogram state.
+  base::HistogramTester histograms;
+
+  // Do the logging.
+  AutocompleteResult::LogAsynchronousUpdateMetrics(last_result, current_result);
+
+  // Expect the default match, third match, and last two matches to be logged
+  // as changed, and nothing else.
+  EXPECT_THAT(
+      histograms.GetAllSamples("Omnibox.MatchStability.AsyncMatchChange"),
+      testing::ElementsAre(base::Bucket(0, 1), base::Bucket(2, 1),
+                           base::Bucket(3, 1), base::Bucket(4, 1)));
+}
+
 TEST_F(AutocompleteResultTest, DemoteOnDeviceSearchSuggestions) {
   // clang-format off
   TestData data[] = {
diff --git a/components/page_load_metrics/browser/BUILD.gn b/components/page_load_metrics/browser/BUILD.gn
index 71240c2..817c97f 100644
--- a/components/page_load_metrics/browser/BUILD.gn
+++ b/components/page_load_metrics/browser/BUILD.gn
@@ -10,6 +10,8 @@
     "metrics_navigation_throttle.h",
     "metrics_web_contents_observer.cc",
     "metrics_web_contents_observer.h",
+    "page_load_metrics_embedder_base.cc",
+    "page_load_metrics_embedder_base.h",
     "page_load_metrics_embedder_interface.h",
     "page_load_metrics_observer.cc",
     "page_load_metrics_observer.h",
diff --git a/components/page_load_metrics/browser/page_load_metrics_embedder_base.cc b/components/page_load_metrics/browser/page_load_metrics_embedder_base.cc
new file mode 100644
index 0000000..7eac918
--- /dev/null
+++ b/components/page_load_metrics/browser/page_load_metrics_embedder_base.cc
@@ -0,0 +1,25 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/page_load_metrics/browser/page_load_metrics_embedder_base.h"
+
+#include "base/timer/timer.h"
+
+namespace page_load_metrics {
+
+PageLoadMetricsEmbedderBase::PageLoadMetricsEmbedderBase(
+    content::WebContents* web_contents)
+    : web_contents_(web_contents) {}
+
+PageLoadMetricsEmbedderBase::~PageLoadMetricsEmbedderBase() = default;
+
+void PageLoadMetricsEmbedderBase::RegisterObservers(PageLoadTracker* metrics) {
+  RegisterEmbedderObservers(metrics);
+}
+
+std::unique_ptr<base::OneShotTimer> PageLoadMetricsEmbedderBase::CreateTimer() {
+  return std::make_unique<base::OneShotTimer>();
+}
+
+}  // namespace page_load_metrics
diff --git a/components/page_load_metrics/browser/page_load_metrics_embedder_base.h b/components/page_load_metrics/browser/page_load_metrics_embedder_base.h
new file mode 100644
index 0000000..54c55ae
--- /dev/null
+++ b/components/page_load_metrics/browser/page_load_metrics_embedder_base.h
@@ -0,0 +1,39 @@
+// 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_PAGE_LOAD_METRICS_BROWSER_PAGE_LOAD_METRICS_EMBEDDER_BASE_H_
+#define COMPONENTS_PAGE_LOAD_METRICS_BROWSER_PAGE_LOAD_METRICS_EMBEDDER_BASE_H_
+
+#include "components/page_load_metrics/browser/page_load_metrics_embedder_interface.h"
+
+namespace page_load_metrics {
+
+class PageLoadTracker;
+
+// This is base class for PageLoadMetricsEmbedderInterface implementation, it
+// registers components' observers which are common among the embedders, while,
+// the embedder implementation may override RegisterEmbedderObservers() for its
+// specific observers.
+class PageLoadMetricsEmbedderBase : public PageLoadMetricsEmbedderInterface {
+ public:
+  explicit PageLoadMetricsEmbedderBase(content::WebContents* web_contents);
+
+  // PageLoadMetricsEmbedderInterface.
+  void RegisterObservers(PageLoadTracker* metrics) final;
+  std::unique_ptr<base::OneShotTimer> CreateTimer() override;
+
+  content::WebContents* web_contents() const { return web_contents_; }
+
+ protected:
+  ~PageLoadMetricsEmbedderBase() override;
+  virtual void RegisterEmbedderObservers(PageLoadTracker* metrics) = 0;
+  virtual bool IsPrerendering() const = 0;
+
+ private:
+  content::WebContents* const web_contents_;
+};
+
+}  // namespace page_load_metrics
+
+#endif  // COMPONENTS_PAGE_LOAD_METRICS_BROWSER_PAGE_LOAD_METRICS_EMBEDDER_BASE_H_
diff --git a/components/password_manager/core/browser/BUILD.gn b/components/password_manager/core/browser/BUILD.gn
index aeddc8ac..4cd2a58 100644
--- a/components/password_manager/core/browser/BUILD.gn
+++ b/components/password_manager/core/browser/BUILD.gn
@@ -133,6 +133,8 @@
     "password_generation_frame_helper.h",
     "password_generation_state.cc",
     "password_generation_state.h",
+    "password_leak_history_consumer.cc",
+    "password_leak_history_consumer.h",
     "password_list_sorter.cc",
     "password_list_sorter.h",
     "password_manager.cc",
diff --git a/components/password_manager/core/browser/leaked_credentials_table.cc b/components/password_manager/core/browser/leaked_credentials_table.cc
index 861b48a..e63c012 100644
--- a/components/password_manager/core/browser/leaked_credentials_table.cc
+++ b/components/password_manager/core/browser/leaked_credentials_table.cc
@@ -59,7 +59,9 @@
          lhs.create_time == rhs.create_time;
 }
 
-LeakedCredentialsTable::LeakedCredentialsTable(sql::Database* db) : db_(db) {}
+void LeakedCredentialsTable::Init(sql::Database* db) {
+  db_ = db;
+}
 
 bool LeakedCredentialsTable::CreateTableIfNecessary() {
   if (!db_->DoesTableExist(kLeakedCredentialsTableName)) {
diff --git a/components/password_manager/core/browser/leaked_credentials_table.h b/components/password_manager/core/browser/leaked_credentials_table.h
index ff7c75c4..b476ad77 100644
--- a/components/password_manager/core/browser/leaked_credentials_table.h
+++ b/components/password_manager/core/browser/leaked_credentials_table.h
@@ -32,9 +32,12 @@
 // Represents the 'leaked credentials' table in the Login Database.
 class LeakedCredentialsTable {
  public:
-  explicit LeakedCredentialsTable(sql::Database* db);
+  LeakedCredentialsTable() = default;
   ~LeakedCredentialsTable() = default;
 
+  // Initializes |db_|.
+  void Init(sql::Database* db);
+
   // Creates the leaked credentials table if it doesn't exist.
   bool CreateTableIfNecessary();
 
@@ -50,7 +53,7 @@
   std::vector<LeakedCredentials> GetAllRows();
 
  private:
-  sql::Database* db_;
+  sql::Database* db_ = nullptr;
 
   DISALLOW_COPY_AND_ASSIGN(LeakedCredentialsTable);
 };
diff --git a/components/password_manager/core/browser/leaked_credentials_table_unittest.cc b/components/password_manager/core/browser/leaked_credentials_table_unittest.cc
index a8f89c5..acfb4a2 100644
--- a/components/password_manager/core/browser/leaked_credentials_table_unittest.cc
+++ b/components/password_manager/core/browser/leaked_credentials_table_unittest.cc
@@ -34,10 +34,11 @@
 
   void ReloadDatabase() {
     base::FilePath file = temp_dir_.GetPath().AppendASCII("TestDatabase");
+    db_.reset(new LeakedCredentialsTable);
     connection_.reset(new sql::Database);
     connection_->set_exclusive_locking();
     ASSERT_TRUE(connection_->Open(file));
-    db_.reset(new LeakedCredentialsTable(connection_.get()));
+    db_->Init(connection_.get());
     ASSERT_TRUE(db_->CreateTableIfNecessary());
   }
 
diff --git a/components/password_manager/core/browser/login_database.cc b/components/password_manager/core/browser/login_database.cc
index 7d1c925..03dcae0 100644
--- a/components/password_manager/core/browser/login_database.cc
+++ b/components/password_manager/core/browser/login_database.cc
@@ -142,6 +142,7 @@
   INIT_STATS_ERROR,
   MIGRATION_ERROR,
   COMMIT_TRANSACTION_ERROR,
+  INIT_LEAKED_CREDENTIALS_ERROR,
 
   DATABASE_INIT_ERROR_COUNT,
 };
@@ -734,6 +735,7 @@
   }
 
   stats_table_.Init(&db_);
+  leaked_credentials_table_.Init(&db_);
 
   int current_version = meta_table_.GetVersionNumber();
   bool migration_success = FixVersionIfNeeded(&db_, &current_version);
@@ -772,6 +774,14 @@
     return false;
   }
 
+  if (!leaked_credentials_table_.CreateTableIfNecessary()) {
+    LogDatabaseInitError(INIT_LEAKED_CREDENTIALS_ERROR);
+    LOG(ERROR) << "Unable to create the leaked credentials table.";
+    transaction.Rollback();
+    db_.Close();
+    return false;
+  }
+
   if (!transaction.Commit()) {
     LogDatabaseInitError(COMMIT_TRANSACTION_ERROR);
     LOG(ERROR) << "Unable to commit a transaction.";
diff --git a/components/password_manager/core/browser/login_database.h b/components/password_manager/core/browser/login_database.h
index 7d84ff0ec..8177f8a 100644
--- a/components/password_manager/core/browser/login_database.h
+++ b/components/password_manager/core/browser/login_database.h
@@ -16,6 +16,7 @@
 #include "base/strings/string16.h"
 #include "base/util/type_safety/strong_alias.h"
 #include "build/build_config.h"
+#include "components/password_manager/core/browser/leaked_credentials_table.h"
 #include "components/password_manager/core/browser/password_store.h"
 #include "components/password_manager/core/browser/password_store_change.h"
 #include "components/password_manager/core/browser/password_store_sync.h"
@@ -204,6 +205,9 @@
   bool CommitTransaction();
 
   StatisticsTable& stats_table() { return stats_table_; }
+  LeakedCredentialsTable& leaked_credentials_table() {
+    return leaked_credentials_table_;
+  }
 
 #if defined(OS_POSIX) && !defined(OS_MACOSX)
   void enable_encryption() { use_encryption_ = true; }
@@ -318,6 +322,7 @@
   mutable sql::Database db_;
   sql::MetaTable meta_table_;
   StatisticsTable stats_table_;
+  LeakedCredentialsTable leaked_credentials_table_;
 
   // These cached strings are used to build SQL statements.
   std::string add_statement_;
diff --git a/components/password_manager/core/browser/mock_password_store.h b/components/password_manager/core/browser/mock_password_store.h
index d4d65844..adfa249 100644
--- a/components/password_manager/core/browser/mock_password_store.h
+++ b/components/password_manager/core/browser/mock_password_store.h
@@ -10,6 +10,7 @@
 #include <vector>
 
 #include "components/autofill/core/common/password_form.h"
+#include "components/password_manager/core/browser/leaked_credentials_table.h"
 #include "components/password_manager/core/browser/password_store.h"
 #include "components/password_manager/core/browser/statistics_table.h"
 #include "testing/gmock/include/gmock/gmock.h"
@@ -71,6 +72,10 @@
                std::vector<InteractionsStats>(const GURL& origin_domain));
   MOCK_METHOD1(AddSiteStatsImpl, void(const InteractionsStats&));
   MOCK_METHOD1(RemoveSiteStatsImpl, void(const GURL&));
+  MOCK_METHOD1(AddLeakedCredentialsImpl, void(const LeakedCredentials&));
+  MOCK_METHOD2(RemoveLeakedCredentialsImpl,
+               void(const GURL&, const base::string16&));
+  MOCK_METHOD0(GetAllLeakedCredentialsImpl, std::vector<LeakedCredentials>());
   MOCK_CONST_METHOD0(IsAbleToSavePasswords, bool());
 
 #if defined(SYNC_PASSWORD_REUSE_DETECTION_ENABLED)
diff --git a/components/password_manager/core/browser/password_leak_history_consumer.cc b/components/password_manager/core/browser/password_leak_history_consumer.cc
new file mode 100644
index 0000000..b4dbab8
--- /dev/null
+++ b/components/password_manager/core/browser/password_leak_history_consumer.cc
@@ -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.
+
+#include "components/password_manager/core/browser/password_leak_history_consumer.h"
+
+#include "components/password_manager/core/browser/leaked_credentials_table.h"
+
+namespace password_manager {
+
+PasswordLeakHistoryConsumer::PasswordLeakHistoryConsumer() = default;
+
+PasswordLeakHistoryConsumer::~PasswordLeakHistoryConsumer() = default;
+
+}  // namespace password_manager
diff --git a/components/password_manager/core/browser/password_leak_history_consumer.h b/components/password_manager/core/browser/password_leak_history_consumer.h
new file mode 100644
index 0000000..235d364
--- /dev/null
+++ b/components/password_manager/core/browser/password_leak_history_consumer.h
@@ -0,0 +1,50 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_PASSWORD_MANAGER_CORE_BROWSER_PASSWORD_LEAK_HISTORY_CONSUMER_H_
+#define COMPONENTS_PASSWORD_MANAGER_CORE_BROWSER_PASSWORD_LEAK_HISTORY_CONSUMER_H_
+
+#include <vector>
+
+#include "base/task/cancelable_task_tracker.h"
+
+namespace password_manager {
+
+struct LeakedCredentials;
+
+// Reads information associated with leaked credentials history from the
+// PasswordStore. Reads are done asynchronously on a separate thread. It
+// provides the virtual callback method, which is guaranteed to be executed on
+// this (the UI) thread. It also provides the base::CancelableTaskTracker
+// member, which cancels any outstanding tasks upon destruction.
+class PasswordLeakHistoryConsumer {
+ public:
+  PasswordLeakHistoryConsumer();
+
+  // Called when the GetAllLeakedCredentials() request is finished, with the
+  // associated |leaked_credentials|.
+  virtual void OnGetLeakedCredentials(
+      std::vector<LeakedCredentials> leaked_credentials) = 0;
+
+  // The base::CancelableTaskTracker can be used for cancelling the tasks
+  // associated with the consumer.
+  base::CancelableTaskTracker* cancelable_task_tracker() {
+    return &cancelable_task_tracker_;
+  }
+
+  base::WeakPtr<PasswordLeakHistoryConsumer> GetWeakPtr() {
+    return weak_ptr_factory_.GetWeakPtr();
+  }
+
+ protected:
+  virtual ~PasswordLeakHistoryConsumer();
+
+ private:
+  base::CancelableTaskTracker cancelable_task_tracker_;
+  base::WeakPtrFactory<PasswordLeakHistoryConsumer> weak_ptr_factory_{this};
+};
+
+}  // namespace password_manager
+
+#endif  // COMPONENTS_PASSWORD_MANAGER_CORE_BROWSER_PASSWORD_LEAK_HISTORY_CONSUMER_H_
diff --git a/components/password_manager/core/browser/password_store.cc b/components/password_manager/core/browser/password_store.cc
index 2ebfa80..77a159be 100644
--- a/components/password_manager/core/browser/password_store.cc
+++ b/components/password_manager/core/browser/password_store.cc
@@ -24,6 +24,8 @@
 #include "components/autofill/core/common/form_data.h"
 #include "components/autofill/core/common/password_form.h"
 #include "components/password_manager/core/browser/android_affiliation/affiliated_match_helper.h"
+#include "components/password_manager/core/browser/leaked_credentials_table.h"
+#include "components/password_manager/core/browser/password_leak_history_consumer.h"
 #include "components/password_manager/core/browser/password_manager_metrics_util.h"
 #include "components/password_manager/core/browser/password_manager_util.h"
 #include "components/password_manager/core/browser/password_store_consumer.h"
@@ -340,6 +342,28 @@
       base::BindOnce(&PasswordStore::GetSiteStatsImpl, this, origin_domain));
 }
 
+void PasswordStore::AddLeakedCredentials(
+    const LeakedCredentials& leaked_credentials) {
+  DCHECK(main_task_runner_->RunsTasksInCurrentSequence());
+  ScheduleTask(base::BindOnce(&PasswordStore::AddLeakedCredentialsImpl, this,
+                              leaked_credentials));
+}
+
+void PasswordStore::RemoveLeakedCredentials(const GURL& url,
+                                            const base::string16& username) {
+  DCHECK(main_task_runner_->RunsTasksInCurrentSequence());
+  ScheduleTask(base::BindOnce(&PasswordStore::RemoveLeakedCredentialsImpl, this,
+                              url, username));
+}
+
+void PasswordStore::GetAllLeakedCredentials(
+    PasswordLeakHistoryConsumer* consumer) {
+  DCHECK(main_task_runner_->RunsTasksInCurrentSequence());
+  PostLeakedCredentialsTaskAndReplyToConsumerWithResult(
+      consumer,
+      base::BindOnce(&PasswordStore::GetAllLeakedCredentialsImpl, this));
+}
+
 void PasswordStore::AddObserver(Observer* observer) {
   observers_->AddObserver(observer);
 }
@@ -700,6 +724,15 @@
                      consumer->GetWeakPtr()));
 }
 
+void PasswordStore::PostLeakedCredentialsTaskAndReplyToConsumerWithResult(
+    PasswordLeakHistoryConsumer* consumer,
+    LeakedCredentialsTask task) {
+  consumer->cancelable_task_tracker()->PostTaskAndReplyWithResult(
+      background_task_runner_.get(), FROM_HERE, std::move(task),
+      base::BindOnce(&PasswordLeakHistoryConsumer::OnGetLeakedCredentials,
+                     consumer->GetWeakPtr()));
+}
+
 void PasswordStore::AddLoginInternal(const PasswordForm& form) {
   DCHECK(background_task_runner_->RunsTasksInCurrentSequence());
   SCOPED_UMA_HISTOGRAM_TIMER("PasswordManager.StorePerformance.AddLogin");
diff --git a/components/password_manager/core/browser/password_store.h b/components/password_manager/core/browser/password_store.h
index b5e4014..e170aeb 100644
--- a/components/password_manager/core/browser/password_store.h
+++ b/components/password_manager/core/browser/password_store.h
@@ -54,10 +54,12 @@
 
 class AffiliatedMatchHelper;
 class PasswordStoreConsumer;
+class PasswordLeakHistoryConsumer;
 class PasswordStoreSigninNotifier;
 class PasswordSyncableService;
 class PasswordSyncBridge;
 struct InteractionsStats;
+struct LeakedCredentials;
 
 #if defined(SYNC_PASSWORD_REUSE_DETECTION_ENABLED)
 using PasswordHashDataList = base::Optional<std::vector<PasswordHashData>>;
@@ -242,6 +244,19 @@
   // completion. The request will be cancelled if the consumer is destroyed.
   void GetSiteStats(const GURL& origin_domain, PasswordStoreConsumer* consumer);
 
+  // Adds information about credentials leaked on |leaked_credentials.url| for
+  // |leaked_credentials.username|. The first |leaked_credentials.create_time|
+  // is kept, so if the record for given url and username already exists,
+  // the new one will be ignored.
+  void AddLeakedCredentials(const LeakedCredentials& leaked_credentials);
+
+  // Removes information about credentials leaked on |url| for |username|.
+  void RemoveLeakedCredentials(const GURL& url, const base::string16& username);
+
+  // Retrieves all leaked credentials and notifies |consumer| on completion. The
+  // request will be cancelled if the consumer is destroyed.
+  void GetAllLeakedCredentials(PasswordLeakHistoryConsumer* consumer);
+
   // Adds an observer to be notified when the password store data changes.
   void AddObserver(Observer* observer);
 
@@ -437,6 +452,14 @@
   virtual std::vector<InteractionsStats> GetSiteStatsImpl(
       const GURL& origin_domain) = 0;
 
+  // Synchronous implementation for manipulating with information about leaked
+  // credentials.
+  virtual void AddLeakedCredentialsImpl(
+      const LeakedCredentials& leaked_credentials) = 0;
+  virtual void RemoveLeakedCredentialsImpl(const GURL& url,
+                                           const base::string16& username) = 0;
+  virtual std::vector<LeakedCredentials> GetAllLeakedCredentialsImpl() = 0;
+
   // PasswordStoreSync:
   PasswordStoreChangeList AddLoginSync(const autofill::PasswordForm& form,
                                        AddLoginError* error) override;
@@ -511,6 +534,9 @@
   using StatsResult = std::vector<InteractionsStats>;
   using StatsTask = base::OnceCallback<StatsResult()>;
 
+  using LeakedCredentialsResult = std::vector<LeakedCredentials>;
+  using LeakedCredentialsTask = base::OnceCallback<LeakedCredentialsResult()>;
+
   // Called on the main thread after initialization is completed.
   // |success| is true if initialization was successful. Sets the
   // |init_status_|.
@@ -538,6 +564,13 @@
       PasswordStoreConsumer* consumer,
       StatsTask task);
 
+  // Schedules the given |task| to be run on the PasswordStore's TaskRunner.
+  // Invokes |consumer|->OnGetLeakedCredentials() on the caller's thread with
+  // the result.
+  void PostLeakedCredentialsTaskAndReplyToConsumerWithResult(
+      PasswordLeakHistoryConsumer* consumer,
+      LeakedCredentialsTask task);
+
   // The following methods notify observers that the password store may have
   // been modified via NotifyLoginsChanged(). Note that there is no guarantee
   // that the called method will actually modify the password store data.
diff --git a/components/password_manager/core/browser/password_store_default.cc b/components/password_manager/core/browser/password_store_default.cc
index 8189a1e8..061bed7 100644
--- a/components/password_manager/core/browser/password_store_default.cc
+++ b/components/password_manager/core/browser/password_store_default.cc
@@ -222,6 +222,28 @@
                    : std::vector<InteractionsStats>();
 }
 
+void PasswordStoreDefault::AddLeakedCredentialsImpl(
+    const LeakedCredentials& leaked_credentials) {
+  DCHECK(background_task_runner()->RunsTasksInCurrentSequence());
+  if (login_db_)
+    login_db_->leaked_credentials_table().AddRow(leaked_credentials);
+}
+
+void PasswordStoreDefault::RemoveLeakedCredentialsImpl(
+    const GURL& url,
+    const base::string16& username) {
+  DCHECK(background_task_runner()->RunsTasksInCurrentSequence());
+  if (login_db_)
+    login_db_->leaked_credentials_table().RemoveRow(url, username);
+}
+
+std::vector<LeakedCredentials>
+PasswordStoreDefault::GetAllLeakedCredentialsImpl() {
+  DCHECK(background_task_runner()->RunsTasksInCurrentSequence());
+  return login_db_ ? login_db_->leaked_credentials_table().GetAllRows()
+                   : std::vector<LeakedCredentials>();
+}
+
 bool PasswordStoreDefault::BeginTransaction() {
   if (login_db_)
     return login_db_->BeginTransaction();
diff --git a/components/password_manager/core/browser/password_store_default.h b/components/password_manager/core/browser/password_store_default.h
index 7d9a020..e89d044 100644
--- a/components/password_manager/core/browser/password_store_default.h
+++ b/components/password_manager/core/browser/password_store_default.h
@@ -81,6 +81,11 @@
   std::vector<InteractionsStats> GetAllSiteStatsImpl() override;
   std::vector<InteractionsStats> GetSiteStatsImpl(
       const GURL& origin_domain) override;
+  void AddLeakedCredentialsImpl(
+      const LeakedCredentials& leaked_credentials) override;
+  void RemoveLeakedCredentialsImpl(const GURL& url,
+                                   const base::string16& username) override;
+  std::vector<LeakedCredentials> GetAllLeakedCredentialsImpl() override;
 
   // Implements PasswordStoreSync interface.
   bool BeginTransaction() override;
diff --git a/components/password_manager/core/browser/password_store_unittest.cc b/components/password_manager/core/browser/password_store_unittest.cc
index 7d3836e..dbfe3d33 100644
--- a/components/password_manager/core/browser/password_store_unittest.cc
+++ b/components/password_manager/core/browser/password_store_unittest.cc
@@ -21,6 +21,7 @@
 #include "components/password_manager/core/browser/android_affiliation/affiliated_match_helper.h"
 #include "components/password_manager/core/browser/android_affiliation/affiliation_service.h"
 #include "components/password_manager/core/browser/android_affiliation/mock_affiliated_match_helper.h"
+#include "components/password_manager/core/browser/password_leak_history_consumer.h"
 #include "components/password_manager/core/browser/password_manager_test_utils.h"
 #include "components/password_manager/core/browser/password_reuse_detector.h"
 #include "components/password_manager/core/browser/password_store_consumer.h"
@@ -37,6 +38,7 @@
 using base::WaitableEvent;
 using testing::_;
 using testing::DoAll;
+using testing::UnorderedElementsAre;
 using testing::WithArg;
 
 namespace password_manager {
@@ -74,6 +76,16 @@
 constexpr const char kTestAndroidName2[] = "Example Android App 2";
 constexpr const char kTestAndroidIconURL2[] = "https://example.com/icon_2.png";
 
+class MockPasswordLeakHistoryConsumer : public PasswordLeakHistoryConsumer {
+ public:
+  MockPasswordLeakHistoryConsumer() = default;
+
+  MOCK_METHOD1(OnGetLeakedCredentials, void(std::vector<LeakedCredentials>));
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(MockPasswordLeakHistoryConsumer);
+};
+
 class MockPasswordStoreConsumer : public PasswordStoreConsumer {
  public:
   MockPasswordStoreConsumer() = default;
@@ -1235,6 +1247,39 @@
   store->ShutdownOnUIThread();
 }
 
+TEST_F(PasswordStoreTest, GetAllLeakedCredentials) {
+  LeakedCredentials leaked_credentials;
+  leaked_credentials.url = GURL("https://example.com");
+  leaked_credentials.username = base::ASCIIToUTF16("username");
+  leaked_credentials.create_time = base::Time::FromTimeT(1);
+
+  LeakedCredentials leaked_credentials2;
+  leaked_credentials2.url = GURL("https://example2.com");
+  leaked_credentials2.username = base::ASCIIToUTF16("username2");
+  leaked_credentials2.create_time = base::Time::FromTimeT(2);
+
+  scoped_refptr<PasswordStoreDefault> store = CreatePasswordStore();
+  store->Init(syncer::SyncableService::StartSyncFlare(), nullptr);
+
+  store->AddLeakedCredentials(leaked_credentials);
+  store->AddLeakedCredentials(leaked_credentials2);
+  MockPasswordLeakHistoryConsumer consumer;
+  EXPECT_CALL(consumer, OnGetLeakedCredentials(UnorderedElementsAre(
+                            leaked_credentials, leaked_credentials2)));
+  store->GetAllLeakedCredentials(&consumer);
+  WaitForPasswordStore();
+  testing::Mock::VerifyAndClearExpectations(&consumer);
+
+  store->RemoveLeakedCredentials(leaked_credentials.url,
+                                 leaked_credentials.username);
+  EXPECT_CALL(consumer, OnGetLeakedCredentials(
+                            UnorderedElementsAre(leaked_credentials2)));
+  store->GetAllLeakedCredentials(&consumer);
+  WaitForPasswordStore();
+
+  store->ShutdownOnUIThread();
+}
+
 #endif
 
 }  // namespace password_manager
diff --git a/components/password_manager/core/browser/test_password_store.cc b/components/password_manager/core/browser/test_password_store.cc
index a9fae60..dcbe4a9 100644
--- a/components/password_manager/core/browser/test_password_store.cc
+++ b/components/password_manager/core/browser/test_password_store.cc
@@ -225,6 +225,23 @@
   return std::vector<InteractionsStats>();
 }
 
+void TestPasswordStore::AddLeakedCredentialsImpl(
+    const LeakedCredentials& stats) {
+  NOTIMPLEMENTED();
+}
+
+void TestPasswordStore::RemoveLeakedCredentialsImpl(
+    const GURL& url,
+    const base::string16& username) {
+  NOTIMPLEMENTED();
+}
+
+std::vector<LeakedCredentials>
+TestPasswordStore::GetAllLeakedCredentialsImpl() {
+  NOTIMPLEMENTED();
+  return std::vector<LeakedCredentials>();
+}
+
 bool TestPasswordStore::BeginTransaction() {
   return true;
 }
diff --git a/components/password_manager/core/browser/test_password_store.h b/components/password_manager/core/browser/test_password_store.h
index 541f70c..9035896 100644
--- a/components/password_manager/core/browser/test_password_store.h
+++ b/components/password_manager/core/browser/test_password_store.h
@@ -85,6 +85,11 @@
   void AddSiteStatsImpl(const InteractionsStats& stats) override;
   void RemoveSiteStatsImpl(const GURL& origin_domain) override;
   std::vector<InteractionsStats> GetAllSiteStatsImpl() override;
+  void AddLeakedCredentialsImpl(
+      const LeakedCredentials& leaked_credentials) override;
+  void RemoveLeakedCredentialsImpl(const GURL& url,
+                                   const base::string16& username) override;
+  std::vector<LeakedCredentials> GetAllLeakedCredentialsImpl() override;
 
   // PasswordStoreSync interface.
   bool BeginTransaction() override;
diff --git a/components/plugins/renderer/webview_plugin.cc b/components/plugins/renderer/webview_plugin.cc
index 967bb3a7d..2adb1541 100644
--- a/components/plugins/renderer/webview_plugin.cc
+++ b/components/plugins/renderer/webview_plugin.cc
@@ -20,6 +20,7 @@
 #include "gin/converter.h"
 #include "mojo/public/cpp/bindings/pending_remote.h"
 #include "skia/ext/platform_canvas.h"
+#include "third_party/blink/public/common/page/page_zoom.h"
 #include "third_party/blink/public/mojom/frame/document_interface_broker.mojom.h"
 #include "third_party/blink/public/platform/scheduler/web_thread_scheduler.h"
 #include "third_party/blink/public/platform/web_coalesced_input_event.h"
@@ -124,7 +125,7 @@
   // resources when images have a "srcset" attribute.
   web_view()->SetDeviceScaleFactor(container_->DeviceScaleFactor());
   web_view()->SetZoomLevel(
-      blink::WebView::ZoomFactorToZoomLevel(container_->PageZoomFactor()));
+      blink::PageZoomFactorToZoomLevel(container_->PageZoomFactor()));
 
   return true;
 }
@@ -387,7 +388,7 @@
 void WebViewPlugin::OnZoomLevelChanged() {
   if (container_) {
     web_view()->SetZoomLevel(
-        blink::WebView::ZoomFactorToZoomLevel(container_->PageZoomFactor()));
+        blink::PageZoomFactorToZoomLevel(container_->PageZoomFactor()));
   }
 }
 
diff --git a/components/policy/resources/policy_templates.json b/components/policy/resources/policy_templates.json
index 811b274..7c73b12 100644
--- a/components/policy/resources/policy_templates.json
+++ b/components/policy/resources/policy_templates.json
@@ -15552,6 +15552,30 @@
       This policy is only effective when the <ph name="CHROME_REPORTING_EXTENSION_NAME">Chrome Reporting Extension</ph> is enabled, and the machine is enrolled with <ph name="MACHINE_LEVEL_USER_CLOUD_POLICY_ENROLLMENT_TOKEN_POLICY_NAME">MachineLevelUserCloudPolicyEnrollmentToken</ph>.'''
     },
     {
+      'id': 608,
+      'name': 'CloudExtensionRequestEnabled',
+      'owners': ['zmin@chromium.org'],
+      'type': 'main',
+      'schema': {'type': 'boolean' },
+      'tags': ['admin-sharing', 'google-sharing'],
+      'features': {
+        'dynamic_refresh': True,
+        'per_profile': True,
+      },
+      'supported_on': ['chrome.*:79-', 'chrome_os:79-'],
+      'future': True,
+      'caption': '''Enables <ph name="PRODUCT_NAME">$1<ex>Google Chrome</ex></ph> extension installation requests''',
+      'example_value': True,
+      'desc': '''This policy controls <ph name="PRODUCT_NAME">$1<ex>Google Chrome</ex></ph> extension installation requests which allows users to send the requests to the Google Admin console for approval.
+
+      When this policy is left unset or set to disabled, extension installation requests are not created or uploaded.
+      When this policy is set to enabled, extension installation requests are created and uploaded to Google Admin console.
+
+      Extension installation requests are created when users try to install an extension that is not whitelisted by <ph name="EXTENSION_INSTALL_WHITELIST">ExtesionInstallWhitelist</ph> or <ph name="EXTENSION_SETTINGS">ExtensionSettings</ph>.
+
+      This policy is only effective when the machine is enrolled with <ph name="CLOUD_MANAGEMENT_ENROLLMENT_TOKEN">CloudManagementEnrollmentToken</ph> and <ph name="CLOUD_REPORTING_ENABLED">CloudReportingEnabled</ph> is enabled.'''
+    },
+    {
       'id': 493,
       'name': 'CloudReportingEnabled',
       'owners': ['zmin@chromium.org', 'pastarmovj@chromium.org'],
@@ -18582,6 +18606,6 @@
   ],
   'placeholders': [],
   'deleted_policy_ids': [412, 546, 562, 569],
-  'highest_id_currently_used': 607,
+  'highest_id_currently_used': 608,
   'highest_atomic_group_id_currently_used': 37
 }
diff --git a/components/safe_browsing/password_protection/DEPS b/components/safe_browsing/password_protection/DEPS
index e745435..1072517 100644
--- a/components/safe_browsing/password_protection/DEPS
+++ b/components/safe_browsing/password_protection/DEPS
@@ -5,14 +5,19 @@
   "+components/password_manager/core/browser/password_reuse_detector.h",
   "+components/sessions",
   "+components/signin/public/identity_manager/account_info.h",
-  "+components/sync_preferences/testing_pref_service_syncable.h",
   "+components/zoom",
-  "+content/public/test",
   "+net",
   "+services/identity/public",
   "+services/network/public",
-  "+services/network/test",
+  "+third_party/blink/public/common/page/page_zoom.h",
   "+ui/gfx/geometry",
   "+third_party/skia/include",
 ]
 
+specific_include_rules = {
+  ".*_(unit|pixel|perf)?test.*\.(cc|h)": [
+    "+components/sync_preferences/testing_pref_service_syncable.h",
+    "+content/public/test",
+    "+services/network/test",
+  ],
+}
diff --git a/components/safe_browsing/password_protection/password_protection_service.cc b/components/safe_browsing/password_protection/password_protection_service.cc
index a100ae3..c05937e 100644
--- a/components/safe_browsing/password_protection/password_protection_service.cc
+++ b/components/safe_browsing/password_protection/password_protection_service.cc
@@ -31,10 +31,10 @@
 #include "content/public/browser/browser_thread.h"
 #include "content/public/browser/navigation_handle.h"
 #include "content/public/browser/web_contents.h"
-#include "content/public/common/page_zoom.h"
 #include "google_apis/google_api_keys.h"
 #include "net/base/escape.h"
 #include "net/base/url_util.h"
+#include "third_party/blink/public/common/page/page_zoom.h"
 
 using content::BrowserThread;
 using content::WebContents;
@@ -134,7 +134,7 @@
         zoom::ZoomController::GetZoomLevelForWebContents(web_contents);
     UMA_HISTOGRAM_COUNTS_1000(
         "PasswordProtection.PageZoomFactor",
-        static_cast<int>(100 * content::ZoomLevelToZoomFactor(zoom_level)));
+        static_cast<int>(100 * blink::PageZoomLevelToZoomFactor(zoom_level)));
 #endif  // defined(FULL_SAFE_BROWSING)
     if (can_send_ping) {
       StartRequest(web_contents, main_frame_url, GURL(), GURL(), username,
diff --git a/components/signin/internal/identity_manager/primary_account_manager.cc b/components/signin/internal/identity_manager/primary_account_manager.cc
index 4628e18..784a2ea 100644
--- a/components/signin/internal/identity_manager/primary_account_manager.cc
+++ b/components/signin/internal/identity_manager/primary_account_manager.cc
@@ -53,6 +53,7 @@
   registry->RegisterStringPref(prefs::kGoogleServicesLastUsername,
                                std::string());
   registry->RegisterStringPref(prefs::kGoogleServicesAccountId, std::string());
+  registry->RegisterBooleanPref(prefs::kGoogleServicesConsentedToSync, false);
   registry->RegisterBooleanPref(prefs::kAutologinEnabled, true);
   registry->RegisterListPref(prefs::kReverseAutologinRejectedEmailList);
   registry->RegisterBooleanPref(prefs::kSigninAllowed, true);
@@ -74,13 +75,22 @@
   // clear their login info also (not valid to be logged in without any
   // tokens).
   base::CommandLine* cmd_line = base::CommandLine::ForCurrentProcess();
-  if (cmd_line->HasSwitch(switches::kClearTokenService)) {
-    client_->GetPrefs()->ClearPref(prefs::kGoogleServicesAccountId);
-  }
+  if (cmd_line->HasSwitch(switches::kClearTokenService))
+    SetPrimaryAccountInternal(CoreAccountInfo(), false);
 
   std::string pref_account_id =
       client_->GetPrefs()->GetString(prefs::kGoogleServicesAccountId);
 
+  // Initial value for the kGoogleServicesConsentedToSync preference if it is
+  // missing.
+  const PrefService::Preference* consented_pref =
+      client_->GetPrefs()->FindPreference(
+          prefs::kGoogleServicesConsentedToSync);
+  if (consented_pref->IsDefaultValue()) {
+    client_->GetPrefs()->SetBoolean(prefs::kGoogleServicesConsentedToSync,
+                                    !pref_account_id.empty());
+  }
+
   if (!pref_account_id.empty()) {
     if (account_tracker_service_->GetMigrationState() ==
         AccountTrackerService::MIGRATION_IN_PROGRESS) {
@@ -90,12 +100,25 @@
       if (!account_info.gaia.empty()) {
         pref_account_id = account_info.gaia;
         client_->GetPrefs()->SetString(prefs::kGoogleServicesAccountId,
-                                       pref_account_id);
+                                       account_info.gaia);
       }
     }
-    SetAuthenticatedAccountInfo(account_tracker_service_->GetAccountInfo(
-        CoreAccountId(pref_account_id)));
   }
+
+  bool consented =
+      client_->GetPrefs()->GetBoolean(prefs::kGoogleServicesConsentedToSync);
+  CoreAccountInfo account_info =
+      account_tracker_service_->GetAccountInfo(CoreAccountId(pref_account_id));
+  if (consented) {
+    DCHECK(!account_info.account_id.empty());
+    // First reset the state, because SetAuthenticatedAccountInfo can only be
+    // called if the user is not already signed in.
+    SetPrimaryAccountInternal(CoreAccountInfo(), /*consented=*/false);
+    SetAuthenticatedAccountInfo(account_info);
+  } else {
+    SetPrimaryAccountInternal(account_info, consented);
+  }
+
   if (policy_manager_) {
     policy_manager_->InitializePolicy(local_state, this);
   }
@@ -110,31 +133,60 @@
 }
 
 CoreAccountInfo PrimaryAccountManager::GetAuthenticatedAccountInfo() const {
-  return authenticated_account_info_.value_or(CoreAccountInfo());
+  if (!IsAuthenticated())
+    return CoreAccountInfo();
+  return primary_account_info();
 }
 
 CoreAccountId PrimaryAccountManager::GetAuthenticatedAccountId() const {
   return GetAuthenticatedAccountInfo().account_id;
 }
 
+CoreAccountInfo PrimaryAccountManager::GetUnconsentedPrimaryAccountInfo()
+    const {
+  return primary_account_info();
+}
+
+bool PrimaryAccountManager::HasUnconsentedPrimaryAccount() const {
+  return !primary_account_info().account_id.empty();
+}
+
+void PrimaryAccountManager::SetUnconsentedPrimaryAccountInfo(
+    CoreAccountInfo account_info) {
+  if (IsAuthenticated()) {
+    DCHECK_EQ(account_info, GetAuthenticatedAccountInfo());
+    return;
+  }
+
+  bool account_changed = account_info != primary_account_info();
+  SetPrimaryAccountInternal(account_info, /*consented_to_sync=*/false);
+
+  if (account_changed) {
+    for (Observer& observer : observers_)
+      observer.UnconsentedPrimaryAccountChanged(primary_account_info());
+  }
+}
+
 void PrimaryAccountManager::SetAuthenticatedAccountInfo(
     const CoreAccountInfo& account_info) {
   DCHECK(!account_info.account_id.empty());
   DCHECK(!IsAuthenticated());
 
-  std::string pref_account_id =
-      client_->GetPrefs()->GetString(prefs::kGoogleServicesAccountId);
+#if DCHECK_IS_ON()
+  {
+    std::string pref_account_id =
+        client_->GetPrefs()->GetString(prefs::kGoogleServicesAccountId);
+    bool consented_to_sync =
+        client_->GetPrefs()->GetBoolean(prefs::kGoogleServicesConsentedToSync);
 
-  DCHECK(pref_account_id.empty() ||
-         pref_account_id == account_info.account_id.id)
-      << "account_id=" << account_info.account_id
-      << " pref_account_id=" << pref_account_id;
-  authenticated_account_info_ = account_info;
+    DCHECK(pref_account_id.empty() || !consented_to_sync ||
+           pref_account_id == account_info.account_id.id)
+        << "account_id=" << account_info.account_id
+        << " pref_account_id=" << pref_account_id;
+  }
+#endif  // DCHECK_IS_ON()
 
-  // This preference is set so that code on I/O thread has access to the
-  // Gaia id of the signed in user.
-  client_->GetPrefs()->SetString(prefs::kGoogleServicesAccountId,
-                                 account_info.account_id.id);
+  SetPrimaryAccountInternal(account_info, /*consented_to_sync=*/true);
 
   // Go ahead and update the last signed in account info here as well. Once a
   // user is signed in the corresponding preferences should match. Doing it here
@@ -149,8 +201,28 @@
   client_->GetPrefs()->CommitPendingWrite();
 }
 
+void PrimaryAccountManager::SetPrimaryAccountInternal(
+    const CoreAccountInfo& account_info,
+    bool consented_to_sync) {
+  primary_account_info_ = account_info;
+
+  PrefService* prefs = client_->GetPrefs();
+  const std::string& account_id = primary_account_info_.account_id;
+  if (account_id.empty()) {
+    DCHECK(!consented_to_sync);
+    prefs->ClearPref(prefs::kGoogleServicesAccountId);
+    prefs->ClearPref(prefs::kGoogleServicesConsentedToSync);
+  } else {
+    prefs->SetString(prefs::kGoogleServicesAccountId, account_id);
+    prefs->SetBoolean(prefs::kGoogleServicesConsentedToSync, consented_to_sync);
+  }
+}
+
 bool PrimaryAccountManager::IsAuthenticated() const {
-  return authenticated_account_info_.has_value();
+  bool consented_pref =
+      client_->GetPrefs()->GetBoolean(prefs::kGoogleServicesConsentedToSync);
+  DCHECK(!consented_pref || !primary_account_info().account_id.empty());
+  return consented_pref;
 }
 
 void PrimaryAccountManager::SignIn(const std::string& username) {
@@ -161,23 +233,27 @@
   DCHECK(!info.account_id.empty());
   if (IsAuthenticated()) {
     DCHECK_EQ(info.account_id, GetAuthenticatedAccountId())
-        << "Changing the authenticated account while authenticated is not "
-           "allowed.";
+        << "Changing the authenticated account while it is not allowed.";
     return;
   }
 
+  bool account_changed = info != primary_account_info();
   SetAuthenticatedAccountInfo(info);
 
-  for (Observer& observer : observers_)
+  for (Observer& observer : observers_) {
+    if (account_changed)
+      observer.UnconsentedPrimaryAccountChanged(info);
     observer.GoogleSigninSucceeded(info);
+  }
 }
 
 void PrimaryAccountManager::UpdateAuthenticatedAccountInfo() {
-  DCHECK(authenticated_account_info_.has_value());
+  DCHECK(!primary_account_info().account_id.empty());
+  DCHECK(IsAuthenticated());
   const CoreAccountInfo info = account_tracker_service_->GetAccountInfo(
-      authenticated_account_info_->account_id);
-  DCHECK_EQ(info.account_id, authenticated_account_info_->account_id);
-  authenticated_account_info_ = info;
+      primary_account_info().account_id);
+  DCHECK_EQ(info.account_id, primary_account_info().account_id);
+  SetPrimaryAccountInternal(info, /*consented_to_sync=*/true);
 }
 
 void PrimaryAccountManager::AddObserver(Observer* observer) {
@@ -249,9 +325,8 @@
   }
 
   const CoreAccountInfo account_info = GetAuthenticatedAccountInfo();
-  authenticated_account_info_ = base::nullopt;
   client_->GetPrefs()->ClearPref(prefs::kGoogleServicesHostedDomain);
-  client_->GetPrefs()->ClearPref(prefs::kGoogleServicesAccountId);
+  SetPrimaryAccountInternal(CoreAccountInfo(), /*consented_to_sync=*/false);
 
   // Revoke all tokens before sending signed_out notification, because there
   // may be components that don't listen for token service events when the
@@ -275,8 +350,10 @@
       break;
   }
 
-  for (Observer& observer : observers_)
+  for (Observer& observer : observers_) {
     observer.GoogleSignedOut(account_info);
+    observer.UnconsentedPrimaryAccountChanged(primary_account_info());
+  }
 }
 
 void PrimaryAccountManager::OnRefreshTokensLoaded() {
diff --git a/components/signin/internal/identity_manager/primary_account_manager.h b/components/signin/internal/identity_manager/primary_account_manager.h
index 128a47a7..3b927cfd 100644
--- a/components/signin/internal/identity_manager/primary_account_manager.h
+++ b/components/signin/internal/identity_manager/primary_account_manager.h
@@ -49,6 +49,11 @@
     // Not called during a reauth.
     virtual void GoogleSigninSucceeded(const CoreAccountInfo& info) {}
 
+    // Called whenever the unconsented primary account changes. This includes
+    // the changes for the consented primary account as well.
+    virtual void UnconsentedPrimaryAccountChanged(const CoreAccountInfo& info) {
+    }
+
 #if !defined(OS_CHROMEOS)
     // Called whenever the currently signed-in user has been signed out.
     virtual void GoogleSignedOut(const CoreAccountInfo& info) {}
@@ -143,8 +148,21 @@
   void AddObserver(Observer* observer);
   void RemoveObserver(const Observer* observer);
 
+  // Provides access to the core information of the user's unconsented primary
+  // account. Returns an empty info, if there is no such account.
+  CoreAccountInfo GetUnconsentedPrimaryAccountInfo() const;
+
+  // Returns whether the user's unconsented primary account is available.
+  bool HasUnconsentedPrimaryAccount() const;
+
+  // Sets the unconsented primary account. The unconsented primary account can
+  // only be changed if the user is not authenticated. If the user is
+  // authenticated, use Signout() instead.
+  void SetUnconsentedPrimaryAccountInfo(CoreAccountInfo account_info);
+
  private:
-  // Sets the authenticated user's account id.
+  // Sets the authenticated user's account id, when the user has consented to
+  // sync.
   // If the user is already authenticated with the same account id, then this
   // method is a no-op.
   // It is forbidden to call this method if the user is already authenticated
@@ -153,6 +171,10 @@
   // ClearAuthenticatedAccountId() instead.
   void SetAuthenticatedAccountInfo(const CoreAccountInfo& account_info);
 
+  // Sets |primary_account_info_| and updates the associated preferences.
+  void SetPrimaryAccountInternal(const CoreAccountInfo& account_info,
+                                 bool consented_to_sync);
+
 #if !defined(OS_CHROMEOS)
   // Starts the sign out process.
   void StartSignOut(signin_metrics::ProfileSignout signout_source_metric,
@@ -170,6 +192,10 @@
   void OnRefreshTokensLoaded() override;
 #endif
 
+  const CoreAccountInfo& primary_account_info() const {
+    return primary_account_info_;
+  }
+
   SigninClient* client_;
 
   // The ProfileOAuth2TokenService instance associated with this object. Must
@@ -179,8 +205,11 @@
 
   bool initialized_ = false;
 
-  // Account id after successful authentication.
-  base::Optional<CoreAccountInfo> authenticated_account_info_;
+  // Account id after successful authentication. The account may or may not be
+  // consented to Sync.
+  // Must be kept in sync with prefs. Use SetPrimaryAccountInternal() to change
+  // this field.
+  CoreAccountInfo primary_account_info_;
 
 #if !defined(OS_CHROMEOS)
   signin::AccountConsistencyMethod account_consistency_;
diff --git a/components/signin/internal/identity_manager/primary_account_manager_unittest.cc b/components/signin/internal/identity_manager/primary_account_manager_unittest.cc
index 76f9a6f..b6b3a4d 100644
--- a/components/signin/internal/identity_manager/primary_account_manager_unittest.cc
+++ b/components/signin/internal/identity_manager/primary_account_manager_unittest.cc
@@ -41,7 +41,7 @@
             std::make_unique<FakeProfileOAuth2TokenServiceDelegate>()),
         account_consistency_(signin::AccountConsistencyMethod::kDisabled),
         num_successful_signins_(0),
-        num_signouts_(0) {
+        num_unconsented_account_changed_(0) {
     AccountFetcherService::RegisterPrefs(user_prefs_.registry());
     AccountTrackerService::RegisterPrefs(user_prefs_.registry());
     ProfileOAuth2TokenService::RegisterProfilePrefs(user_prefs_.registry());
@@ -121,11 +121,9 @@
     num_successful_signins_++;
   }
 
-#if !defined(OS_CHROMEOS)
-  void GoogleSignedOut(const CoreAccountInfo& account_info) override {
-    num_signouts_++;
+  void UnconsentedPrimaryAccountChanged(const CoreAccountInfo& info) override {
+    num_unconsented_account_changed_++;
   }
-#endif
 
   base::test::TaskEnvironment task_environment_;
   sync_preferences::TestingPrefServiceSyncable user_prefs_;
@@ -142,7 +140,7 @@
   std::vector<std::string> cookies_;
   signin::AccountConsistencyMethod account_consistency_;
   int num_successful_signins_;
-  int num_signouts_;
+  int num_unconsented_account_changed_;
 };
 
 #if !defined(OS_CHROMEOS)
@@ -162,6 +160,7 @@
   EXPECT_FALSE(manager_->IsAuthenticated());
   EXPECT_TRUE(manager_->GetAuthenticatedAccountInfo().email.empty());
   EXPECT_TRUE(manager_->GetAuthenticatedAccountId().empty());
+  EXPECT_EQ(CoreAccountInfo(), manager_->GetUnconsentedPrimaryAccountInfo());
 }
 
 TEST_F(PrimaryAccountManagerTest, SignOutRevoke) {
@@ -205,6 +204,8 @@
   std::vector<CoreAccountId> expected_tokens = {main_account_id,
                                                 other_account_id};
   EXPECT_EQ(expected_tokens, token_service_.GetAccounts());
+  EXPECT_EQ(manager_->GetUnconsentedPrimaryAccountInfo(),
+            manager_->GetAuthenticatedAccountInfo());
 }
 
 TEST_F(PrimaryAccountManagerTest, SignOutDiceWithError) {
@@ -280,17 +281,21 @@
 }
 #endif
 
-TEST_F(PrimaryAccountManagerTest, ExternalSignIn) {
+TEST_F(PrimaryAccountManagerTest, SignIn) {
   CreatePrimaryAccountManager();
   EXPECT_EQ("", manager_->GetAuthenticatedAccountInfo().email);
   EXPECT_EQ("", manager_->GetAuthenticatedAccountId());
   EXPECT_EQ(0, num_successful_signins_);
+  EXPECT_EQ(0, num_unconsented_account_changed_);
 
   std::string account_id = AddToAccountTracker("gaia_id", "user@gmail.com");
   manager_->SignIn("user@gmail.com");
   EXPECT_EQ(1, num_successful_signins_);
+  EXPECT_EQ(1, num_unconsented_account_changed_);
   EXPECT_EQ("user@gmail.com", manager_->GetAuthenticatedAccountInfo().email);
   EXPECT_EQ(account_id, manager_->GetAuthenticatedAccountId());
+  EXPECT_EQ(manager_->GetUnconsentedPrimaryAccountInfo(),
+            manager_->GetAuthenticatedAccountInfo());
 }
 
 TEST_F(PrimaryAccountManagerTest,
@@ -299,15 +304,18 @@
   EXPECT_EQ("", manager_->GetAuthenticatedAccountInfo().email);
   EXPECT_EQ("", manager_->GetAuthenticatedAccountId());
   EXPECT_EQ(0, num_successful_signins_);
+  EXPECT_EQ(0, num_unconsented_account_changed_);
 
   std::string account_id = AddToAccountTracker("gaia_id", "user@gmail.com");
   manager_->SignIn("user@gmail.com");
   EXPECT_EQ(1, num_successful_signins_);
+  EXPECT_EQ(1, num_unconsented_account_changed_);
   EXPECT_EQ("user@gmail.com", manager_->GetAuthenticatedAccountInfo().email);
   EXPECT_EQ(account_id, manager_->GetAuthenticatedAccountId());
 
   manager_->SignIn("user@gmail.com");
   EXPECT_EQ(1, num_successful_signins_);
+  EXPECT_EQ(1, num_unconsented_account_changed_);
   EXPECT_EQ("user@gmail.com", manager_->GetAuthenticatedAccountInfo().email);
   EXPECT_EQ(account_id, manager_->GetAuthenticatedAccountId());
 }
@@ -385,3 +393,86 @@
               account_tracker()->GetMigrationState());
   }
 }
+
+TEST_F(PrimaryAccountManagerTest, RestoreFromPrefsConsented) {
+  CoreAccountId account_id = AddToAccountTracker("gaia_id", "user@gmail.com");
+  user_prefs_.SetString(prefs::kGoogleServicesAccountId, account_id);
+  user_prefs_.SetBoolean(prefs::kGoogleServicesConsentedToSync, true);
+  CreatePrimaryAccountManager();
+  EXPECT_EQ("user@gmail.com", manager_->GetAuthenticatedAccountInfo().email);
+  EXPECT_EQ(account_id.id, manager_->GetAuthenticatedAccountId());
+  EXPECT_EQ(manager_->GetUnconsentedPrimaryAccountInfo(),
+            manager_->GetAuthenticatedAccountInfo());
+}
+
+TEST_F(PrimaryAccountManagerTest, RestoreFromPrefsUnconsented) {
+  CoreAccountId account_id = AddToAccountTracker("gaia_id", "user@gmail.com");
+  user_prefs_.SetString(prefs::kGoogleServicesAccountId, account_id);
+  user_prefs_.SetBoolean(prefs::kGoogleServicesConsentedToSync, false);
+  CreatePrimaryAccountManager();
+  EXPECT_EQ("user@gmail.com",
+            manager_->GetUnconsentedPrimaryAccountInfo().email);
+  EXPECT_EQ(account_id.id,
+            manager_->GetUnconsentedPrimaryAccountInfo().account_id);
+  EXPECT_TRUE(manager_->GetAuthenticatedAccountInfo().IsEmpty());
+}
+
+// If kGoogleServicesConsentedToSync is missing, the account is fully
+// authenticated.
+TEST_F(PrimaryAccountManagerTest, RestoreFromPrefsMissingConsentPref) {
+  CoreAccountId account_id = AddToAccountTracker("gaia_id", "user@gmail.com");
+  user_prefs_.SetString(prefs::kGoogleServicesAccountId, account_id);
+
+  const PrefService::Preference* consented_pref =
+      user_prefs_.FindPreference(prefs::kGoogleServicesConsentedToSync);
+  ASSERT_TRUE(consented_pref);                    // Pref is registered.
+  ASSERT_TRUE(consented_pref->IsDefaultValue());  // Pref is not set.
+
+  CreatePrimaryAccountManager();
+  EXPECT_TRUE(user_prefs_.GetBoolean(prefs::kGoogleServicesConsentedToSync));
+  EXPECT_EQ("user@gmail.com", manager_->GetAuthenticatedAccountInfo().email);
+  EXPECT_EQ(account_id.id, manager_->GetAuthenticatedAccountId());
+  EXPECT_EQ(manager_->GetUnconsentedPrimaryAccountInfo(),
+            manager_->GetAuthenticatedAccountInfo());
+}
+
+TEST_F(PrimaryAccountManagerTest, SetUnconsentedPrimaryAccountInfo) {
+  CreatePrimaryAccountManager();
+  EXPECT_EQ(CoreAccountInfo(), manager_->GetUnconsentedPrimaryAccountInfo());
+  EXPECT_EQ(0, num_unconsented_account_changed_);
+  EXPECT_EQ(0, num_successful_signins_);
+
+  // Set the unconsented primary account.
+  CoreAccountInfo account_info;
+  account_info.account_id = CoreAccountId("gaia_id");
+  account_info.gaia = "gaia_id";
+  account_info.email = "user@gmail.com";
+  manager_->SetUnconsentedPrimaryAccountInfo(account_info);
+  EXPECT_EQ(0, num_successful_signins_);
+  EXPECT_EQ(1, num_unconsented_account_changed_);
+  EXPECT_EQ(account_info, manager_->GetUnconsentedPrimaryAccountInfo());
+  EXPECT_EQ(CoreAccountInfo(), manager_->GetAuthenticatedAccountInfo());
+
+  // Set the same account again.
+  manager_->SetUnconsentedPrimaryAccountInfo(account_info);
+  EXPECT_EQ(0, num_successful_signins_);
+  EXPECT_EQ(1, num_unconsented_account_changed_);
+  EXPECT_EQ(account_info, manager_->GetUnconsentedPrimaryAccountInfo());
+  EXPECT_EQ(CoreAccountInfo(), manager_->GetAuthenticatedAccountInfo());
+
+  // Change the email to another equivalent email. The account is updated but
+  // observers are not notified.
+  account_info.email = "us.er@gmail.com";
+  manager_->SetUnconsentedPrimaryAccountInfo(account_info);
+  EXPECT_EQ(0, num_successful_signins_);
+  EXPECT_EQ(1, num_unconsented_account_changed_);
+  EXPECT_EQ(account_info, manager_->GetUnconsentedPrimaryAccountInfo());
+  EXPECT_EQ(CoreAccountInfo(), manager_->GetAuthenticatedAccountInfo());
+
+  // Clear it.
+  manager_->SetUnconsentedPrimaryAccountInfo(CoreAccountInfo());
+  EXPECT_EQ(0, num_successful_signins_);
+  EXPECT_EQ(2, num_unconsented_account_changed_);
+  EXPECT_EQ(CoreAccountInfo(), manager_->GetUnconsentedPrimaryAccountInfo());
+  EXPECT_EQ(CoreAccountInfo(), manager_->GetAuthenticatedAccountInfo());
+}
diff --git a/components/signin/public/base/signin_pref_names.cc b/components/signin/public/base/signin_pref_names.cc
index 9d7f6b4..3d2a442 100644
--- a/components/signin/public/base/signin_pref_names.cc
+++ b/components/signin/public/base/signin_pref_names.cc
@@ -46,6 +46,10 @@
 // eventually be fixed.
 const char kGoogleServicesAccountId[] = "google.services.account_id";
 
+// Boolean indicating if the user gave consent for Sync.
+const char kGoogleServicesConsentedToSync[] =
+    "google.services.consented_to_sync";
+
 // The profile's hosted domain; empty if unset; kNoHostedDomainFound if there
 // is none.
 const char kGoogleServicesHostedDomain[] = "google.services.hosted_domain";
diff --git a/components/signin/public/base/signin_pref_names.h b/components/signin/public/base/signin_pref_names.h
index 84887f4..1db578b 100644
--- a/components/signin/public/base/signin_pref_names.h
+++ b/components/signin/public/base/signin_pref_names.h
@@ -17,6 +17,7 @@
 extern const char kGaiaCookieChangedTime[];
 extern const char kGaiaCookiePeriodicReportTime[];
 extern const char kGoogleServicesAccountId[];
+extern const char kGoogleServicesConsentedToSync[];
 extern const char kGoogleServicesHostedDomain[];
 extern const char kGoogleServicesLastAccountId[];
 extern const char kGoogleServicesLastUsername[];
diff --git a/components/signin/public/identity_manager/identity_manager.cc b/components/signin/public/identity_manager/identity_manager.cc
index a5cd63c..76ddd6a 100644
--- a/components/signin/public/identity_manager/identity_manager.cc
+++ b/components/signin/public/identity_manager/identity_manager.cc
@@ -80,12 +80,6 @@
       base::BindRepeating(&IdentityManager::OnRefreshTokenRevokedFromSource,
                           base::Unretained(this)));
 
-  // Seed the unconsented primary account with any state that
-  // |primary_account_manager_| and |account_tracker_service_|
-  // loaded from prefs.
-  if (primary_account_manager_->IsAuthenticated())
-    UpdateUnconsentedPrimaryAccount();
-
 #if defined(OS_ANDROID)
   java_identity_manager_ = Java_IdentityManager_create(
       base::android::AttachCurrentThread(), reinterpret_cast<intptr_t>(this));
@@ -133,11 +127,11 @@
 }
 
 CoreAccountInfo IdentityManager::GetUnconsentedPrimaryAccountInfo() const {
-  return unconsented_primary_account_.value_or(CoreAccountInfo());
+  return primary_account_manager_->GetUnconsentedPrimaryAccountInfo();
 }
 
 bool IdentityManager::HasUnconsentedPrimaryAccount() const {
-  return unconsented_primary_account_.has_value();
+  return primary_account_manager_->HasUnconsentedPrimaryAccount();
 }
 
 std::unique_ptr<AccessTokenFetcher>
@@ -466,15 +460,10 @@
 }
 
 void IdentityManager::UpdateUnconsentedPrimaryAccount() {
-  base::Optional<CoreAccountInfo> new_unconsented_primary_account =
+  base::Optional<CoreAccountInfo> account =
       ComputeUnconsentedPrimaryAccountInfo();
-  if (unconsented_primary_account_ != new_unconsented_primary_account) {
-    unconsented_primary_account_ = std::move(new_unconsented_primary_account);
-    for (auto& observer : observer_list_) {
-      observer.OnUnconsentedPrimaryAccountChanged(
-          unconsented_primary_account_.value_or(CoreAccountInfo()));
-    }
-  }
+  if (account)
+    primary_account_manager_->SetUnconsentedPrimaryAccountInfo(*account);
 }
 
 base::Optional<CoreAccountInfo>
@@ -488,14 +477,21 @@
   // request to GAIA that lists cookie accounts.
   return base::nullopt;
 #else
+  AccountsInCookieJarInfo cookie_info = GetAccountsInCookieJar();
+  if (!cookie_info.accounts_are_fresh)
+    return base::nullopt;
+
   std::vector<gaia::ListedAccount> cookie_accounts =
-      GetAccountsInCookieJar().signed_in_accounts;
+      cookie_info.signed_in_accounts;
   if (cookie_accounts.empty())
+    return CoreAccountInfo();
+
+  if (!AreRefreshTokensLoaded())
     return base::nullopt;
 
   const CoreAccountId first_account_id = cookie_accounts[0].id;
   if (!HasAccountWithRefreshToken(first_account_id))
-    return base::nullopt;
+    return CoreAccountInfo();
 
   return GetAccountInfoForAccountWithRefreshToken(first_account_id);
 #endif
@@ -517,6 +513,12 @@
 #endif
 }
 
+void IdentityManager::UnconsentedPrimaryAccountChanged(
+    const CoreAccountInfo& account_info) {
+  for (auto& observer : observer_list_)
+    observer.OnUnconsentedPrimaryAccountChanged(account_info);
+}
+
 #if !defined(OS_CHROMEOS)
 void IdentityManager::GoogleSignedOut(const CoreAccountInfo& account_info) {
   DCHECK(!HasPrimaryAccount());
diff --git a/components/signin/public/identity_manager/identity_manager.h b/components/signin/public/identity_manager/identity_manager.h
index 1bd4ecd..33c817f 100644
--- a/components/signin/public/identity_manager/identity_manager.h
+++ b/components/signin/public/identity_manager/identity_manager.h
@@ -597,10 +597,14 @@
 
   // Figures out and returns the current unconsented primary account based on
   // current cookies.
+  // Returns nullopt if the account could not be computed, and CoreAccountInfo()
+  // if there is no account.
   base::Optional<CoreAccountInfo> ComputeUnconsentedPrimaryAccountInfo() const;
 
   // PrimaryAccountManager::Observer:
   void GoogleSigninSucceeded(const CoreAccountInfo& account_info) override;
+  void UnconsentedPrimaryAccountChanged(
+      const CoreAccountInfo& account_info) override;
 #if !defined(OS_CHROMEOS)
   void GoogleSignedOut(const CoreAccountInfo& account_info) override;
 #endif
@@ -667,10 +671,6 @@
   base::ObserverList<DiagnosticsObserver, true>::Unchecked
       diagnostics_observer_list_;
 
-  // If HasPrimaryAccount() is true, then |unconsented_primary_account_|
-  // must be equal to the value returned by GetPrimaryAccountInfo().
-  base::Optional<CoreAccountInfo> unconsented_primary_account_;
-
 #if defined(OS_ANDROID)
   // Java-side IdentityManager object.
   base::android::ScopedJavaGlobalRef<jobject> java_identity_manager_;
diff --git a/components/sync/engine_impl/model_type_worker.h b/components/sync/engine_impl/model_type_worker.h
index f3b99ac..459fb86c3 100644
--- a/components/sync/engine_impl/model_type_worker.h
+++ b/components/sync/engine_impl/model_type_worker.h
@@ -34,7 +34,6 @@
 
 class CancelationSignal;
 class ModelTypeProcessor;
-class WorkerEntityTracker;
 
 // A smart cache for sync types that use message passing (rather than
 // transactions and the syncable::Directory) to communicate with the sync
@@ -192,16 +191,6 @@
   // Should only be called during a GetUpdates cycle.
   void DecryptStoredEntities();
 
-  // Returns the entity tracker for the given |tag_hash|, or nullptr.
-  WorkerEntityTracker* GetEntityTracker(const std::string& tag_hash);
-
-  // Creates an entity tracker in the map using the given |data| and returns a
-  // pointer to it. Requires that one doesn't exist for data.client_tag_hash.
-  WorkerEntityTracker* CreateEntityTracker(const std::string& tag_hash);
-
-  // Gets the entity tracker for |data| or creates one if it doesn't exist.
-  WorkerEntityTracker* GetOrCreateEntityTracker(const std::string& tag_hash);
-
   // Nudges nudge_handler_ when initial sync is done, processor has local
   // changes and either encryption is disabled for the type or cryptographer is
   // ready (doesn't have pending keys).
diff --git a/components/test/data/payments/bobpay_ui_skip.js b/components/test/data/payments/bobpay_ui_skip.js
index 29c9e55..510a0bc 100644
--- a/components/test/data/payments/bobpay_ui_skip.js
+++ b/components/test/data/payments/bobpay_ui_skip.js
@@ -14,7 +14,7 @@
  * the UI skip optimization, skipping its own UI enterily and going directly to
  * Bob Pay.
  */
-function buy() {  // eslint-disable-line no-unused-vars
+function buy() { // eslint-disable-line no-unused-vars
   try {
     new PaymentRequest(
         [{supportedMethods: 'https://bobpay.com'}],
@@ -40,10 +40,37 @@
 }
 
 /**
+ * Launches the PaymentRequest UI with Bob Pay as the only payment method, then
+ * tells the browser that the transaction has failed.
+ */
+function buyFail() { // eslint-disable-line no-unused-vars
+  try {
+    new PaymentRequest(
+        [{supportedMethods: 'https://bobpay.com'}],
+        {total: {label: 'Total', amount: {currency: 'USD', value: '5.00'}}})
+        .show()
+        .then(function(resp) {
+          resp.complete('fail')
+              .then(function() {
+                print('Transaction failed');
+              })
+              .catch(function(error) {
+                print('complete() rejected<br>' + error);
+            });
+        })
+        .catch(function(error) {
+          print('show() rejected<br>' + error);
+        });
+  } catch (error) {
+    print('exception thrown<br>' + error);
+  }
+}
+
+/**
  * Launches the PaymentRequest UI with Bob Pay as the only payment method but
  * requesting the payer's email as to disable skip ui.
  */
-function buyWithRequestedEmail() {  // eslint-disable-line no-unused-vars
+function buyWithRequestedEmail() { // eslint-disable-line no-unused-vars
   try {
     new PaymentRequest(
         [{supportedMethods: 'https://bobpay.com'}],
diff --git a/components/test/data/payments/payment_request_bobpay_ui_skip_test.html b/components/test/data/payments/payment_request_bobpay_ui_skip_test.html
index d4cfea8b..a541ade 100644
--- a/components/test/data/payments/payment_request_bobpay_ui_skip_test.html
+++ b/components/test/data/payments/payment_request_bobpay_ui_skip_test.html
@@ -13,6 +13,7 @@
 </head>
 <body>
 <div><button onclick="buy()" id="buy">Bob Pay Test</button></div>
+<div><button onclick="buyFail()" id="buyFail">Failure Test</button></div>
 <div><button onclick="buyWithRequestedEmail()" id="buyWithRequestedEmail">Bob Pay Test</button></div>
 <pre id="result"></pre>
 <script src="util.js"></script>
diff --git a/components/viz/common/features.cc b/components/viz/common/features.cc
index a9cefca..1e9fca6 100644
--- a/components/viz/common/features.cc
+++ b/components/viz/common/features.cc
@@ -74,12 +74,17 @@
   // We require OOP-D everywhere but WebView.
   bool enabled = base::FeatureList::IsEnabled(kUseSkiaRenderer) ||
                  base::FeatureList::IsEnabled(kVulkan);
-#if !defined(OS_ANDROID)
+#if defined(OS_ANDROID)
+  if (enabled && IsAndroidSurfaceControlEnabled()) {
+    DLOG(ERROR) << "UseSkiaRenderer does not work with SurfaceControl";
+    return false;
+  }
+#else
   if (enabled && !IsVizDisplayCompositorEnabled()) {
     DLOG(ERROR) << "UseSkiaRenderer requires VizDisplayCompositor.";
     return false;
   }
-#endif  // !defined(OS_ANDROID)
+#endif  // defined(OS_ANDROID)
   return enabled;
 }
 
diff --git a/components/viz/common/resources/resource_format.h b/components/viz/common/resources/resource_format.h
index c1de1f29..e5f18e2 100644
--- a/components/viz/common/resources/resource_format.h
+++ b/components/viz/common/resources/resource_format.h
@@ -29,7 +29,6 @@
   BGRX_1010102,
   YVU_420,
   YUV_420_BIPLANAR,
-  UYVY_422,
   P010,
   RESOURCE_FORMAT_MAX = P010,
 };
diff --git a/components/viz/common/resources/resource_format_utils.cc b/components/viz/common/resources/resource_format_utils.cc
index 5c28991..2b9d0ce 100644
--- a/components/viz/common/resources/resource_format_utils.cc
+++ b/components/viz/common/resources/resource_format_utils.cc
@@ -50,7 +50,6 @@
     case BGRX_1010102:
     case YVU_420:
     case YUV_420_BIPLANAR:
-    case UYVY_422:
     case P010:
       return kN32_SkColorType;
     case RGBA_F16:
@@ -78,7 +77,6 @@
     case R16_EXT:
     case BGR_565:
     case RG_88:
-    case UYVY_422:
       return 16;
     case YVU_420:
     case YUV_420_BIPLANAR:
@@ -116,7 +114,6 @@
     case BGRX_1010102:
     case YVU_420:
     case YUV_420_BIPLANAR:
-    case UYVY_422:
     case P010:
       return false;
   }
@@ -146,7 +143,6 @@
       GL_ZERO,                             // BGRX_1010102
       GL_ZERO,                             // YVU_420
       GL_ZERO,                             // YUV_420_BIPLANAR
-      GL_ZERO,                             // UYVY_422
       GL_ZERO,                             // P010
   };
   static_assert(base::size(format_gl_data_type) == (RESOURCE_FORMAT_MAX + 1),
@@ -177,7 +173,6 @@
       GL_ZERO,       // BGRX_1010102
       GL_ZERO,       // YVU_420
       GL_ZERO,       // YUV_420_BIPLANAR
-      GL_ZERO,       // UYVY_422
       GL_ZERO,       // P010
   };
   static_assert(base::size(format_gl_data_format) == (RESOURCE_FORMAT_MAX + 1),
@@ -229,7 +224,6 @@
       GL_ZERO,       // BGRX_1010102
       GL_ZERO,       // YVU_420
       GL_ZERO,       // YUV_420_BIPLANAR
-      GL_ZERO,       // UYVY_422
       GL_ZERO,       // P010
   };
 
@@ -271,7 +265,6 @@
       return gfx::BufferFormat::YUV_420_BIPLANAR;
     case P010:
       return gfx::BufferFormat::P010;
-    case UYVY_422:
     case ETC1:
     case ALPHA_8:
     case LUMINANCE_8:
@@ -322,7 +315,6 @@
     case BGRX_1010102:
     case YVU_420:
     case YUV_420_BIPLANAR:
-    case UYVY_422:
     case P010:
       break;
   }
@@ -354,7 +346,6 @@
     case BGRX_1010102:
     case YVU_420:
     case YUV_420_BIPLANAR:
-    case UYVY_422:
     case P010:
       return false;
   }
@@ -384,7 +375,6 @@
     case BGRX_1010102:
     case YVU_420:
     case YUV_420_BIPLANAR:
-    case UYVY_422:
     case P010:
       return false;
   }
@@ -436,7 +426,6 @@
     case BGRX_1010102:
     case YVU_420:
     case YUV_420_BIPLANAR:
-    case UYVY_422:
     case P010:
       return false;
     default:
@@ -482,7 +471,6 @@
     case YUV_420_BIPLANAR:
       return VK_FORMAT_G8_B8R8_2PLANE_420_UNORM;
     case LUMINANCE_F16:
-    case UYVY_422:
     case ETC1:
     case P010:
       break;
diff --git a/components/viz/host/hit_test/hit_test_query.cc b/components/viz/host/hit_test/hit_test_query.cc
index 56e8ac9..8cae150 100644
--- a/components/viz/host/hit_test/hit_test_query.cc
+++ b/components/viz/host/hit_test/hit_test_query.cc
@@ -285,10 +285,18 @@
       !(flags & HitTestRegionFlags::kHitTestIgnore)) {
     target->frame_sink_id = hit_test_data_[region_index].frame_sink_id;
     target->location_in_target = location_in_target;
-    target->flags = flags;
+    uint32_t async_hit_test_reasons =
+        hit_test_data_[region_index].async_hit_test_reasons;
+    uint32_t target_flags = flags;
+    if (root_view_overlapped) {
+      DCHECK_EQ(hit_test_data_[region_index].async_hit_test_reasons,
+                AsyncHitTestReasons::kOverlappedRegion);
+      target_flags &= ~HitTestRegionFlags::kHitTestAsk;
+      async_hit_test_reasons = AsyncHitTestReasons::kNotAsyncHitTest;
+    }
+    target->flags = target_flags;
     // We record fast path hit testing instances with reason kNotAsyncHitTest.
-    RecordSlowPathHitTestReasons(
-        hit_test_data_[region_index].async_hit_test_reasons);
+    RecordSlowPathHitTestReasons(async_hit_test_reasons);
     return true;
   }
   return false;
diff --git a/components/viz/host/hit_test/hit_test_query_unittest.cc b/components/viz/host/hit_test/hit_test_query_unittest.cc
index ca6a0f4..1adf93cb 100644
--- a/components/viz/host/hit_test/hit_test_query_unittest.cc
+++ b/components/viz/host/hit_test/hit_test_query_unittest.cc
@@ -1348,5 +1348,59 @@
                                HitTestRegionFlags::kHitTestMouse);
 }
 
+// One embedder with nested OOPIFs. When the root view is overlapped we should
+// continue checking its descendants rather than doing async hit test.
+//
+//  +e-------------+
+//  |   +c---------|     Point   maps to
+//  | 1 |    2     |     -----   -------
+//  |   |          |       1        e
+//  |   |          |       2        c
+//  |   |          |
+//  |   |          |
+//  +--------------+
+//
+TEST_F(HitTestQueryTest, OverlappedRootView) {
+  FrameSinkId e_id = FrameSinkId(1, 1);
+  FrameSinkId c_id = FrameSinkId(2, 2);
+  gfx::Rect e_bounds_in_e = gfx::Rect(0, 0, 600, 600);
+  gfx::Rect c_bounds_in_e = gfx::Rect(0, 0, 800, 800);
+  gfx::Transform transform_e_to_e, transform_e_to_c;
+  transform_e_to_c.Translate(-200, -100);
+  active_data_.push_back(AggregatedHitTestRegion(
+      e_id,
+      HitTestRegionFlags::kHitTestMine | HitTestRegionFlags::kHitTestMouse |
+          HitTestRegionFlags::kHitTestAsk,
+      e_bounds_in_e, transform_e_to_e, 1,
+      AsyncHitTestReasons::kOverlappedRegion));  // e
+  active_data_.push_back(AggregatedHitTestRegion(
+      c_id,
+      HitTestRegionFlags::kHitTestChildSurface | kHitTestMine |
+          HitTestRegionFlags::kHitTestMouse,
+      c_bounds_in_e, transform_e_to_c, 0));  // c
+  SendHitTestData();
+
+  // All points are in e's coordinate system when we reach this case.
+  gfx::PointF point1(1, 1);
+  gfx::PointF point2(202, 102);
+
+  Target target1 =
+      hit_test_query().FindTargetForLocation(EventSource::MOUSE, point1);
+  EXPECT_EQ(target1.frame_sink_id, e_id);
+  EXPECT_EQ(target1.location_in_target, point1);
+  // kHitTestAsk should be dropped for overlapped root view.
+  EXPECT_EQ(target1.flags, HitTestRegionFlags::kHitTestMine |
+                               HitTestRegionFlags::kHitTestMouse);
+
+  Target target2 =
+      hit_test_query().FindTargetForLocation(EventSource::MOUSE, point2);
+  EXPECT_EQ(target2.frame_sink_id, c_id);
+  // point2 + transform_e_to_c  = (2, 2).
+  EXPECT_EQ(target2.location_in_target, gfx::PointF(2, 2));
+  EXPECT_EQ(target2.flags, HitTestRegionFlags::kHitTestChildSurface |
+                               HitTestRegionFlags::kHitTestMine |
+                               HitTestRegionFlags::kHitTestMouse);
+}
+
 }  // namespace test
 }  // namespace viz
diff --git a/components/viz/service/display/dc_layer_overlay.cc b/components/viz/service/display/dc_layer_overlay.cc
index 7362219..69a85d1 100644
--- a/components/viz/service/display/dc_layer_overlay.cc
+++ b/components/viz/service/display/dc_layer_overlay.cc
@@ -291,7 +291,7 @@
 DCLayerOverlayProcessor::DCLayerOverlayProcessor(
     const OutputSurface::Capabilities& capabilities,
     const RendererSettings& settings)
-    : has_hw_overlay_support_(capabilities.supports_dc_layers),
+    : has_hw_overlay_support_(capabilities.supports_dc_video_overlays),
       show_debug_borders_(settings.show_dc_layer_debug_borders) {}
 
 DCLayerOverlayProcessor::DCLayerOverlayProcessor()
diff --git a/components/viz/service/display/direct_renderer.cc b/components/viz/service/display/direct_renderer.cc
index 2af4f9c..237d582 100644
--- a/components/viz/service/display/direct_renderer.cc
+++ b/components/viz/service/display/direct_renderer.cc
@@ -759,7 +759,8 @@
   current_frame()->current_render_pass = render_pass;
   if (render_pass == current_frame()->root_render_pass) {
     BindFramebufferToOutputSurface();
-    output_surface_->SetDrawRectangle(current_frame()->root_damage_rect);
+    if (supports_dc_layers_)
+      output_surface_->SetDrawRectangle(current_frame()->root_damage_rect);
     InitializeViewport(current_frame(), render_pass->output_rect,
                        gfx::Rect(current_frame()->device_viewport_size),
                        current_frame()->device_viewport_size);
diff --git a/components/viz/service/display/output_surface.h b/components/viz/service/display/output_surface.h
index 6b67fc7..0906ceb 100644
--- a/components/viz/service/display/output_surface.h
+++ b/components/viz/service/display/output_surface.h
@@ -66,8 +66,10 @@
     // on the current system transform. So the OS presentation engine can
     // present buffers onto the screen directly.
     bool supports_pre_transform = false;
-    // Whether this OutputSurface supports DC layer overlays.
+    // Whether this OutputSurface supports direct composition layers.
     bool supports_dc_layers = false;
+    // Whether this OutputSurface supports direct composition video overlays.
+    bool supports_dc_video_overlays = false;
     // Whether this OutputSurface should skip DrawAndSwap(). This is true for
     // the unified display on Chrome OS. All drawing is handled by the physical
     // displays so the unified display should skip that work.
diff --git a/components/viz/service/display/software_renderer.cc b/components/viz/service/display/software_renderer.cc
index 1f01fbe..dc382c9 100644
--- a/components/viz/service/display/software_renderer.cc
+++ b/components/viz/service/display/software_renderer.cc
@@ -812,12 +812,14 @@
       (unclipped_rect.top_right() - backdrop_rect.top_right()) +
       (backdrop_rect.bottom_left() - unclipped_rect.bottom_left());
 
-  sk_sp<SkImageFilter> filter =
+  sk_sp<cc::PaintFilter> paint_filter =
       cc::RenderSurfaceFilters::BuildImageFilter(
           *backdrop_filters,
           gfx::SizeF(backdrop_bitmap.width(), backdrop_bitmap.height()),
-          clipping_offset)
-          ->cached_sk_filter_;
+          clipping_offset);
+  if (!paint_filter)
+    return nullptr;
+  sk_sp<SkImageFilter> filter = paint_filter->cached_sk_filter_;
 
   // TODO(989238): Software renderer does not support/implement kClamp_TileMode.
   SkIRect result_rect;
diff --git a/components/viz/service/display_embedder/gl_output_surface.cc b/components/viz/service/display_embedder/gl_output_surface.cc
index b334686..312a587b 100644
--- a/components/viz/service/display_embedder/gl_output_surface.cc
+++ b/components/viz/service/display_embedder/gl_output_surface.cc
@@ -44,6 +44,8 @@
       context_capabilities.num_surface_buffers - 1;
   capabilities_.supports_gpu_vsync = context_capabilities.gpu_vsync;
   capabilities_.supports_dc_layers = context_capabilities.dc_layers;
+  capabilities_.supports_dc_video_overlays =
+      context_capabilities.use_dc_overlays_for_video;
 }
 
 GLOutputSurface::~GLOutputSurface() {
@@ -71,8 +73,7 @@
 }
 
 void GLOutputSurface::SetDrawRectangle(const gfx::Rect& rect) {
-  if (!context_provider_->ContextCapabilities().dc_layers)
-    return;
+  DCHECK(capabilities_.supports_dc_layers);
 
   if (set_draw_rectangle_for_frame_)
     return;
diff --git a/components/viz/service/display_embedder/skia_output_device_gl.cc b/components/viz/service/display_embedder/skia_output_device_gl.cc
index 98327f5..0058c3d3 100644
--- a/components/viz/service/display_embedder/skia_output_device_gl.cc
+++ b/components/viz/service/display_embedder/skia_output_device_gl.cc
@@ -45,6 +45,7 @@
   capabilities_.max_frames_pending = gl_surface_->GetBufferCount() - 1;
   capabilities_.supports_gpu_vsync = gl_surface_->SupportsGpuVSync();
   capabilities_.supports_dc_layers = gl_surface_->SupportsDCLayers();
+  capabilities_.supports_dc_video_overlays = gl_surface_->UseOverlaysForVideo();
 }
 
 void SkiaOutputDeviceGL::Initialize(GrContext* gr_context,
diff --git a/components/viz/service/display_embedder/skia_output_surface_impl.cc b/components/viz/service/display_embedder/skia_output_surface_impl.cc
index cb773de..b9ca2cb 100644
--- a/components/viz/service/display_embedder/skia_output_surface_impl.cc
+++ b/components/viz/service/display_embedder/skia_output_surface_impl.cc
@@ -147,6 +147,7 @@
 
 void SkiaOutputSurfaceImpl::SetDrawRectangle(const gfx::Rect& draw_rectangle) {
   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+  DCHECK(capabilities().supports_dc_layers);
   DCHECK(!draw_rectangle_);
   draw_rectangle_.emplace(draw_rectangle);
 }
@@ -796,6 +797,13 @@
   needs_swap_size_notifications_ = needs_swap_size_notifications;
 }
 
+base::ScopedClosureRunner SkiaOutputSurfaceImpl::GetCacheBackBufferCb() {
+  // TODO(weiliangc) : Add support for this once SkiaRenderer works with
+  // SurfaceControl.
+  CHECK(false);
+  return base::ScopedClosureRunner();
+}
+
 void SkiaOutputSurfaceImpl::AddContextLostObserver(
     ContextLostObserver* observer) {
   observers_.AddObserver(observer);
diff --git a/components/viz/service/display_embedder/skia_output_surface_impl.h b/components/viz/service/display_embedder/skia_output_surface_impl.h
index d8fc0f0..7a6a0c6 100644
--- a/components/viz/service/display_embedder/skia_output_surface_impl.h
+++ b/components/viz/service/display_embedder/skia_output_surface_impl.h
@@ -84,6 +84,7 @@
   unsigned UpdateGpuFence() override;
   void SetNeedsSwapSizeNotifications(
       bool needs_swap_size_notifications) override;
+  base::ScopedClosureRunner GetCacheBackBufferCb() override;
 
   // SkiaOutputSurface implementation:
   SkCanvas* BeginPaintCurrentFrame() override;
diff --git a/components/viz/test/data/backdrop_filter_invalid.png b/components/viz/test/data/backdrop_filter_invalid.png
new file mode 100644
index 0000000..0a680885
--- /dev/null
+++ b/components/viz/test/data/backdrop_filter_invalid.png
Binary files differ
diff --git a/components/zoom/BUILD.gn b/components/zoom/BUILD.gn
index 208334ae..f2813cef 100644
--- a/components/zoom/BUILD.gn
+++ b/components/zoom/BUILD.gn
@@ -25,6 +25,7 @@
     "//content/public/common",
     "//ipc",
     "//net",
+    "//third_party/blink/public/common",
     "//url",
   ]
 }
diff --git a/components/zoom/DEPS b/components/zoom/DEPS
index 13041bbd..634e5c2339 100644
--- a/components/zoom/DEPS
+++ b/components/zoom/DEPS
@@ -4,6 +4,7 @@
   "+content/public/common",
   "+ipc",
   "+net/base",
+  "+third_party/blink/public/common/page/page_zoom.h",
   "+url",
 ]
 
diff --git a/components/zoom/page_zoom.cc b/components/zoom/page_zoom.cc
index 2049f67..b3cc689 100644
--- a/components/zoom/page_zoom.cc
+++ b/components/zoom/page_zoom.cc
@@ -17,6 +17,7 @@
 #include "content/public/browser/render_view_host.h"
 #include "content/public/browser/web_contents.h"
 #include "content/public/common/page_zoom.h"
+#include "third_party/blink/public/common/page/page_zoom.h"
 
 using base::UserMetricsAction;
 
@@ -37,19 +38,21 @@
   for (size_t i = 0; i < zoom::kPresetZoomFactorsSize; i++) {
     double zoom_value = zoom::kPresetZoomFactors[i];
     if (value_type == PAGE_ZOOM_VALUE_TYPE_LEVEL)
-      zoom_value = content::ZoomFactorToZoomLevel(zoom_value);
-    if (content::ZoomValuesEqual(zoom_value, custom_value))
+      zoom_value = blink::PageZoomFactorToZoomLevel(zoom_value);
+    if (blink::PageZoomValuesEqual(zoom_value, custom_value))
       found_custom = true;
     zoom_values.push_back(zoom_value);
   }
   // If the preset array did not contain the custom value, append it to the
   // vector and then sort.
-  double min = value_type == PAGE_ZOOM_VALUE_TYPE_LEVEL
-                   ? content::ZoomFactorToZoomLevel(content::kMinimumZoomFactor)
-                   : content::kMinimumZoomFactor;
-  double max = value_type == PAGE_ZOOM_VALUE_TYPE_LEVEL
-                   ? content::ZoomFactorToZoomLevel(content::kMaximumZoomFactor)
-                   : content::kMaximumZoomFactor;
+  double min =
+      value_type == PAGE_ZOOM_VALUE_TYPE_LEVEL
+          ? blink::PageZoomFactorToZoomLevel(blink::kMinimumPageZoomFactor)
+          : blink::kMinimumPageZoomFactor;
+  double max =
+      value_type == PAGE_ZOOM_VALUE_TYPE_LEVEL
+          ? blink::PageZoomFactorToZoomLevel(blink::kMaximumPageZoomFactor)
+          : blink::kMaximumPageZoomFactor;
   if (!found_custom && custom_value > min && custom_value < max) {
     zoom_values.push_back(custom_value);
     std::sort(zoom_values.begin(), zoom_values.end());
@@ -98,7 +101,7 @@
     // lower level based on the current zoom level for this page.
     for (auto i = zoom_levels.rbegin(); i != zoom_levels.rend(); ++i) {
       double zoom_level = *i;
-      if (content::ZoomValuesEqual(zoom_level, current_zoom_level))
+      if (blink::PageZoomValuesEqual(zoom_level, current_zoom_level))
         continue;
       if (zoom_level < current_zoom_level) {
         zoom_controller->SetZoomLevel(zoom_level);
@@ -113,7 +116,7 @@
     for (std::vector<double>::const_iterator i = zoom_levels.begin();
          i != zoom_levels.end(); ++i) {
       double zoom_level = *i;
-      if (content::ZoomValuesEqual(zoom_level, current_zoom_level))
+      if (blink::PageZoomValuesEqual(zoom_level, current_zoom_level))
         continue;
       if (zoom_level > current_zoom_level) {
         zoom_controller->SetZoomLevel(zoom_level);
diff --git a/components/zoom/page_zoom_unittests.cc b/components/zoom/page_zoom_unittests.cc
index 31e2a09..4e4488a6 100644
--- a/components/zoom/page_zoom_unittests.cc
+++ b/components/zoom/page_zoom_unittests.cc
@@ -3,8 +3,8 @@
 // found in the LICENSE file.
 
 #include "components/zoom/page_zoom.h"
-#include "content/public/common/page_zoom.h"
 #include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/public/common/page/page_zoom.h"
 
 TEST(PageTestZoom, PresetZoomFactors) {
   // Fetch a vector of preset zoom factors, including a custom value that we
@@ -16,8 +16,8 @@
   EXPECT_GE(factors.size(), 10U);
 
   // Expect the first and last items to match the minimum and maximum values.
-  EXPECT_DOUBLE_EQ(factors.front(), content::kMinimumZoomFactor);
-  EXPECT_DOUBLE_EQ(factors.back(), content::kMaximumZoomFactor);
+  EXPECT_DOUBLE_EQ(factors.front(), blink::kMinimumPageZoomFactor);
+  EXPECT_DOUBLE_EQ(factors.back(), blink::kMaximumPageZoomFactor);
 
   // Iterate through the vector, with the following checks:
   // 1. The values are in sorted order.
@@ -31,9 +31,9 @@
   for (i = factors.begin(); i != factors.end(); ++i) {
     double factor = *i;
     EXPECT_GT(factor, last_value);
-    if (content::ZoomValuesEqual(factor, custom_value))
+    if (blink::PageZoomValuesEqual(factor, custom_value))
       found_custom_value = true;
-    if (content::ZoomValuesEqual(factor, 1.0))
+    if (blink::PageZoomValuesEqual(factor, 1.0))
       found_100_percent = true;
     last_value = factor;
   }
@@ -63,9 +63,9 @@
   for (i = levels.begin(); i != levels.end(); ++i) {
     double level = *i;
     EXPECT_GT(level, last_value);
-    if (content::ZoomValuesEqual(level, custom_value))
+    if (blink::PageZoomValuesEqual(level, custom_value))
       found_custom_value = true;
-    if (content::ZoomValuesEqual(level, 0))
+    if (blink::PageZoomValuesEqual(level, 0))
       found_100_percent = true;
     last_value = level;
   }
@@ -77,19 +77,19 @@
 TEST(PageTestZoom, InvalidCustomFactor) {
   double too_low = 0.01;
   std::vector<double> factors = zoom::PageZoom::PresetZoomFactors(too_low);
-  EXPECT_FALSE(content::ZoomValuesEqual(factors.front(), too_low));
+  EXPECT_FALSE(blink::PageZoomValuesEqual(factors.front(), too_low));
 
   double too_high = 99.0;
   factors = zoom::PageZoom::PresetZoomFactors(too_high);
-  EXPECT_FALSE(content::ZoomValuesEqual(factors.back(), too_high));
+  EXPECT_FALSE(blink::PageZoomValuesEqual(factors.back(), too_high));
 }
 
 TEST(PageTestZoom, InvalidCustomLevel) {
   double too_low = -99.0;
   std::vector<double> levels = zoom::PageZoom::PresetZoomLevels(too_low);
-  EXPECT_FALSE(content::ZoomValuesEqual(levels.front(), too_low));
+  EXPECT_FALSE(blink::PageZoomValuesEqual(levels.front(), too_low));
 
   double too_high = 99.0;
   levels = zoom::PageZoom::PresetZoomLevels(too_high);
-  EXPECT_FALSE(content::ZoomValuesEqual(levels.back(), too_high));
+  EXPECT_FALSE(blink::PageZoomValuesEqual(levels.back(), too_high));
 }
diff --git a/components/zoom/test/zoom_test_utils.cc b/components/zoom/test/zoom_test_utils.cc
index bc85bd8..803c767 100644
--- a/components/zoom/test/zoom_test_utils.cc
+++ b/components/zoom/test/zoom_test_utils.cc
@@ -6,16 +6,16 @@
 
 #include "base/bind.h"
 #include "base/callback.h"
-#include "content/public/common/page_zoom.h"
 #include "content/public/test/test_utils.h"
+#include "third_party/blink/public/common/page/page_zoom.h"
 
 namespace zoom {
 
 bool operator==(const ZoomController::ZoomChangedEventData& lhs,
                 const ZoomController::ZoomChangedEventData& rhs) {
   return lhs.web_contents == rhs.web_contents &&
-         content::ZoomValuesEqual(lhs.old_zoom_level, rhs.old_zoom_level) &&
-         content::ZoomValuesEqual(lhs.new_zoom_level, rhs.new_zoom_level) &&
+         blink::PageZoomValuesEqual(lhs.old_zoom_level, rhs.old_zoom_level) &&
+         blink::PageZoomValuesEqual(lhs.new_zoom_level, rhs.new_zoom_level) &&
          lhs.zoom_mode == rhs.zoom_mode &&
          lhs.can_show_bubble == rhs.can_show_bubble;
 }
diff --git a/components/zoom/zoom_controller.cc b/components/zoom/zoom_controller.cc
index 3a66e89..2188ae6 100644
--- a/components/zoom/zoom_controller.cc
+++ b/components/zoom/zoom_controller.cc
@@ -17,8 +17,8 @@
 #include "content/public/browser/render_view_host.h"
 #include "content/public/browser/web_contents.h"
 #include "content/public/common/page_type.h"
-#include "content/public/common/page_zoom.h"
 #include "net/base/url_util.h"
+#include "third_party/blink/public/common/page/page_zoom.h"
 
 using content::BrowserThread;
 
@@ -58,14 +58,14 @@
 
 bool ZoomController::IsAtDefaultZoom() const {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
-  return content::ZoomValuesEqual(GetZoomLevel(), GetDefaultZoomLevel());
+  return blink::PageZoomValuesEqual(GetZoomLevel(), GetDefaultZoomLevel());
 }
 
 ZoomController::RelativeZoom ZoomController::GetZoomRelativeToDefault() const {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
   double current_level = GetZoomLevel();
   double default_level = GetDefaultZoomLevel();
-  if (content::ZoomValuesEqual(current_level, default_level))
+  if (blink::PageZoomValuesEqual(current_level, default_level))
     return ZOOM_AT_DEFAULT_ZOOM;
   if (current_level > default_level)
     return ZOOM_ABOVE_DEFAULT_ZOOM;
@@ -91,7 +91,7 @@
 
 int ZoomController::GetZoomPercent() const {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
-  double zoom_factor = content::ZoomLevelToZoomFactor(GetZoomLevel());
+  double zoom_factor = blink::PageZoomLevelToZoomFactor(GetZoomLevel());
   // Round double for return.
   return static_cast<int>(zoom_factor * 100 + 0.5);
 }
@@ -122,7 +122,7 @@
   // Do not actually rescale the page in manual mode.
   if (zoom_mode_ == ZOOM_MODE_MANUAL) {
     // If the zoom level hasn't changed, early out to avoid sending an event.
-    if (content::ZoomValuesEqual(zoom_level_, zoom_level))
+    if (blink::PageZoomValuesEqual(zoom_level_, zoom_level))
       return true;
 
     double old_zoom_level = zoom_level_;
diff --git a/content/browser/accessibility/browser_accessibility_android.cc b/content/browser/accessibility/browser_accessibility_android.cc
index 17aefcd..1686a972 100644
--- a/content/browser/accessibility/browser_accessibility_android.cc
+++ b/content/browser/accessibility/browser_accessibility_android.cc
@@ -577,8 +577,20 @@
     case ax::mojom::Role::kAnchor:
       // No role description.
       break;
-    case ax::mojom::Role::kAnnotation:
-      // No role description.
+    case ax::mojom::Role::kAnnotationAttribution:
+      message_id = IDS_AX_ROLE_ANNOTATION_ATTRIBUTION;
+      break;
+    case ax::mojom::Role::kAnnotationCommentary:
+      message_id = IDS_AX_ROLE_ANNOTATION_COMMENTARY;
+      break;
+    case ax::mojom::Role::kAnnotationPresence:
+      message_id = IDS_AX_ROLE_ANNOTATION_PRESENCE;
+      break;
+    case ax::mojom::Role::kAnnotationRevision:
+      message_id = IDS_AX_ROLE_ANNOTATION_REVISION;
+      break;
+    case ax::mojom::Role::kAnnotationSuggestion:
+      message_id = IDS_AX_ROLE_ANNOTATION_SUGGESTION;
       break;
     case ax::mojom::Role::kApplication:
       message_id = IDS_AX_ROLE_APPLICATION;
@@ -993,6 +1005,9 @@
     case ax::mojom::Role::kRuby:
       // No role description.
       break;
+    case ax::mojom::Role::kRubyAnnotation:
+      // No role description.
+      break;
     case ax::mojom::Role::kSvgRoot:
       message_id = IDS_AX_ROLE_GRAPHIC;
       break;
diff --git a/content/browser/accessibility/browser_accessibility_cocoa.mm b/content/browser/accessibility/browser_accessibility_cocoa.mm
index ca88cdf6..a43bc06 100644
--- a/content/browser/accessibility/browser_accessibility_cocoa.mm
+++ b/content/browser/accessibility/browser_accessibility_cocoa.mm
@@ -1899,6 +1899,21 @@
   }
 
   switch ([self internalRole]) {
+    case ax::mojom::Role::kAnnotationAttribution:
+      return base::SysUTF16ToNSString(content_client->GetLocalizedString(
+          IDS_AX_ROLE_ANNOTATION_ATTRIBUTION));
+    case ax::mojom::Role::kAnnotationCommentary:
+      return base::SysUTF16ToNSString(content_client->GetLocalizedString(
+          IDS_AX_ROLE_ANNOTATION_COMMENTARY));
+    case ax::mojom::Role::kAnnotationPresence:
+      return base::SysUTF16ToNSString(
+          content_client->GetLocalizedString(IDS_AX_ROLE_ANNOTATION_PRESENCE));
+    case ax::mojom::Role::kAnnotationRevision:
+      return base::SysUTF16ToNSString(
+          content_client->GetLocalizedString(IDS_AX_ROLE_ANNOTATION_REVISION));
+    case ax::mojom::Role::kAnnotationSuggestion:
+      return base::SysUTF16ToNSString(content_client->GetLocalizedString(
+          IDS_AX_ROLE_ANNOTATION_SUGGESTION));
     case ax::mojom::Role::kArticle:
       return base::SysUTF16ToNSString(
           content_client->GetLocalizedString(IDS_AX_ROLE_ARTICLE));
diff --git a/content/browser/accessibility/browser_accessibility_position.cc b/content/browser/accessibility/browser_accessibility_position.cc
index 2ff9d32..5fa5466 100644
--- a/content/browser/accessibility/browser_accessibility_position.cc
+++ b/content/browser/accessibility/browser_accessibility_position.cc
@@ -112,19 +112,14 @@
 // On some platforms, most objects are represented in the text of their parents
 // with a special (embedded object) character and not with their actual text
 // contents.
-int BrowserAccessibilityPosition::MaxTextOffsetInParent() const {
+bool BrowserAccessibilityPosition::IsEmbeddedObjectInParent() const {
 #if defined(OS_WIN) || BUILDFLAG(USE_ATK)
-  if (IsNullPosition())
-    return INVALID_OFFSET;
-  if (GetAnchor()->IsTextOnlyObject())
-    return MaxTextOffset();
   // Not all objects in the internal accessibility tree are exposed to platform
   // APIs.
-  if (GetAnchor()->PlatformIsChildOfLeaf())
-    return MaxTextOffset();
-  return 1;
+  return !IsNullPosition() && !GetAnchor()->IsTextOnlyObject() &&
+         !GetAnchor()->PlatformIsChildOfLeaf();
 #else
-  return MaxTextOffset();
+  return false;
 #endif
 }
 
diff --git a/content/browser/accessibility/browser_accessibility_position.h b/content/browser/accessibility/browser_accessibility_position.h
index 73d5b2f..081e587 100644
--- a/content/browser/accessibility/browser_accessibility_position.h
+++ b/content/browser/accessibility/browser_accessibility_position.h
@@ -46,7 +46,7 @@
   void AnchorParent(AXTreeID* tree_id, int32_t* parent_id) const override;
   BrowserAccessibility* GetNodeInTree(AXTreeID tree_id,
                                       int32_t node_id) const override;
-  int MaxTextOffsetInParent() const override;
+  bool IsEmbeddedObjectInParent() const override;
 
   bool IsInLineBreakingObject() const override;
   ax::mojom::Role GetRole() const override;
diff --git a/content/browser/accessibility/dump_accessibility_tree_browsertest.cc b/content/browser/accessibility/dump_accessibility_tree_browsertest.cc
index 57cbf02..7e08168d 100644
--- a/content/browser/accessibility/dump_accessibility_tree_browsertest.cc
+++ b/content/browser/accessibility/dump_accessibility_tree_browsertest.cc
@@ -327,6 +327,11 @@
   RunHtmlTest(FILE_PATH_LITERAL("address.html"));
 }
 
+IN_PROC_BROWSER_TEST_P(DumpAccessibilityTreeTest,
+                       AccessibilityAnnotationRoles) {
+  RunAriaTest(FILE_PATH_LITERAL("annotation-roles.html"));
+}
+
 IN_PROC_BROWSER_TEST_P(DumpAccessibilityTreeTest, AccessibilityArea) {
   RunHtmlTest(FILE_PATH_LITERAL("area.html"));
 }
diff --git a/content/browser/child_process_security_policy_impl.cc b/content/browser/child_process_security_policy_impl.cc
index 97d3cc6a..d864d37 100644
--- a/content/browser/child_process_security_policy_impl.cc
+++ b/content/browser/child_process_security_policy_impl.cc
@@ -329,18 +329,22 @@
     return false;
   }
 
-  bool CanAccessDataForOrigin(const GURL& site_url) {
-    if (origin_lock_.is_empty())
-      return true;
-    return origin_lock_ == site_url;
-  }
-
   void LockToOrigin(const GURL& gurl, BrowsingInstanceId browsing_instance_id) {
     DCHECK(origin_lock_.is_empty());
+    DCHECK_NE(SiteInstanceImpl::GetDefaultSiteURL(), gurl);
     origin_lock_ = gurl;
     lowest_browsing_instance_id_ = browsing_instance_id;
   }
 
+  void SetLowestBrowsingInstanceId(
+      BrowsingInstanceId new_browsing_instance_id_to_include) {
+    DCHECK(!new_browsing_instance_id_to_include.is_null());
+    if (lowest_browsing_instance_id_.is_null() ||
+        (new_browsing_instance_id_to_include < lowest_browsing_instance_id_)) {
+      lowest_browsing_instance_id_ = new_browsing_instance_id_to_include;
+    }
+  }
+
   const GURL& origin_lock() { return origin_lock_; }
 
   BrowsingInstanceId lowest_browsing_instance_id() {
@@ -1320,43 +1324,99 @@
 bool ChildProcessSecurityPolicyImpl::CanAccessDataForOrigin(int child_id,
                                                             const GURL& url) {
   DCHECK(IsRunningOnExpectedThread());
-
   base::AutoLock lock(lock_);
+
   SecurityState* security_state = GetSecurityState(child_id);
+  BrowserOrResourceContext browser_or_resource_context;
+  if (security_state)
+    browser_or_resource_context = security_state->GetBrowserOrResourceContext();
 
-  // Determine the BrowsingInstance ID for calculating the expected process
-  // lock URL.
   GURL expected_process_lock;
-  BrowserOrResourceContext context;
-  if (security_state) {
-    context = security_state->GetBrowserOrResourceContext();
-    if (context) {
-      BrowsingInstanceId browsing_instance_id =
-          security_state->lowest_browsing_instance_id();
-      expected_process_lock = SiteInstanceImpl::DetermineProcessLockURL(
-          IsolationContext(browsing_instance_id, context), url);
+  if (security_state && browser_or_resource_context) {
+    IsolationContext isolation_context(
+        security_state->lowest_browsing_instance_id(),
+        browser_or_resource_context);
+    expected_process_lock =
+        SiteInstanceImpl::DetermineProcessLockURL(isolation_context, url);
+
+    GURL actual_process_lock = security_state->origin_lock();
+    if (!actual_process_lock.is_empty()) {
+      // Jail-style enforcement - a process with a lock can only access data
+      // from origins that require exactly the same lock.
+      if (actual_process_lock == expected_process_lock)
+        return true;
+    } else {
+      // Citadel-style enforcement - an unlocked process should not be able to
+      // access data from origins that require a lock.
+#if !defined(OS_ANDROID)
+      // TODO(lukasza): https://crbug.com/566091: Once remote NTP is capable of
+      // embedding OOPIFs, start enforcing citadel-style checks on desktop
+      // platforms.
+      // TODO(lukasza): https://crbug.com/614463: Enforce isolation within
+      // GuestView (once OOPIFs are supported within GuestView).
+      return true;
+#else
+      // TODO(acolwell, lukasza): https://crbug.com/764958: Make it possible to
+      // call ShouldLockToOrigin (and GetSiteForURL?) on the IO thread.
+      if (BrowserThread::CurrentlyOn(BrowserThread::IO))
+        return true;
+      DCHECK_CURRENTLY_ON(BrowserThread::UI);
+
+      // TODO(lukasza): Consider making the checks below IO-thread-friendly, by
+      // storing |is_unused| inside SecurityState.
+      RenderProcessHost* process = RenderProcessHostImpl::FromID(child_id);
+      if (process) {  // |process| can be null in unittests
+        // Unlocked process can be legitimately used when navigating from an
+        // unused process (about:blank, NTP on Android) to an isolated origin.
+        // See also https://crbug.com/945399.  Returning |true| below will allow
+        // such navigations to succeed (i.e. pass CanCommitOriginAndUrl checks).
+        // We don't expect unused processes to be used outside of navigations
+        // (e.g. when checking CanAccessDataForOrigin for localStorage, etc.).
+        if (process->IsUnused())
+          return true;
+      }
+
+      // TODO(alexmos, lukasza): https://crbug.com/764958: Consider making
+      // ShouldLockToOrigin work with |expected_process_lock| instead of
+      // |site_url|.
+      GURL site_url = SiteInstanceImpl::GetSiteForURL(isolation_context, url);
+
+      // A process with no lock can only access data from origins that do not
+      // require a locked process.
+      bool should_lock_target =
+          SiteInstanceImpl::ShouldLockToOrigin(isolation_context, site_url);
+      if (!should_lock_target)
+        return true;
+#endif
     }
   }
 
-  bool can_access =
-      context && security_state &&
-      security_state->CanAccessDataForOrigin(expected_process_lock);
-  if (!can_access) {
-    // Returning false here will result in a renderer kill.  Set some crash
-    // keys that will help understand the circumstances of that kill.
-    std::string killed_process_origin_lock;
-    if (!security_state) {
-      killed_process_origin_lock = "(child id not found)";
-    } else if (!context) {
-      killed_process_origin_lock = "(context is null)";
-    } else {
-      killed_process_origin_lock = security_state->origin_lock().spec();
-    }
-    LogCanAccessDataForOriginCrashKeys(expected_process_lock.spec(),
-                                       killed_process_origin_lock,
-                                       url.GetOrigin().spec());
+  // Returning false here will result in a renderer kill.  Set some crash
+  // keys that will help understand the circumstances of that kill.
+  std::string killed_process_origin_lock;
+  if (!security_state) {
+    killed_process_origin_lock = "(child id not found)";
+  } else if (!browser_or_resource_context) {
+    killed_process_origin_lock = "(context is null)";
+  } else if (security_state->origin_lock().is_empty()) {
+    killed_process_origin_lock = "(no lock - citadel-enforcement)";
+  } else {
+    killed_process_origin_lock = security_state->origin_lock().spec();
   }
-  return can_access;
+  LogCanAccessDataForOriginCrashKeys(expected_process_lock.spec(),
+                                     killed_process_origin_lock,
+                                     url.GetOrigin().spec());
+  return false;
+}
+
+void ChildProcessSecurityPolicyImpl::IncludeIsolationContext(
+    int child_id,
+    const IsolationContext& isolation_context) {
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
+  base::AutoLock lock(lock_);
+  auto* state = GetSecurityState(child_id);
+  DCHECK(state);
+  state->SetLowestBrowsingInstanceId(isolation_context.browsing_instance_id());
 }
 
 void ChildProcessSecurityPolicyImpl::LockToOrigin(
diff --git a/content/browser/child_process_security_policy_impl.h b/content/browser/child_process_security_policy_impl.h
index 7df4965..14dd452 100644
--- a/content/browser/child_process_security_policy_impl.h
+++ b/content/browser/child_process_security_policy_impl.h
@@ -259,6 +259,12 @@
   // Returns true if the specified child_id has been granted ReadRawCookies.
   bool CanReadRawCookies(int child_id);
 
+  // Notifies security state of |child_id| about the IsolationContext it will
+  // host.  The main side effect is proper setting of the lowest
+  // BrowsingInstanceId associated with the security state.
+  void IncludeIsolationContext(int child_id,
+                               const IsolationContext& isolation_context);
+
   // Sets the process identified by |child_id| as only permitted to access data
   // for the origin specified by |lock_url|. Most callers should use
   // RenderProcessHostImpl::LockToOrigin instead of calling this directly.
diff --git a/content/browser/child_process_security_policy_unittest.cc b/content/browser/child_process_security_policy_unittest.cc
index c693be0..feb8a34 100644
--- a/content/browser/child_process_security_policy_unittest.cc
+++ b/content/browser/child_process_security_policy_unittest.cc
@@ -9,9 +9,11 @@
 #include "base/bind_helpers.h"
 #include "base/files/file_path.h"
 #include "base/logging.h"
+#include "base/memory/ref_counted.h"
 #include "base/synchronization/waitable_event.h"
 #include "base/test/bind_test_util.h"
 #include "base/test/mock_log.h"
+#include "build/build_config.h"
 #include "content/browser/child_process_security_policy_impl.h"
 #include "content/browser/isolated_origin_util.h"
 #include "content/browser/site_instance_impl.h"
@@ -64,6 +66,31 @@
   std::set<std::string> schemes_;
 };
 
+bool IsCitadelProtectionEnabled() {
+#if !defined(OS_ANDROID)
+  // TODO(lukasza): https://crbug.com/566091: Once remote NTP is capable of
+  // embedding OOPIFs, start enforcing citadel-style checks on desktop
+  // platforms.
+  return false;
+#else
+  return true;
+#endif
+}
+
+void LockProcessIfNeeded(int process_id,
+                         BrowserContext* browser_context,
+                         const GURL& url) {
+  scoped_refptr<SiteInstanceImpl> site_instance =
+      SiteInstanceImpl::CreateForURL(browser_context, url);
+  if (site_instance->RequiresDedicatedProcess() &&
+      SiteInstanceImpl::ShouldLockToOrigin(site_instance->GetIsolationContext(),
+                                           site_instance->GetSiteURL())) {
+    ChildProcessSecurityPolicyImpl::GetInstance()->LockToOrigin(
+        site_instance->GetIsolationContext(), process_id,
+        site_instance->GetSiteURL());
+  }
+}
+
 }  // namespace
 
 class ChildProcessSecurityPolicyTest : public testing::Test {
@@ -277,12 +304,24 @@
   EXPECT_TRUE(p->CanRedirectToURL(GURL("data:text/html,<b>Hi</b>")));
   EXPECT_TRUE(
       p->CanRedirectToURL(GURL("filesystem:http://localhost/temporary/a.gif")));
-  EXPECT_TRUE(p->CanCommitURL(kRendererID, GURL("http://www.google.com/")));
-  EXPECT_TRUE(p->CanCommitURL(kRendererID, GURL("https://www.paypal.com/")));
-  EXPECT_TRUE(p->CanCommitURL(kRendererID, GURL("ftp://ftp.gnu.org/")));
-  EXPECT_TRUE(p->CanCommitURL(kRendererID, GURL("data:text/html,<b>Hi</b>")));
-  EXPECT_TRUE(p->CanCommitURL(
-      kRendererID, GURL("filesystem:http://localhost/temporary/a.gif")));
+  if (AreAllSitesIsolatedForTesting() && IsCitadelProtectionEnabled()) {
+    // A non-locked process cannot access URLs below (because with
+    // site-per-process all the URLs need to be isolated).
+    EXPECT_FALSE(p->CanCommitURL(kRendererID, GURL("http://www.google.com/")));
+    EXPECT_FALSE(p->CanCommitURL(kRendererID, GURL("https://www.paypal.com/")));
+    EXPECT_FALSE(p->CanCommitURL(kRendererID, GURL("ftp://ftp.gnu.org/")));
+    EXPECT_FALSE(
+        p->CanCommitURL(kRendererID, GURL("data:text/html,<b>Hi</b>")));
+    EXPECT_FALSE(p->CanCommitURL(
+        kRendererID, GURL("filesystem:http://localhost/temporary/a.gif")));
+  } else {
+    EXPECT_TRUE(p->CanCommitURL(kRendererID, GURL("http://www.google.com/")));
+    EXPECT_TRUE(p->CanCommitURL(kRendererID, GURL("https://www.paypal.com/")));
+    EXPECT_TRUE(p->CanCommitURL(kRendererID, GURL("ftp://ftp.gnu.org/")));
+    EXPECT_TRUE(p->CanCommitURL(kRendererID, GURL("data:text/html,<b>Hi</b>")));
+    EXPECT_TRUE(p->CanCommitURL(
+        kRendererID, GURL("filesystem:http://localhost/temporary/a.gif")));
+  }
   EXPECT_TRUE(
       p->CanSetAsOriginHeader(kRendererID, GURL("http://www.google.com/")));
   EXPECT_TRUE(
@@ -322,7 +361,9 @@
   ChildProcessSecurityPolicyImpl* p =
       ChildProcessSecurityPolicyImpl::GetInstance();
 
+  GURL localhost_url("http://localhost/");
   p->Add(kRendererID, browser_context());
+  LockProcessIfNeeded(kRendererID, browser_context(), localhost_url);
 
   EXPECT_TRUE(
       p->CanRequestURL(kRendererID, GURL("blob:http://localhost/some-guid")));
@@ -501,8 +542,19 @@
   p->RegisterWebSafeScheme("asdf");
   EXPECT_TRUE(p->CanRequestURL(kRendererID, GURL("asdf:rockers")));
   EXPECT_TRUE(p->CanRedirectToURL(GURL("asdf:rockers")));
-  EXPECT_TRUE(p->CanCommitURL(kRendererID, GURL("asdf:rockers")));
   EXPECT_TRUE(p->CanSetAsOriginHeader(kRendererID, GURL("asdf:rockers")));
+  if (AreAllSitesIsolatedForTesting() && IsCitadelProtectionEnabled()) {
+    // With site-per-process, all URLs (including the one below) will ask to be
+    // hosted in isolated processes.  Since |p| is not locked, CanCommitURL
+    // should return false.
+    EXPECT_FALSE(p->CanCommitURL(kRendererID, GURL("asdf:rockers")));
+
+    // After locking the process, CanCommitURL should start returning true.
+    LockProcessIfNeeded(kRendererID, browser_context(), GURL("asdf:rockers"));
+    EXPECT_TRUE(p->CanCommitURL(kRendererID, GURL("asdf:rockers")));
+  } else {
+    EXPECT_TRUE(p->CanCommitURL(kRendererID, GURL("asdf:rockers")));
+  }
 
   // Cleanup.
   p->Remove(kRendererID);
@@ -512,7 +564,9 @@
   ChildProcessSecurityPolicyImpl* p =
       ChildProcessSecurityPolicyImpl::GetInstance();
 
+  GURL file_url("file:///etc/passwd");
   p->Add(kRendererID, browser_context());
+  LockProcessIfNeeded(kRendererID, browser_context(), file_url);
 
   EXPECT_FALSE(p->CanRequestURL(kRendererID, GURL("file:///etc/passwd")));
   EXPECT_TRUE(p->CanRedirectToURL(GURL("file:///etc/passwd")));
@@ -622,6 +676,7 @@
   RegisterTestScheme("httpxml");
 
   p->Add(kRendererID, browser_context());
+  LockProcessIfNeeded(kRendererID, browser_context(), url);
 
   EXPECT_FALSE(p->CanRequestURL(kRendererID, url));
   EXPECT_FALSE(p->CanRequestURL(kRendererID, url2));
@@ -652,10 +707,12 @@
   ChildProcessSecurityPolicyImpl* p =
       ChildProcessSecurityPolicyImpl::GetInstance();
 
-  p->Add(kRendererID, browser_context());
-
   GURL icon_url("file:///tmp/foo.png");
   GURL sensitive_url("file:///etc/passwd");
+
+  p->Add(kRendererID, browser_context());
+  LockProcessIfNeeded(kRendererID, browser_context(), sensitive_url);
+
   EXPECT_FALSE(p->CanRequestURL(kRendererID, icon_url));
   EXPECT_FALSE(p->CanRequestURL(kRendererID, sensitive_url));
   EXPECT_TRUE(p->CanRedirectToURL(icon_url));
@@ -775,6 +832,8 @@
       storage::FILE_PERMISSION_USE_FILE_PERMISSION);
 
   p->Add(kRendererID, browser_context());
+  LockProcessIfNeeded(kRendererID, browser_context(), GURL("http://foo/"));
+
   base::FilePath file(TEST_PATH("/dir/testfile"));
   file = file.NormalizePathSeparators();
   storage::FileSystemURL url = storage::FileSystemURL::CreateForTest(
@@ -825,6 +884,8 @@
   // Test having no permissions upon re-adding same renderer ID.
   p->Add(kRendererID, browser_context());
   CheckHasNoFileSystemFilePermission(p, file, url);
+  LockProcessIfNeeded(kRendererID, browser_context(), GURL("http://foo/"));
+  CheckHasNoFileSystemFilePermission(p, file, url);
 
   // Cleanup.
   p->Remove(kRendererID);
@@ -967,6 +1028,7 @@
   const url::Origin origin = url::Origin::Create(url);
   {
     p->Add(kRendererID, browser_context());
+    LockProcessIfNeeded(kRendererID, browser_context(), url);
 
     EXPECT_FALSE(p->HasWebUIBindings(kRendererID));
 
@@ -1002,8 +1064,10 @@
 
     p->Remove(kRendererID);
   }
+
   {
     p->Add(kRendererID, browser_context());
+    LockProcessIfNeeded(kRendererID, browser_context(), url);
 
     EXPECT_FALSE(p->HasWebUIBindings(kRendererID));
 
@@ -1039,8 +1103,10 @@
 
     p->Remove(kRendererID);
   }
+
   {
     p->Add(kRendererID, browser_context());
+    LockProcessIfNeeded(kRendererID, browser_context(), url);
 
     EXPECT_FALSE(p->HasWebUIBindings(kRendererID));
 
@@ -1127,6 +1193,7 @@
   GURL url("file:///etc/passwd");
 
   p->Add(kRendererID, browser_context());
+  LockProcessIfNeeded(kRendererID, browser_context(), url);
 
   base::WaitableEvent ready_for_remove_event;
   base::WaitableEvent remove_called_event;
@@ -1246,11 +1313,21 @@
   p->Add(kRendererID, &browser_context);
 
   // Verify unlocked origin permissions.
-  EXPECT_TRUE(p->CanAccessDataForOrigin(kRendererID, file_url));
-  EXPECT_TRUE(p->CanAccessDataForOrigin(kRendererID, foo_http_url));
-  EXPECT_TRUE(p->CanAccessDataForOrigin(kRendererID, foo_blob_url));
-  EXPECT_TRUE(p->CanAccessDataForOrigin(kRendererID, foo_filesystem_url));
-  EXPECT_TRUE(p->CanAccessDataForOrigin(kRendererID, bar_http_url));
+  if (AreAllSitesIsolatedForTesting() && IsCitadelProtectionEnabled()) {
+    // A non-locked process cannot access URLs below (because with
+    // site-per-process all the URLs need to be isolated).
+    EXPECT_FALSE(p->CanAccessDataForOrigin(kRendererID, file_url));
+    EXPECT_FALSE(p->CanAccessDataForOrigin(kRendererID, foo_http_url));
+    EXPECT_FALSE(p->CanAccessDataForOrigin(kRendererID, foo_blob_url));
+    EXPECT_FALSE(p->CanAccessDataForOrigin(kRendererID, foo_filesystem_url));
+    EXPECT_FALSE(p->CanAccessDataForOrigin(kRendererID, bar_http_url));
+  } else {
+    EXPECT_TRUE(p->CanAccessDataForOrigin(kRendererID, file_url));
+    EXPECT_TRUE(p->CanAccessDataForOrigin(kRendererID, foo_http_url));
+    EXPECT_TRUE(p->CanAccessDataForOrigin(kRendererID, foo_blob_url));
+    EXPECT_TRUE(p->CanAccessDataForOrigin(kRendererID, foo_filesystem_url));
+    EXPECT_TRUE(p->CanAccessDataForOrigin(kRendererID, bar_http_url));
+  }
 
   // Isolate |http_url| so we can't get a default SiteInstance.
   p->AddIsolatedOrigins({url::Origin::Create(foo_http_url)},
@@ -1260,8 +1337,7 @@
   scoped_refptr<SiteInstanceImpl> foo_instance =
       SiteInstanceImpl::CreateForURL(&browser_context, foo_http_url);
   EXPECT_FALSE(foo_instance->IsDefaultSiteInstance());
-  p->LockToOrigin(foo_instance->GetIsolationContext(), kRendererID,
-                  foo_instance->GetSiteURL());
+  LockProcessIfNeeded(kRendererID, &browser_context, foo_http_url);
 
   // Verify that file access is no longer allowed.
   EXPECT_FALSE(p->CanAccessDataForOrigin(kRendererID, file_url));
@@ -1346,8 +1422,18 @@
   p->Add(kRendererID, &browser_context);
 
   // Verify unlocked process permissions.
-  for (const auto& origin : all_origins)
-    EXPECT_TRUE(p->CanAccessDataForOrigin(kRendererID, origin)) << origin;
+  for (const auto& origin : all_origins) {
+    if (AreAllSitesIsolatedForTesting() && IsCitadelProtectionEnabled()) {
+      if (origin.opaque() &&
+          origin.GetTupleOrPrecursorTupleIfOpaque().IsInvalid()) {
+        EXPECT_TRUE(p->CanAccessDataForOrigin(kRendererID, origin)) << origin;
+      } else {
+        EXPECT_FALSE(p->CanAccessDataForOrigin(kRendererID, origin)) << origin;
+      }
+    } else {
+      EXPECT_TRUE(p->CanAccessDataForOrigin(kRendererID, origin)) << origin;
+    }
+  }
 
   // Isolate |foo_origin| so we can't get a default SiteInstance.
   p->AddIsolatedOrigins({foo_origin}, IsolatedOriginSource::TEST,
@@ -1357,8 +1443,7 @@
   scoped_refptr<SiteInstanceImpl> foo_instance =
       SiteInstanceImpl::CreateForURL(&browser_context, foo_origin.GetURL());
   EXPECT_FALSE(foo_instance->IsDefaultSiteInstance());
-  p->LockToOrigin(foo_instance->GetIsolationContext(), kRendererID,
-                  foo_instance->GetSiteURL());
+  LockProcessIfNeeded(kRendererID, &browser_context, foo_origin.GetURL());
 
   // Verify that access is no longer allowed for origins that are not associated
   // with foo.com.
@@ -1388,12 +1473,13 @@
   ChildProcessSecurityPolicyImpl* p =
       ChildProcessSecurityPolicyImpl::GetInstance();
 
-  p->Add(kRendererID, browser_context());
-
   GURL url_foo1(GetWebUIURL("foo/resource1"));
   GURL url_foo2(GetWebUIURL("foo/resource2"));
   GURL url_bar(GetWebUIURL("bar/resource3"));
 
+  p->Add(kRendererID, browser_context());
+  LockProcessIfNeeded(kRendererID, browser_context(), url_foo1);
+
   EXPECT_FALSE(p->CanRequestURL(kRendererID, url_foo1));
   EXPECT_FALSE(p->CanRequestURL(kRendererID, url_foo2));
   EXPECT_FALSE(p->CanRequestURL(kRendererID, url_bar));
diff --git a/content/browser/client_hints/client_hints.cc b/content/browser/client_hints/client_hints.cc
index 5b326627..ed270d8c 100644
--- a/content/browser/client_hints/client_hints.cc
+++ b/content/browser/client_hints/client_hints.cc
@@ -20,13 +20,13 @@
 #include "content/public/browser/host_zoom_map.h"
 #include "content/public/common/content_features.h"
 #include "content/public/common/content_switches.h"
-#include "content/public/common/page_zoom.h"
 #include "net/base/url_util.h"
 #include "net/nqe/effective_connection_type.h"
 #include "net/nqe/network_quality_estimator_params.h"
 #include "services/network/public/cpp/network_quality_tracker.h"
 #include "third_party/blink/public/common/client_hints/client_hints.h"
 #include "third_party/blink/public/common/device_memory/approximated_device_memory.h"
+#include "third_party/blink/public/common/page/page_zoom.h"
 #include "third_party/blink/public/common/user_agent/user_agent_metadata.h"
 #include "third_party/blink/public/platform/web_client_hints_type.h"
 #include "ui/display/display.h"
@@ -137,7 +137,7 @@
                      ->GetDefaultZoomLevel();
   }
 
-  return content::ZoomLevelToZoomFactor(zoom_level);
+  return blink::PageZoomLevelToZoomFactor(zoom_level);
 #endif
 }
 
diff --git a/content/browser/host_zoom_map_impl.cc b/content/browser/host_zoom_map_impl.cc
index d1acf73..45fbb2e 100644
--- a/content/browser/host_zoom_map_impl.cc
+++ b/content/browser/host_zoom_map_impl.cc
@@ -24,9 +24,9 @@
 #include "content/public/browser/resource_context.h"
 #include "content/public/browser/site_instance.h"
 #include "content/public/browser/storage_partition.h"
-#include "content/public/common/page_zoom.h"
 #include "content/public/common/url_constants.h"
 #include "net/base/url_util.h"
+#include "third_party/blink/public/common/page/page_zoom.h"
 
 namespace content {
 
@@ -225,7 +225,7 @@
                                                   base::Time last_modified) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
 
-  if (ZoomValuesEqual(level, default_zoom_level_)) {
+  if (blink::PageZoomValuesEqual(level, default_zoom_level_)) {
     host_zoom_levels_.erase(host);
   } else {
     ZoomLevel& zoomLevel = host_zoom_levels_[host];
@@ -273,14 +273,14 @@
 void HostZoomMapImpl::SetDefaultZoomLevel(double level) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
 
-  if (ZoomValuesEqual(level, default_zoom_level_))
-      return;
+  if (blink::PageZoomValuesEqual(level, default_zoom_level_))
+    return;
 
   default_zoom_level_ = level;
 
   // First, remove all entries that match the new default zoom level.
   for (auto it = host_zoom_levels_.begin(); it != host_zoom_levels_.end();) {
-    if (ZoomValuesEqual(it->second.level, default_zoom_level_))
+    if (blink::PageZoomValuesEqual(it->second.level, default_zoom_level_))
       it = host_zoom_levels_.erase(it);
     else
       it++;
diff --git a/content/browser/indexed_db/indexed_db_dispatcher_host_unittest.cc b/content/browser/indexed_db/indexed_db_dispatcher_host_unittest.cc
index cd10d2b..03e101d 100644
--- a/content/browser/indexed_db/indexed_db_dispatcher_host_unittest.cc
+++ b/content/browser/indexed_db/indexed_db_dispatcher_host_unittest.cc
@@ -347,14 +347,8 @@
   loop3.Run();
 }
 
-// TODO(https://crbug.com/995716) Test is flaky on Mac ASan.
-#if defined(OS_MACOSX) && defined(ADDRESS_SANITIZER)
-#define MAYBE_OpenNewConnectionWhileUpgrading \
-  DISABLED_OpenNewConnectionWhileUpgrading
-#else
-#define MAYBE_OpenNewConnectionWhileUpgrading OpenNewConnectionWhileUpgrading
-#endif
-TEST_F(IndexedDBDispatcherHostTest, MAYBE_OpenNewConnectionWhileUpgrading) {
+// TODO(https://crbug.com/995716) Test is flaky on multiple platforms.
+TEST_F(IndexedDBDispatcherHostTest, DISABLED_OpenNewConnectionWhileUpgrading) {
   const int64_t kDBVersion = 1;
   const int64_t kTransactionId = 1;
   const int64_t kObjectStoreId = 10;
@@ -454,12 +448,7 @@
 }
 
 // See https://crbug.com/989723 for more context, this test seems to flake.
-#if defined(OS_WIN) || (defined(OS_MACOSX) && defined(ADDRESS_SANITIZER))
-#define MAYBE_PutWithInvalidBlob DISABLED_PutWithInvalidBlob
-#else
-#define MAYBE_PutWithInvalidBlob PutWithInvalidBlob
-#endif
-TEST_F(IndexedDBDispatcherHostTest, MAYBE_PutWithInvalidBlob) {
+TEST_F(IndexedDBDispatcherHostTest, DISABLED_PutWithInvalidBlob) {
   const int64_t kDBVersion = 1;
   const int64_t kTransactionId = 1;
   const int64_t kObjectStoreId = 10;
diff --git a/content/browser/isolated_origin_browsertest.cc b/content/browser/isolated_origin_browsertest.cc
index 2da2701..f1b3cef 100644
--- a/content/browser/isolated_origin_browsertest.cc
+++ b/content/browser/isolated_origin_browsertest.cc
@@ -1239,8 +1239,6 @@
 
 // Verify that an isolated renderer process cannot read localStorage of an
 // origin outside of its isolated site.
-// TODO(nasko): Write a test to verify the opposite - any non-isolated renderer
-// process cannot access data of an isolated site.
 IN_PROC_BROWSER_TEST_F(
     IsolatedOriginTest,
     LocalStorageOriginEnforcement_IsolatedAccessingNonIsolated) {
@@ -1253,6 +1251,7 @@
   GURL isolated_url(
       embedded_test_server()->GetURL("isolated.foo.com", "/title1.html"));
   EXPECT_TRUE(IsIsolatedOrigin(url::Origin::Create(isolated_url)));
+
   EXPECT_TRUE(NavigateToURL(shell(), isolated_url));
 
   content::RenderProcessHostKillWaiter kill_waiter(
@@ -1265,6 +1264,45 @@
   EXPECT_EQ(bad_message::RPH_MOJO_PROCESS_ERROR, kill_waiter.Wait());
 }
 
+#if defined(OS_ANDROID)
+#define MAYBE_LocalStorageOriginEnforcement_NonIsolatedAccessingIsolated \
+  LocalStorageOriginEnforcement_NonIsolatedAccessingIsolated
+#else
+// TODO(lukasza): https://crbug.com/566091: Once remote NTP is capable of
+// embedding OOPIFs, start enforcing citadel-style checks on desktop
+// platforms.
+#define MAYBE_LocalStorageOriginEnforcement_NonIsolatedAccessingIsolated \
+  DISABLED_LocalStorageOriginEnforcement_NonIsolatedAccessingIsolated
+#endif
+// Verify that a non-isolated renderer process cannot read localStorage of an
+// isolated origin.
+//
+// TODO(alexmos, lukasza): https://crbug.com/764958: Replicate this test for
+// the IO-thread case.
+IN_PROC_BROWSER_TEST_F(
+    IsolatedOriginTest,
+    MAYBE_LocalStorageOriginEnforcement_NonIsolatedAccessingIsolated) {
+  auto isolated_origin = url::Origin::Create(GURL("http://isolated.foo.com"));
+  EXPECT_TRUE(IsIsolatedOrigin(isolated_origin));
+
+  GURL nonisolated_url(
+      embedded_test_server()->GetURL("non-isolated.com", "/title1.html"));
+  EXPECT_FALSE(IsIsolatedOrigin(url::Origin::Create(nonisolated_url)));
+
+  RenderProcessHostImpl::SetStoragePartitionServiceRequestHandlerForTesting(
+      base::BindRepeating(&CreateTestStoragePartitionService, isolated_origin));
+  EXPECT_TRUE(NavigateToURL(shell(), nonisolated_url));
+
+  content::RenderProcessHostKillWaiter kill_waiter(
+      shell()->web_contents()->GetMainFrame()->GetProcess());
+  // Use ignore_result here, since on Android the renderer process is
+  // terminated, but ExecuteScript still returns true. It properly returns
+  // false on all other platforms.
+  ignore_result(ExecuteScript(shell()->web_contents()->GetMainFrame(),
+                              "localStorage.length;"));
+  EXPECT_EQ(bad_message::RPH_MOJO_PROCESS_ERROR, kill_waiter.Wait());
+}
+
 // Verify that an IPC request for reading localStorage of an *opaque* origin
 // will be rejected.
 IN_PROC_BROWSER_TEST_F(IsolatedOriginTest,
diff --git a/content/browser/loader/navigation_url_loader_impl.cc b/content/browser/loader/navigation_url_loader_impl.cc
index c828608..6d0d316 100644
--- a/content/browser/loader/navigation_url_loader_impl.cc
+++ b/content/browser/loader/navigation_url_loader_impl.cc
@@ -230,12 +230,19 @@
   new_request->has_user_gesture = request_info->common_params->has_user_gesture;
   new_request->enable_load_timing = true;
 
+  FrameTreeNode* frame_tree_node =
+      FrameTreeNode::GloballyFindByID(frame_tree_node_id);
   if (request_info->is_main_frame) {
-    new_request->mode = network::mojom::RequestMode::kNavigate;
+    // `<portal>` acts like a top-level navigation, but we want to represent it
+    // as a nested navigation for the purposes of security checks like
+    // `Sec-Fetch-Mode`.
+    new_request->mode =
+        frame_tree_node &&
+                WebContentsImpl::FromFrameTreeNode(frame_tree_node)->IsPortal()
+            ? network::mojom::RequestMode::kNavigateNestedFrame
+            : network::mojom::RequestMode::kNavigate;
   } else {
     new_request->mode = network::mojom::RequestMode::kNavigateNestedFrame;
-    FrameTreeNode* frame_tree_node =
-        FrameTreeNode::GloballyFindByID(frame_tree_node_id);
     if (frame_tree_node && (frame_tree_node->frame_owner_element_type() ==
                                 blink::FrameOwnerElementType::kObject ||
                             frame_tree_node->frame_owner_element_type() ==
diff --git a/content/browser/renderer_host/render_process_host_impl.cc b/content/browser/renderer_host/render_process_host_impl.cc
index 0400baf..694b2a4 100644
--- a/content/browser/renderer_host/render_process_host_impl.cc
+++ b/content/browser/renderer_host/render_process_host_impl.cc
@@ -2927,7 +2927,6 @@
   // Propagate the following switches to the renderer command line (along
   // with any associated values) if present in the browser command line.
   static const char* const kSwitchNames[] = {
-    network::switches::kNoReferrers,
     network::switches::kExplicitlyAllowedPorts,
     service_manager::switches::kDisableInProcessStackTraces,
     service_manager::switches::kDisableSeccompFilterSandbox,
diff --git a/content/browser/renderer_host/render_widget_host_unittest.cc b/content/browser/renderer_host/render_widget_host_unittest.cc
index 2bc014f..542aa4fa 100644
--- a/content/browser/renderer_host/render_widget_host_unittest.cc
+++ b/content/browser/renderer_host/render_widget_host_unittest.cc
@@ -58,6 +58,7 @@
 #include "mojo/public/cpp/bindings/pending_remote.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/public/common/page/page_zoom.h"
 #include "ui/display/screen.h"
 #include "ui/events/base_event_utils.h"
 #include "ui/events/blink/blink_features.h"
@@ -762,7 +763,7 @@
   // The zoom has changed so host should send out a sync message
   EXPECT_CALL(mock_visual_properties_manager_, SendVisualProperties(_, _))
       .Times(1);
-  double new_zoom_level = content::ZoomFactorToZoomLevel(0.25);
+  double new_zoom_level = blink::PageZoomFactorToZoomLevel(0.25);
   delegate_->SetZoomLevel(new_zoom_level);
   EXPECT_TRUE(host_->SynchronizeVisualProperties());
   EXPECT_FALSE(host_->visual_properties_ack_pending_);
diff --git a/content/browser/site_instance_impl.cc b/content/browser/site_instance_impl.cc
index d0d4e89..2cee6bf 100644
--- a/content/browser/site_instance_impl.cc
+++ b/content/browser/site_instance_impl.cc
@@ -1046,6 +1046,12 @@
                    << " in process locked to " << process_lock;
     }
   }
+
+  // Track which isolation contexts use the given process.  This lets
+  // ChildProcessSecurityPolicyImpl (e.g. CanAccessDataForOrigin) determine
+  // whether a given URL should require a lock or not (a dynamically isolated
+  // origin may require a lock in some isolation contexts but not in others).
+  policy->IncludeIsolationContext(process_->GetID(), GetIsolationContext());
 }
 
 // static
diff --git a/content/browser/tracing/tracing_controller_browsertest.cc b/content/browser/tracing/tracing_controller_browsertest.cc
index 9ed33ec..52a96c4 100644
--- a/content/browser/tracing/tracing_controller_browsertest.cc
+++ b/content/browser/tracing/tracing_controller_browsertest.cc
@@ -234,9 +234,6 @@
     Navigate(shell());
 
     TracingControllerImpl* controller = TracingControllerImpl::GetInstance();
-    tracing::TraceEventAgent::GetInstance()->AddMetadataGeneratorFunction(
-        base::Bind(&TracingControllerTest::GenerateMetadataDict,
-                   base::Unretained(this)));
 
     {
       base::RunLoop run_loop;
@@ -266,6 +263,9 @@
 
       metadata_ = std::make_unique<base::DictionaryValue>();
       metadata_->SetString("not-whitelisted", "this_not_found");
+      tracing::TraceEventAgent::GetInstance()->AddMetadataGeneratorFunction(
+          base::Bind(&TracingControllerTest::GenerateMetadataDict,
+                     base::Unretained(this)));
 
       bool result = controller->StopTracing(trace_data_endpoint);
       ASSERT_TRUE(result);
diff --git a/content/browser/utility_process_host.cc b/content/browser/utility_process_host.cc
index 910762c..c560a27 100644
--- a/content/browser/utility_process_host.cc
+++ b/content/browser/utility_process_host.cc
@@ -392,7 +392,6 @@
       network::switches::kIgnoreUrlFetcherCertRequests,
       network::switches::kLogNetLog,
       network::switches::kNetLogCaptureMode,
-      network::switches::kNoReferrers,
       network::switches::kExplicitlyAllowedPorts,
       service_manager::switches::kNoSandbox,
 #if defined(OS_MACOSX)
diff --git a/content/browser/web_contents/web_contents_impl.cc b/content/browser/web_contents/web_contents_impl.cc
index cfaab6e90..f408405 100644
--- a/content/browser/web_contents/web_contents_impl.cc
+++ b/content/browser/web_contents/web_contents_impl.cc
@@ -132,7 +132,6 @@
 #include "content/public/common/content_features.h"
 #include "content/public/common/content_switches.h"
 #include "content/public/common/page_state.h"
-#include "content/public/common/page_zoom.h"
 #include "content/public/common/referrer_type_converters.h"
 #include "content/public/common/result_codes.h"
 #include "content/public/common/url_utils.h"
@@ -153,6 +152,7 @@
 #include "third_party/blink/public/common/associated_interfaces/associated_interface_provider.h"
 #include "third_party/blink/public/common/frame/sandbox_flags.h"
 #include "third_party/blink/public/common/mime_util/mime_util.h"
+#include "third_party/blink/public/common/page/page_zoom.h"
 #include "third_party/blink/public/mojom/loader/pause_subresource_loading_handle.mojom.h"
 #include "third_party/blink/public/mojom/mediastream/media_stream.mojom-shared.h"
 #include "third_party/blink/public/platform/web_security_style.h"
@@ -572,8 +572,10 @@
       is_showing_before_unload_dialog_(false),
       last_active_time_(base::TimeTicks::Now()),
       closed_by_user_gesture_(false),
-      minimum_zoom_percent_(static_cast<int>(kMinimumZoomFactor * 100)),
-      maximum_zoom_percent_(static_cast<int>(kMaximumZoomFactor * 100)),
+      minimum_zoom_percent_(
+          static_cast<int>(blink::kMinimumPageZoomFactor * 100)),
+      maximum_zoom_percent_(
+          static_cast<int>(blink::kMaximumPageZoomFactor * 100)),
       zoom_scroll_remainder_(0),
       fullscreen_widget_process_id_(ChildProcessHost::kInvalidUniqueID),
       fullscreen_widget_routing_id_(MSG_ROUTING_NONE),
diff --git a/content/browser/zoom_browsertest.cc b/content/browser/zoom_browsertest.cc
index f575a34c..7983ca39 100644
--- a/content/browser/zoom_browsertest.cc
+++ b/content/browser/zoom_browsertest.cc
@@ -12,7 +12,6 @@
 #include "content/public/browser/navigation_entry.h"
 #include "content/public/browser/notification_service.h"
 #include "content/public/browser/notification_types.h"
-#include "content/public/common/page_zoom.h"
 #include "content/public/test/browser_test_utils.h"
 #include "content/public/test/content_browser_test.h"
 #include "content/public/test/content_browser_test_utils.h"
@@ -21,6 +20,7 @@
 #include "content/test/content_browser_test_utils_internal.h"
 #include "net/dns/mock_host_resolver.h"
 #include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/public/common/page/page_zoom.h"
 #include "url/gurl.h"
 
 namespace content {
@@ -244,7 +244,7 @@
     ResizeObserver observer(root->current_frame_host());
 
     const double new_zoom_level =
-        default_zoom_level + ZoomFactorToZoomLevel(new_zoom_factor);
+        default_zoom_level + blink::PageZoomFactorToZoomLevel(new_zoom_factor);
     host_zoom_map->SetZoomLevelForHost(top_level_host, new_zoom_level);
 
     WaitForResize(msg_queue, observer);
@@ -311,7 +311,7 @@
                                  scale_one_grandchild_width, kTolerance);
 
     const double new_zoom_level =
-        default_zoom_level + ZoomFactorToZoomLevel(new_zoom_factor);
+        default_zoom_level + blink::PageZoomFactorToZoomLevel(new_zoom_factor);
     host_zoom_map->SetZoomLevelForHost(top_level_host, new_zoom_level);
 
     WaitAndCheckFrameZoom(msg_queue, frame_observers);
@@ -356,7 +356,7 @@
 
   const double new_zoom_factor = 2.0;
   const double new_zoom_level =
-      default_zoom_level + ZoomFactorToZoomLevel(new_zoom_factor);
+      default_zoom_level + blink::PageZoomFactorToZoomLevel(new_zoom_factor);
 
   // This should not cause the nested iframe to change its zoom.
   host_zoom_map->SetZoomLevelForHost("b.com", new_zoom_level);
@@ -415,7 +415,8 @@
                                  scale_one_grandchild_width, kTolerance);
 
     const double new_default_zoom_level =
-        default_zoom_level + ZoomFactorToZoomLevel(new_default_zoom_factor);
+        default_zoom_level +
+        blink::PageZoomFactorToZoomLevel(new_default_zoom_factor);
 
     host_zoom_map->SetZoomLevelForHost("b.com", new_default_zoom_level + 1.0);
     host_zoom_map->SetDefaultZoomLevel(new_default_zoom_level);
@@ -470,7 +471,7 @@
                                  scale_one_child2_width, kTolerance);
 
     const double new_zoom_level =
-        default_zoom_level + ZoomFactorToZoomLevel(new_zoom_factor);
+        default_zoom_level + blink::PageZoomFactorToZoomLevel(new_zoom_factor);
     host_zoom_map->SetZoomLevelForHost(top_level_host, new_zoom_level);
 
     WaitAndCheckFrameZoom(msg_queue, frame_observers);
@@ -519,7 +520,7 @@
                                  scale_one_child_width, kTolerance);
 
     const double new_zoom_level =
-        default_zoom_level + ZoomFactorToZoomLevel(new_zoom_factor);
+        default_zoom_level + blink::PageZoomFactorToZoomLevel(new_zoom_factor);
     host_zoom_map->SetZoomLevelForHost(top_level_host, new_zoom_level);
 
     WaitAndCheckFrameZoom(msg_queue, frame_observers);
@@ -562,7 +563,8 @@
   const double kZoomFactorForRedirectedHost = 1.5;
   HostZoomMap* host_zoom_map = HostZoomMap::GetForWebContents(web_contents());
   host_zoom_map->SetZoomLevelForHost(
-      redirected_host, ZoomFactorToZoomLevel(kZoomFactorForRedirectedHost));
+      redirected_host,
+      blink::PageZoomFactorToZoomLevel(kZoomFactorForRedirectedHost));
 
   // Navigation to a.com doesn't change the zoom level, but when it redirects
   // to b.com, and then a subframe loads, the zoom should change.
@@ -607,7 +609,7 @@
   // Set a zoom for a host that will be navigated to below.
   const double new_zoom_factor = 2.0;
   const double new_zoom_level =
-      default_zoom_level + ZoomFactorToZoomLevel(new_zoom_factor);
+      default_zoom_level + blink::PageZoomFactorToZoomLevel(new_zoom_factor);
   host_zoom_map->SetZoomLevelForHost("foo.com", new_zoom_level);
 
   // Navigate forward in the same RFH to a site with that host via a
diff --git a/content/common/BUILD.gn b/content/common/BUILD.gn
index a3beecf..2d73082 100644
--- a/content/common/BUILD.gn
+++ b/content/common/BUILD.gn
@@ -195,7 +195,6 @@
     "page_messages.h",
     "page_state_serialization.cc",
     "page_state_serialization.h",
-    "page_zoom.cc",
     "pepper_file_util.cc",
     "pepper_file_util.h",
     "pepper_plugin_list.cc",
diff --git a/content/common/page_zoom.cc b/content/common/page_zoom.cc
deleted file mode 100644
index 2b7c7a06..0000000
--- a/content/common/page_zoom.cc
+++ /dev/null
@@ -1,30 +0,0 @@
-// Copyright (c) 2011 The Chromium 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 <cmath>
-
-#include "content/public/common/page_zoom.h"
-
-namespace content {
-
-// Mirrored in third_party/blink/renderer/core/exported/web_view_impl.cc.
-const double kMinimumZoomFactor = 0.25;
-const double kMaximumZoomFactor = 5.0;
-
-const double kEpsilon = 0.001;
-const double kTextSizeMultiplierRatio = 1.2;
-
-bool ZoomValuesEqual(double value_a, double value_b) {
-  return (std::fabs(value_a - value_b) <= kEpsilon);
-}
-
-double ZoomLevelToZoomFactor(double zoom_level) {
-  return std::pow(kTextSizeMultiplierRatio, zoom_level);
-}
-
-double ZoomFactorToZoomLevel(double factor) {
-  return std::log(factor) / std::log(kTextSizeMultiplierRatio);
-}
-
-}  // namespace content
diff --git a/content/common/service_worker/service_worker_loader_helpers.cc b/content/common/service_worker/service_worker_loader_helpers.cc
index 649fee9..65af54da 100644
--- a/content/common/service_worker/service_worker_loader_helpers.cc
+++ b/content/common/service_worker/service_worker_loader_helpers.cc
@@ -130,7 +130,7 @@
       original_request.method, original_request.url,
       original_request.site_for_cookies, first_party_url_policy,
       original_request.referrer_policy,
-      network::ComputeReferrer(original_request.referrer),
+      original_request.referrer.GetAsReferrer().spec(),
       response_head.headers->response_code(),
       original_request.url.Resolve(new_location),
       net::RedirectUtil::GetReferrerPolicyHeader(response_head.headers.get()),
diff --git a/content/public/common/page_zoom.h b/content/public/common/page_zoom.h
index aa5e56a..49e0810 100644
--- a/content/public/common/page_zoom.h
+++ b/content/public/common/page_zoom.h
@@ -5,8 +5,6 @@
 #ifndef CONTENT_PUBLIC_COMMON_PAGE_ZOOM_H_
 #define CONTENT_PUBLIC_COMMON_PAGE_ZOOM_H_
 
-#include "content/common/content_export.h"
-
 namespace content {
 
 // This enum is the parameter to various text/page zoom commands so we know
@@ -17,28 +15,6 @@
   PAGE_ZOOM_IN    = 1,
 };
 
-// The minimum zoom factor permitted for a page. This is an alternative to
-// WebView::minTextSizeMultiplier.
-CONTENT_EXPORT extern const double kMinimumZoomFactor;
-
-// The maximum zoom factor permitted for a page. This is an alternative to
-// WebView::maxTextSizeMultiplier.
-CONTENT_EXPORT extern const double kMaximumZoomFactor;
-
-// Epsilon value for comparing two floating-point zoom values. We don't use
-// std::numeric_limits<> because it is too precise for zoom values. Zoom
-// values lose precision due to factor/level conversions. A value of 0.001
-// is precise enough for zoom value comparisons.
-CONTENT_EXPORT extern const double kEpsilon;
-
-// Test if two zoom values (either zoom factors or zoom levels) should be
-// considered equal.
-CONTENT_EXPORT bool ZoomValuesEqual(double value_a, double value_b);
-
-// Converts between zoom factors and levels.
-CONTENT_EXPORT double ZoomLevelToZoomFactor(double zoom_level);
-CONTENT_EXPORT double ZoomFactorToZoomLevel(double factor);
-
 }  // namespace content
 
 #endif  // CONTENT_PUBLIC_COMMON_PAGE_ZOOM_H_
diff --git a/content/public/common/referrer.cc b/content/public/common/referrer.cc
index 50245ea..d8ed5796 100644
--- a/content/public/common/referrer.cc
+++ b/content/public/common/referrer.cc
@@ -117,13 +117,6 @@
 }
 
 // static
-void Referrer::SetReferrerForRequest(net::URLRequest* request,
-                                     const Referrer& referrer) {
-  request->SetReferrer(network::ComputeReferrer(referrer.url));
-  request->set_referrer_policy(ReferrerPolicyForUrlRequest(referrer.policy));
-}
-
-// static
 net::URLRequest::ReferrerPolicy Referrer::ReferrerPolicyForUrlRequest(
     network::mojom::ReferrerPolicy referrer_policy) {
   switch (referrer_policy) {
diff --git a/content/public/common/referrer.h b/content/public/common/referrer.h
index c6e32b65..9c85047f 100644
--- a/content/public/common/referrer.h
+++ b/content/public/common/referrer.h
@@ -47,9 +47,6 @@
       const url::Origin& initiator,
       network::mojom::ReferrerPolicy policy);
 
-  static void SetReferrerForRequest(net::URLRequest* request,
-                                    const Referrer& referrer);
-
   static net::URLRequest::ReferrerPolicy ReferrerPolicyForUrlRequest(
       network::mojom::ReferrerPolicy referrer_policy);
 
diff --git a/content/public/common/url_utils.cc b/content/public/common/url_utils.cc
index 0242fe3..e1879aa 100644
--- a/content/public/common/url_utils.cc
+++ b/content/public/common/url_utils.cc
@@ -115,8 +115,11 @@
 bool IsSafeRedirectTarget(const GURL& from_url, const GURL& to_url) {
   static const base::NoDestructor<base::flat_set<base::StringPiece>>
       kUnsafeSchemes(base::flat_set<base::StringPiece>({
-        url::kAboutScheme, url::kDataScheme, url::kFileScheme,
+        url::kAboutScheme, url::kFileScheme,
             url::kFileSystemScheme, url::kBlobScheme,
+#if !defined(CHROMECAST_BUILD)
+            url::kDataScheme,
+#endif
 #if defined(OS_ANDROID)
             url::kContentScheme,
 #endif
diff --git a/content/renderer/accessibility/blink_ax_tree_source.cc b/content/renderer/accessibility/blink_ax_tree_source.cc
index cde7dfc..e63e64b 100644
--- a/content/renderer/accessibility/blink_ax_tree_source.cc
+++ b/content/renderer/accessibility/blink_ax_tree_source.cc
@@ -805,10 +805,10 @@
                                     src.AccessKey().Utf8());
     }
 
-    if (src.AriaAutoComplete().length()) {
+    if (src.AutoComplete().length()) {
       TruncateAndAddStringAttribute(dst,
                                     ax::mojom::StringAttribute::kAutoComplete,
-                                    src.AriaAutoComplete().Utf8());
+                                    src.AutoComplete().Utf8());
     }
 
     if (src.Action() != ax::mojom::DefaultActionVerb::kNone) {
diff --git a/content/renderer/render_view_browsertest.cc b/content/renderer/render_view_browsertest.cc
index 59f1152..5437184 100644
--- a/content/renderer/render_view_browsertest.cc
+++ b/content/renderer/render_view_browsertest.cc
@@ -37,7 +37,6 @@
 #include "content/public/browser/web_ui_controller_factory.h"
 #include "content/public/common/bindings_policy.h"
 #include "content/public/common/content_switches.h"
-#include "content/public/common/page_zoom.h"
 #include "content/public/common/url_constants.h"
 #include "content/public/common/url_utils.h"
 #include "content/public/common/use_zoom_for_dsf_policy.h"
@@ -70,6 +69,7 @@
 #include "third_party/blink/public/common/dom_storage/session_storage_namespace_id.h"
 #include "third_party/blink/public/common/origin_trials/origin_trial_policy.h"
 #include "third_party/blink/public/common/origin_trials/trial_token_validator.h"
+#include "third_party/blink/public/common/page/page_zoom.h"
 #include "third_party/blink/public/mojom/loader/request_context_frame_type.mojom.h"
 #include "third_party/blink/public/platform/modules/service_worker/web_service_worker_network_provider.h"
 #include "third_party/blink/public/platform/scheduler/test/renderer_scheduler_test_support.h"
@@ -2398,7 +2398,7 @@
   gfx::Size size = GetPreferredSize();
   EXPECT_EQ(gfx::Size(400 + scrollbar_width, 400), size);
 
-  EXPECT_TRUE(view()->SetZoomLevel(ZoomFactorToZoomLevel(2.0)));
+  EXPECT_TRUE(view()->SetZoomLevel(blink::PageZoomFactorToZoomLevel(2.0)));
   size = GetPreferredSize();
   EXPECT_EQ(gfx::Size(800 + scrollbar_width, 800), size);
 }
@@ -2784,7 +2784,7 @@
   EXPECT_FALSE(view()->SetZoomLevel(0));
 
   // Change the zoom level to 25% and check if the view gets the change.
-  EXPECT_TRUE(view()->SetZoomLevel(content::ZoomFactorToZoomLevel(0.25)));
+  EXPECT_TRUE(view()->SetZoomLevel(blink::PageZoomFactorToZoomLevel(0.25)));
 }
 
 #endif
diff --git a/content/shell/test_runner/web_ax_object_proxy.cc b/content/shell/test_runner/web_ax_object_proxy.cc
index 2170b861..98b852dc 100644
--- a/content/shell/test_runner/web_ax_object_proxy.cc
+++ b/content/shell/test_runner/web_ax_object_proxy.cc
@@ -37,8 +37,16 @@
       return result.append("Alert");
     case ax::mojom::Role::kAnchor:
       return result.append("Anchor");
-    case ax::mojom::Role::kAnnotation:
-      return result.append("Annotation");
+    case ax::mojom::Role::kAnnotationAttribution:
+      return result.append("AnnotationAttribution");
+    case ax::mojom::Role::kAnnotationCommentary:
+      return result.append("AnnotationCommentary");
+    case ax::mojom::Role::kAnnotationPresence:
+      return result.append("AnnotationPresence");
+    case ax::mojom::Role::kAnnotationRevision:
+      return result.append("AnnotationRevision");
+    case ax::mojom::Role::kAnnotationSuggestion:
+      return result.append("AnnotationSuggestion");
     case ax::mojom::Role::kApplication:
       return result.append("Application");
     case ax::mojom::Role::kArticle:
@@ -299,6 +307,8 @@
       return result.append("Row");
     case ax::mojom::Role::kRuby:
       return result.append("Ruby");
+    case ax::mojom::Role::kRubyAnnotation:
+      return result.append("RubyAnnotation");
     case ax::mojom::Role::kSvgRoot:
       return result.append("SVGRoot");
     case ax::mojom::Role::kScrollBar:
@@ -1283,7 +1293,7 @@
 
 std::string WebAXObjectProxy::Autocomplete() {
   accessibility_object_.UpdateLayoutAndCheckValidity();
-  return accessibility_object_.AriaAutoComplete().Utf8();
+  return accessibility_object_.AutoComplete().Utf8();
 }
 
 std::string WebAXObjectProxy::Current() {
diff --git a/content/test/BUILD.gn b/content/test/BUILD.gn
index 58862874..4f1be9a 100644
--- a/content/test/BUILD.gn
+++ b/content/test/BUILD.gn
@@ -1896,7 +1896,6 @@
     "../common/mime_sniffing_throttle_unittest.cc",
     "../common/origin_util_unittest.cc",
     "../common/page_state_serialization_unittest.cc",
-    "../common/page_zoom_unittest.cc",
     "../common/service_manager/service_manager_connection_impl_unittest.cc",
     "../common/service_worker/service_worker_utils_unittest.cc",
     "../common/tab_switch_time_recorder_unittest.cc",
diff --git a/content/test/data/accessibility/aria/annotation-roles-expected-android.txt b/content/test/data/accessibility/aria/annotation-roles-expected-android.txt
new file mode 100644
index 0000000..75e0426
--- /dev/null
+++ b/content/test/data/accessibility/aria/annotation-roles-expected-android.txt
@@ -0,0 +1,6 @@
+android.webkit.WebView focusable focused scrollable
+++android.view.View role_description='authoring info' name='annotation-attribution'
+++android.view.View role_description='comments' name='annotation-commentary'
+++android.view.View role_description='live presence' name='annotation-presence'
+++android.view.View role_description='revision' name='annotation-revision'
+++android.view.View role_description='suggestion' name='annotation-suggestion'
diff --git a/content/test/data/accessibility/aria/annotation-roles-expected-auralinux.txt b/content/test/data/accessibility/aria/annotation-roles-expected-auralinux.txt
new file mode 100644
index 0000000..f3e5c546
--- /dev/null
+++ b/content/test/data/accessibility/aria/annotation-roles-expected-auralinux.txt
@@ -0,0 +1,6 @@
+[document web]
+++[section] name='annotation-attribution' xml-roles:annotation-attribution
+++[section] name='annotation-commentary' xml-roles:annotation-commentary
+++[section] name='annotation-presence' xml-roles:annotation-presence
+++[section] name='annotation-revision' xml-roles:annotation-revision
+++[section] name='annotation-suggestion' xml-roles:annotation-suggestion
diff --git a/content/test/data/accessibility/aria/annotation-roles-expected-blink.txt b/content/test/data/accessibility/aria/annotation-roles-expected-blink.txt
new file mode 100644
index 0000000..0464074
--- /dev/null
+++ b/content/test/data/accessibility/aria/annotation-roles-expected-blink.txt
@@ -0,0 +1,7 @@
+rootWebArea
+++genericContainer ignored
+++++annotationAttribution name='annotation-attribution'
+++++annotationCommentary name='annotation-commentary'
+++++annotationPresence name='annotation-presence'
+++++annotationRevision name='annotation-revision'
+++++annotationSuggestion name='annotation-suggestion'
diff --git a/content/test/data/accessibility/aria/annotation-roles-expected-mac.txt b/content/test/data/accessibility/aria/annotation-roles-expected-mac.txt
new file mode 100644
index 0000000..159abfd5
--- /dev/null
+++ b/content/test/data/accessibility/aria/annotation-roles-expected-mac.txt
@@ -0,0 +1,6 @@
+AXWebArea AXRoleDescription='HTML content'
+++AXGroup AXRoleDescription='annotation-attribution' AXDescription='annotation-attribution'
+++AXGroup AXRoleDescription='annotation-commentary' AXDescription='annotation-commentary'
+++AXGroup AXRoleDescription='annotation-presence' AXDescription='annotation-presence'
+++AXGroup AXRoleDescription='annotation-revision' AXDescription='annotation-revision'
+++AXGroup AXRoleDescription='annotation-suggestion' AXDescription='annotation-suggestion'
diff --git a/content/test/data/accessibility/aria/annotation-roles.html b/content/test/data/accessibility/aria/annotation-roles.html
new file mode 100644
index 0000000..a2c99404
--- /dev/null
+++ b/content/test/data/accessibility/aria/annotation-roles.html
@@ -0,0 +1,16 @@
+<!--
+@MAC-ALLOW:AXRole*
+@MAC-ALLOW:AXSubrole*
+@WIN-ALLOW:xml-roles:*
+@AURALINUX-ALLOW:xml-roles*
+-->
+<!DOCTYPE html>
+<html>
+<body>
+  <div role="annotation-attribution" aria-label="annotation-attribution"></div>
+  <div role="annotation-commentary" aria-label="annotation-commentary"></div>
+  <div role="annotation-presence" aria-label="annotation-presence"></div>
+  <div role="annotation-revision" aria-label="annotation-revision"></div>
+  <div role="annotation-suggestion" aria-label="annotation-suggestion"></div>
+</body>
+</html>
diff --git a/content/test/data/accessibility/html/ruby-expected-blink.txt b/content/test/data/accessibility/html/ruby-expected-blink.txt
index 96b5ab0..4c510485 100644
--- a/content/test/data/accessibility/html/ruby-expected-blink.txt
+++ b/content/test/data/accessibility/html/ruby-expected-blink.txt
@@ -1,8 +1,8 @@
 rootWebArea
 ++genericContainer
 ++++ruby
-++++++annotation
+++++++rubyAnnotation
 ++++++++staticText name='ruby text'
 ++++++++++inlineTextBox name='ruby text'
 ++++++staticText name='ruby base'
-++++++++inlineTextBox name='ruby base'
\ No newline at end of file
+++++++++inlineTextBox name='ruby base'
diff --git a/device/vr/windows_mixed_reality/mixed_reality_statics.cc b/device/vr/windows_mixed_reality/mixed_reality_statics.cc
index b58e5f3e..138333d 100644
--- a/device/vr/windows_mixed_reality/mixed_reality_statics.cc
+++ b/device/vr/windows_mixed_reality/mixed_reality_statics.cc
@@ -17,11 +17,8 @@
 
 namespace device {
 
-// TODO(crbug.com/941546): Remove namespaces to comply with coding standard.
-using namespace ABI::Windows::Graphics::Holographic;
-using namespace Microsoft::WRL;
-using namespace Microsoft::WRL::Wrappers;
-using namespace Windows::Foundation;
+namespace Holographic = ABI::Windows::Graphics::Holographic;
+using Microsoft::WRL::ComPtr;
 
 class DEVICE_VR_EXPORT MixedRealityDeviceStaticsImpl
     : public MixedRealityDeviceStatics {
@@ -34,7 +31,7 @@
 
  private:
   // Adds get_IsAvailable and get_IsSupported to HolographicSpaceStatics.
-  ComPtr<IHolographicSpaceStatics2> holographic_space_statics_;
+  ComPtr<Holographic::IHolographicSpaceStatics2> holographic_space_statics_;
 };
 
 VRTestHook* MixedRealityDeviceStatics::test_hook_ = nullptr;
@@ -73,7 +70,7 @@
   base::win::ScopedHString holographic_space_string =
       base::win::ScopedHString::Create(
           RuntimeClass_Windows_Graphics_Holographic_HolographicSpace);
-  ComPtr<IHolographicSpaceStatics> holographic_space_statics;
+  ComPtr<Holographic::IHolographicSpaceStatics> holographic_space_statics;
   HRESULT hr = base::win::RoGetActivationFactory(
       holographic_space_string.get(), IID_PPV_ARGS(&holographic_space_statics));
   if (FAILED(hr))
diff --git a/extensions/browser/guest_view/web_view/web_view_guest.cc b/extensions/browser/guest_view/web_view/web_view_guest.cc
index 78786fa..4b69ca49 100644
--- a/extensions/browser/guest_view/web_view/web_view_guest.cc
+++ b/extensions/browser/guest_view/web_view/web_view_guest.cc
@@ -40,7 +40,6 @@
 #include "content/public/browser/web_contents.h"
 #include "content/public/browser/web_contents_delegate.h"
 #include "content/public/common/content_switches.h"
-#include "content/public/common/page_zoom.h"
 #include "content/public/common/result_codes.h"
 #include "content/public/common/stop_find_action.h"
 #include "content/public/common/url_constants.h"
@@ -68,6 +67,7 @@
 #include "net/cookies/canonical_cookie.h"
 #include "third_party/blink/public/common/logging/logging_utils.h"
 #include "third_party/blink/public/common/mediastream/media_stream_request.h"
+#include "third_party/blink/public/common/page/page_zoom.h"
 #include "ui/base/models/simple_menu_model.h"
 #include "ui/events/keycodes/keyboard_codes.h"
 #include "url/url_constants.h"
@@ -197,7 +197,7 @@
 }
 
 double ConvertZoomLevelToZoomFactor(double zoom_level) {
-  double zoom_factor = content::ZoomLevelToZoomFactor(zoom_level);
+  double zoom_factor = blink::PageZoomLevelToZoomFactor(zoom_level);
   // Because the conversion from zoom level to zoom factor isn't perfect, the
   // resulting zoom factor is rounded to the nearest 6th decimal place.
   zoom_factor = round(zoom_factor * 1000000) / 1000000;
@@ -1236,7 +1236,7 @@
   did_set_explicit_zoom_ = true;
   auto* zoom_controller = ZoomController::FromWebContents(web_contents());
   DCHECK(zoom_controller);
-  double zoom_level = content::ZoomFactorToZoomLevel(zoom_factor);
+  double zoom_level = blink::PageZoomFactorToZoomLevel(zoom_factor);
   zoom_controller->SetZoomLevel(zoom_level);
 }
 
diff --git a/extensions/common/api/automation.idl b/extensions/common/api/automation.idl
index 0475987..b7b0764 100644
--- a/extensions/common/api/automation.idl
+++ b/extensions/common/api/automation.idl
@@ -81,7 +81,11 @@
     alert,
     alertDialog,
     anchor,
-    annotation,
+    annotationAttribution,
+    annotationCommentary,
+    annotationPresence,
+    annotationRevision,
+    annotationSuggestion,
     application,
     article,
     audio,
@@ -231,6 +235,7 @@
     row,
     rowHeader,
     ruby,
+    rubyAnnotation,
     scrollBar,
     scrollView,
     search,
diff --git a/gpu/command_buffer/build_raster_cmd_buffer.py b/gpu/command_buffer/build_raster_cmd_buffer.py
index eaf5e695..4e18014 100755
--- a/gpu/command_buffer/build_raster_cmd_buffer.py
+++ b/gpu/command_buffer/build_raster_cmd_buffer.py
@@ -135,7 +135,6 @@
       'viz::ResourceFormat::BGRX_1010102',
       'viz::ResourceFormat::YVU_420',
       'viz::ResourceFormat::YUV_420_BIPLANAR',
-      'viz::ResourceFormat::UYVY_422',
 
     ],
     'invalid': [
diff --git a/gpu/command_buffer/service/raster_cmd_validation_implementation_autogen.h b/gpu/command_buffer/service/raster_cmd_validation_implementation_autogen.h
index d820958..030b62f 100644
--- a/gpu/command_buffer/service/raster_cmd_validation_implementation_autogen.h
+++ b/gpu/command_buffer/service/raster_cmd_validation_implementation_autogen.h
@@ -81,7 +81,6 @@
     viz::ResourceFormat::RGBX_8888,    viz::ResourceFormat::BGRX_8888,
     viz::ResourceFormat::RGBX_1010102, viz::ResourceFormat::BGRX_1010102,
     viz::ResourceFormat::YVU_420,      viz::ResourceFormat::YUV_420_BIPLANAR,
-    viz::ResourceFormat::UYVY_422,
 };
 
 Validators::Validators()
diff --git a/gpu/command_buffer/service/shared_image_backing_factory_ahardwarebuffer_unittest.cc b/gpu/command_buffer/service/shared_image_backing_factory_ahardwarebuffer_unittest.cc
index 4f1590a..6647d31d 100644
--- a/gpu/command_buffer/service/shared_image_backing_factory_ahardwarebuffer_unittest.cc
+++ b/gpu/command_buffer/service/shared_image_backing_factory_ahardwarebuffer_unittest.cc
@@ -290,7 +290,7 @@
     return;
 
   auto mailbox = Mailbox::GenerateForSharedImage();
-  auto format = viz::ResourceFormat::UYVY_422;
+  auto format = viz::ResourceFormat::YUV_420_BIPLANAR;
   gfx::Size size(256, 256);
   auto color_space = gfx::ColorSpace::CreateSRGB();
   uint32_t usage = SHARED_IMAGE_USAGE_GLES2;
diff --git a/gpu/command_buffer/service/shared_image_backing_factory_gl_texture_unittest.cc b/gpu/command_buffer/service/shared_image_backing_factory_gl_texture_unittest.cc
index d67c23d..dc487f72 100644
--- a/gpu/command_buffer/service/shared_image_backing_factory_gl_texture_unittest.cc
+++ b/gpu/command_buffer/service/shared_image_backing_factory_gl_texture_unittest.cc
@@ -461,7 +461,7 @@
 
 TEST_P(SharedImageBackingFactoryGLTextureTest, InvalidFormat) {
   auto mailbox = Mailbox::GenerateForSharedImage();
-  auto format = viz::ResourceFormat::UYVY_422;
+  auto format = viz::ResourceFormat::YUV_420_BIPLANAR;
   gfx::Size size(256, 256);
   auto color_space = gfx::ColorSpace::CreateSRGB();
   uint32_t usage = SHARED_IMAGE_USAGE_GLES2;
diff --git a/ios/chrome/browser/browser_state/chrome_browser_state.h b/ios/chrome/browser/browser_state/chrome_browser_state.h
index 5900efd9..2d309a50 100644
--- a/ios/chrome/browser/browser_state/chrome_browser_state.h
+++ b/ios/chrome/browser/browser_state/chrome_browser_state.h
@@ -114,11 +114,6 @@
   virtual net::URLRequestContextGetter* CreateRequestContext(
       ProtocolHandlerMap* protocol_handlers) = 0;
 
-  // Creates a isolated net::URLRequestContextGetter. Should only be called once
-  // per partition_path per browser state object.
-  virtual net::URLRequestContextGetter* CreateIsolatedRequestContext(
-      const base::FilePath& partition_path) = 0;
-
   // web::BrowserState
   net::URLRequestContextGetter* GetRequestContext() override;
   void UpdateCorsExemptHeader(
diff --git a/ios/chrome/browser/browser_state/chrome_browser_state_impl.cc b/ios/chrome/browser/browser_state/chrome_browser_state_impl.cc
index 6885ea4b..936bab1 100644
--- a/ios/chrome/browser/browser_state/chrome_browser_state_impl.cc
+++ b/ios/chrome/browser/browser_state/chrome_browser_state_impl.cc
@@ -195,12 +195,6 @@
       .get();
 }
 
-net::URLRequestContextGetter*
-ChromeBrowserStateImpl::CreateIsolatedRequestContext(
-    const base::FilePath& partition_path) {
-  return io_data_->CreateIsolatedAppRequestContextGetter(partition_path).get();
-}
-
 void ChromeBrowserStateImpl::ClearNetworkingHistorySince(
     base::Time time,
     const base::Closure& completion) {
diff --git a/ios/chrome/browser/browser_state/chrome_browser_state_impl.h b/ios/chrome/browser/browser_state/chrome_browser_state_impl.h
index 9e4dc8b..008cae48 100644
--- a/ios/chrome/browser/browser_state/chrome_browser_state_impl.h
+++ b/ios/chrome/browser/browser_state/chrome_browser_state_impl.h
@@ -40,8 +40,6 @@
                                    const base::Closure& completion) override;
   net::URLRequestContextGetter* CreateRequestContext(
       ProtocolHandlerMap* protocol_handlers) override;
-  net::URLRequestContextGetter* CreateIsolatedRequestContext(
-      const base::FilePath& partition_path) override;
 
   // BrowserState:
   bool IsOffTheRecord() const override;
diff --git a/ios/chrome/browser/browser_state/chrome_browser_state_impl_io_data.h b/ios/chrome/browser/browser_state/chrome_browser_state_impl_io_data.h
index e986f7716..7803dfd 100644
--- a/ios/chrome/browser/browser_state/chrome_browser_state_impl_io_data.h
+++ b/ios/chrome/browser/browser_state/chrome_browser_state_impl_io_data.h
@@ -47,9 +47,6 @@
     CreateMainRequestContextGetter(ProtocolHandlerMap* protocol_handlers,
                                    PrefService* local_state,
                                    IOSChromeIOThread* io_thread) const;
-    scoped_refptr<IOSChromeURLRequestContextGetter>
-    CreateIsolatedAppRequestContextGetter(
-        const base::FilePath& partition_path) const;
 
     ChromeBrowserStateIOData* io_data() const;
 
@@ -112,10 +109,6 @@
       std::unique_ptr<IOSChromeNetworkDelegate> chrome_network_delegate,
       ProfileParams* profile_params,
       ProtocolHandlerMap* protocol_handlers) const override;
-  AppRequestContext* InitializeAppRequestContext(
-      net::URLRequestContext* main_context) const override;
-  AppRequestContext* AcquireIsolatedAppRequestContext(
-      net::URLRequestContext* main_context) const override;
 
   // Deletes all network related data since |time|. It deletes transport
   // security state since |time| and also deletes HttpServerProperties data.
diff --git a/ios/chrome/browser/browser_state/chrome_browser_state_impl_io_data.mm b/ios/chrome/browser/browser_state/chrome_browser_state_impl_io_data.mm
index 3249d66..a1b3c61d 100644
--- a/ios/chrome/browser/browser_state/chrome_browser_state_impl_io_data.mm
+++ b/ios/chrome/browser/browser_state/chrome_browser_state_impl_io_data.mm
@@ -97,28 +97,6 @@
   return main_request_context_getter_;
 }
 
-scoped_refptr<IOSChromeURLRequestContextGetter>
-ChromeBrowserStateImplIOData::Handle::CreateIsolatedAppRequestContextGetter(
-    const base::FilePath& partition_path) const {
-  DCHECK_CURRENTLY_ON(web::WebThread::UI);
-  // Check that the partition_path is not the same as the base profile path. We
-  // expect isolated partition, which will never go to the default profile path.
-  CHECK(partition_path != browser_state_->GetStatePath());
-  LazyInitialize();
-
-  // Keep a map of request context getters, one per requested storage partition.
-  IOSChromeURLRequestContextGetterMap::iterator iter =
-      app_request_context_getter_map_.find(partition_path);
-  if (iter != app_request_context_getter_map_.end())
-    return iter->second;
-
-  IOSChromeURLRequestContextGetter* context =
-      IOSChromeURLRequestContextGetter::CreateForIsolatedApp(
-          browser_state_->GetRequestContext(), io_data_, partition_path);
-  app_request_context_getter_map_[partition_path] = context;
-  return context;
-}
-
 ChromeBrowserStateIOData* ChromeBrowserStateImplIOData::Handle::io_data()
     const {
   LazyInitialize();
@@ -259,62 +237,6 @@
   lazy_params_.reset();
 }
 
-ChromeBrowserStateIOData::AppRequestContext*
-ChromeBrowserStateImplIOData::InitializeAppRequestContext(
-    net::URLRequestContext* main_context) const {
-  // Copy most state from the main context.
-  AppRequestContext* context = new AppRequestContext();
-  context->CopyFrom(main_context);
-
-  // Build a new HttpNetworkSession.
-  net::HttpNetworkSession::Context session_context =
-      http_network_session_->context();
-  std::unique_ptr<net::HttpNetworkSession> http_network_session(
-      new net::HttpNetworkSession(http_network_session_->params(),
-                                  session_context));
-
-  // Use a separate HTTP disk cache for isolated apps.
-  std::unique_ptr<net::HttpCache::BackendFactory> app_backend =
-      net::HttpCache::DefaultBackend::InMemory(0);
-  std::unique_ptr<net::HttpCache> app_http_cache =
-      CreateHttpFactory(http_network_session.get(), std::move(app_backend));
-
-  cookie_util::CookieStoreConfig ios_cookie_config(
-      base::FilePath(),
-      cookie_util::CookieStoreConfig::EPHEMERAL_SESSION_COOKIES,
-      cookie_util::CookieStoreConfig::COOKIE_STORE_IOS, nullptr);
-  // TODO(crbug.com/779106): Check if cookiestore type should be changed.
-  std::unique_ptr<net::CookieStore> cookie_store =
-      cookie_util::CreateCookieStore(
-          ios_cookie_config, std::make_unique<net::NSHTTPSystemCookieStore>(),
-          main_context->net_log());
-
-  // Transfer ownership of the HttpNetworkSession, cookies, and
-  // cache to AppRequestContext.
-  context->SetHttpNetworkSession(std::move(http_network_session));
-  context->SetCookieStore(std::move(cookie_store));
-  context->SetHttpTransactionFactory(std::move(app_http_cache));
-
-  std::unique_ptr<net::URLRequestJobFactoryImpl> job_factory(
-      new net::URLRequestJobFactoryImpl());
-  std::unique_ptr<net::URLRequestJobFactory> top_job_factory(
-      SetUpJobFactoryDefaults(std::move(job_factory),
-                              main_context->network_delegate()));
-  context->SetJobFactory(std::move(top_job_factory));
-
-  return context;
-}
-
-ChromeBrowserStateIOData::AppRequestContext*
-ChromeBrowserStateImplIOData::AcquireIsolatedAppRequestContext(
-    net::URLRequestContext* main_context) const {
-  // We create per-app contexts on demand, unlike the others above.
-  AppRequestContext* app_request_context =
-      InitializeAppRequestContext(main_context);
-  DCHECK(app_request_context);
-  return app_request_context;
-}
-
 void ChromeBrowserStateImplIOData::ClearNetworkingHistorySinceOnIOThread(
     base::Time time,
     const base::Closure& completion) {
diff --git a/ios/chrome/browser/browser_state/chrome_browser_state_io_data.h b/ios/chrome/browser/browser_state/chrome_browser_state_io_data.h
index ed3ec66..8b034f8d 100644
--- a/ios/chrome/browser/browser_state/chrome_browser_state_io_data.h
+++ b/ios/chrome/browser/browser_state/chrome_browser_state_io_data.h
@@ -80,9 +80,6 @@
   void Init(ProtocolHandlerMap* protocol_handlers) const;
 
   net::URLRequestContext* GetMainRequestContext() const;
-  net::URLRequestContext* GetIsolatedAppRequestContext(
-      net::URLRequestContext* main_context,
-      const base::FilePath& partition_path) const;
 
   // Sets the cookie store associated with a partition path.
   // The path must exist. If there is already a cookie store, it is deleted.
@@ -225,16 +222,6 @@
       ProfileParams* profile_params,
       ProtocolHandlerMap* protocol_handlers) const = 0;
 
-  // Does an on-demand initialization of a RequestContext for the given
-  // isolated app.
-  virtual AppRequestContext* InitializeAppRequestContext(
-      net::URLRequestContext* main_context) const = 0;
-
-  // These functions are used to transfer ownership of the lazily initialized
-  // context from ChromeBrowserStateIOData to the URLRequestContextGetter.
-  virtual AppRequestContext* AcquireIsolatedAppRequestContext(
-      net::URLRequestContext* main_context) const = 0;
-
   // The order *DOES* matter for the majority of these member variables, so
   // don't move them around unless you know what you're doing!
   // General rules:
diff --git a/ios/chrome/browser/browser_state/chrome_browser_state_io_data.mm b/ios/chrome/browser/browser_state/chrome_browser_state_io_data.mm
index a205099..dbbf43f2 100644
--- a/ios/chrome/browser/browser_state/chrome_browser_state_io_data.mm
+++ b/ios/chrome/browser/browser_state/chrome_browser_state_io_data.mm
@@ -228,21 +228,6 @@
   return main_request_context_.get();
 }
 
-net::URLRequestContext* ChromeBrowserStateIOData::GetIsolatedAppRequestContext(
-    net::URLRequestContext* main_context,
-    const base::FilePath& partition_path) const {
-  DCHECK(initialized_);
-  AppRequestContext* context = nullptr;
-  if (base::Contains(app_request_context_map_, partition_path)) {
-    context = app_request_context_map_[partition_path];
-  } else {
-    context = AcquireIsolatedAppRequestContext(main_context);
-    app_request_context_map_[partition_path] = context;
-  }
-  DCHECK(context);
-  return context;
-}
-
 void ChromeBrowserStateIOData::SetCookieStoreForPartitionPath(
     std::unique_ptr<net::CookieStore> cookie_store,
     const base::FilePath& partition_path) {
diff --git a/ios/chrome/browser/browser_state/off_the_record_chrome_browser_state_impl.cc b/ios/chrome/browser/browser_state/off_the_record_chrome_browser_state_impl.cc
index 917822e..8967cd39 100644
--- a/ios/chrome/browser/browser_state/off_the_record_chrome_browser_state_impl.cc
+++ b/ios/chrome/browser/browser_state/off_the_record_chrome_browser_state_impl.cc
@@ -110,10 +110,3 @@
     base::PostTask(FROM_HERE, {web::WebThread::UI}, completion);
   }
 }
-
-net::URLRequestContextGetter*
-OffTheRecordChromeBrowserStateImpl::CreateIsolatedRequestContext(
-    const base::FilePath& partition_path) {
-  NOTIMPLEMENTED();
-  return nullptr;
-}
diff --git a/ios/chrome/browser/browser_state/off_the_record_chrome_browser_state_impl.h b/ios/chrome/browser/browser_state/off_the_record_chrome_browser_state_impl.h
index 314048c..8061d748 100644
--- a/ios/chrome/browser/browser_state/off_the_record_chrome_browser_state_impl.h
+++ b/ios/chrome/browser/browser_state/off_the_record_chrome_browser_state_impl.h
@@ -33,8 +33,6 @@
                                    const base::Closure& completion) override;
   net::URLRequestContextGetter* CreateRequestContext(
       ProtocolHandlerMap* protocol_handlers) override;
-  net::URLRequestContextGetter* CreateIsolatedRequestContext(
-      const base::FilePath& partition_path) override;
 
   // BrowserState:
   bool IsOffTheRecord() const override;
diff --git a/ios/chrome/browser/browser_state/off_the_record_chrome_browser_state_io_data.h b/ios/chrome/browser/browser_state/off_the_record_chrome_browser_state_io_data.h
index a24e375..a7b6bfc4 100644
--- a/ios/chrome/browser/browser_state/off_the_record_chrome_browser_state_io_data.h
+++ b/ios/chrome/browser/browser_state/off_the_record_chrome_browser_state_io_data.h
@@ -24,7 +24,6 @@
 class CookieStore;
 class HttpNetworkSession;
 class HttpTransactionFactory;
-class URLRequestContext;
 }  // namespace net
 
 // OffTheRecordChromeBrowserState owns a
@@ -94,10 +93,6 @@
       std::unique_ptr<IOSChromeNetworkDelegate> chrome_network_delegate,
       ProfileParams* profile_params,
       ProtocolHandlerMap* protocol_handlers) const override;
-  AppRequestContext* InitializeAppRequestContext(
-      net::URLRequestContext* main_context) const override;
-  AppRequestContext* AcquireIsolatedAppRequestContext(
-      net::URLRequestContext* main_context) const override;
 
   mutable std::unique_ptr<IOSChromeNetworkDelegate> network_delegate_;
 
diff --git a/ios/chrome/browser/browser_state/off_the_record_chrome_browser_state_io_data.mm b/ios/chrome/browser/browser_state/off_the_record_chrome_browser_state_io_data.mm
index 0a8d5c8e2..efeda60 100644
--- a/ios/chrome/browser/browser_state/off_the_record_chrome_browser_state_io_data.mm
+++ b/ios/chrome/browser/browser_state/off_the_record_chrome_browser_state_io_data.mm
@@ -194,17 +194,3 @@
                                               main_context->network_delegate());
   main_context->set_job_factory(main_job_factory_.get());
 }
-
-ChromeBrowserStateIOData::AppRequestContext*
-OffTheRecordChromeBrowserStateIOData::InitializeAppRequestContext(
-    net::URLRequestContext* main_context) const {
-  NOTREACHED();
-  return nullptr;
-}
-
-ChromeBrowserStateIOData::AppRequestContext*
-OffTheRecordChromeBrowserStateIOData::AcquireIsolatedAppRequestContext(
-    net::URLRequestContext* main_context) const {
-  NOTREACHED();
-  return nullptr;
-}
diff --git a/ios/chrome/browser/browser_state/test_chrome_browser_state.h b/ios/chrome/browser/browser_state/test_chrome_browser_state.h
index 71b231c..ea58e97b 100644
--- a/ios/chrome/browser/browser_state/test_chrome_browser_state.h
+++ b/ios/chrome/browser/browser_state/test_chrome_browser_state.h
@@ -55,8 +55,6 @@
                                    const base::Closure& completion) override;
   net::URLRequestContextGetter* CreateRequestContext(
       ProtocolHandlerMap* protocol_handlers) override;
-  net::URLRequestContextGetter* CreateIsolatedRequestContext(
-      const base::FilePath& partition_path) override;
 
   // This method is defined as empty following the paradigm of
   // TestingProfile::DestroyOffTheRecordProfile().
diff --git a/ios/chrome/browser/browser_state/test_chrome_browser_state.mm b/ios/chrome/browser/browser_state/test_chrome_browser_state.mm
index a7e1531..d342568b 100644
--- a/ios/chrome/browser/browser_state/test_chrome_browser_state.mm
+++ b/ios/chrome/browser/browser_state/test_chrome_browser_state.mm
@@ -284,12 +284,6 @@
       base::CreateSingleThreadTaskRunner({web::WebThread::IO}));
 }
 
-net::URLRequestContextGetter*
-TestChromeBrowserState::CreateIsolatedRequestContext(
-    const base::FilePath& partition_path) {
-  return nullptr;
-}
-
 void TestChromeBrowserState::CreateWebDataService() {
   ignore_result(
       ios::WebDataServiceFactory::GetInstance()->SetTestingFactoryAndUse(
diff --git a/ios/chrome/browser/metrics/mobile_session_shutdown_metrics_provider.h b/ios/chrome/browser/metrics/mobile_session_shutdown_metrics_provider.h
index 4557f3f6a..cc7238f9 100644
--- a/ios/chrome/browser/metrics/mobile_session_shutdown_metrics_provider.h
+++ b/ios/chrome/browser/metrics/mobile_session_shutdown_metrics_provider.h
@@ -27,6 +27,11 @@
   MOBILE_SESSION_SHUTDOWN_TYPE_COUNT,
 };
 
+// Percentage of battery level which is assumed low enough to have possibly
+// been the reason for the previous session ending in an unclean shutdown.
+// Percent rpresented by a value between 0 and 1.
+extern const float kCriticallyLowBatteryLevel;
+
 class MobileSessionShutdownMetricsProvider : public metrics::MetricsProvider {
  public:
   explicit MobileSessionShutdownMetricsProvider(
diff --git a/ios/chrome/browser/metrics/mobile_session_shutdown_metrics_provider.mm b/ios/chrome/browser/metrics/mobile_session_shutdown_metrics_provider.mm
index aecb4b31..5f2ac096 100644
--- a/ios/chrome/browser/metrics/mobile_session_shutdown_metrics_provider.mm
+++ b/ios/chrome/browser/metrics/mobile_session_shutdown_metrics_provider.mm
@@ -110,6 +110,8 @@
 }
 }  // namespace
 
+const float kCriticallyLowBatteryLevel = 0.01;
+
 MobileSessionShutdownMetricsProvider::MobileSessionShutdownMetricsProvider(
     metrics::MetricsService* metrics_service)
     : metrics_service_(metrics_service) {
@@ -172,15 +174,15 @@
   bool possible_explanation =
       // Log any of the following cases as a possible explanation for the
       // crash:
+      // - device restarted while the battery was critically low
+      (session_info.deviceBatteryState == DeviceBatteryState::kUnplugged &&
+       session_info.deviceBatteryLevel <= kCriticallyLowBatteryLevel &&
+       session_info.OSRestartedAfterPreviousSession) ||
       // - storage was critically low
       (session_info.availableDeviceStorage >= 0 &&
        session_info.availableDeviceStorage <= kCriticallyLowDeviceStorage) ||
       // - OS version changed
       session_info.isFirstSessionAfterOSUpgrade ||
-      // - OS was restarted
-      session_info.OSRestartedAfterPreviousSession ||
-      // - low power mode enabled
-      session_info.deviceWasInLowPowerMode ||
       // - device in abnormal thermal state
       session_info.deviceThermalState == DeviceThermalState::kCritical ||
       session_info.deviceThermalState == DeviceThermalState::kSerious;
diff --git a/ios/chrome/browser/metrics/mobile_session_shutdown_metrics_provider_unittest.mm b/ios/chrome/browser/metrics/mobile_session_shutdown_metrics_provider_unittest.mm
index 328ba38..5c8d6c4e 100644
--- a/ios/chrome/browser/metrics/mobile_session_shutdown_metrics_provider_unittest.mm
+++ b/ios/chrome/browser/metrics/mobile_session_shutdown_metrics_provider_unittest.mm
@@ -206,7 +206,10 @@
       "Stability.iOS.UTE.HasPossibleExplanation", false, 1);
 
   // Test UTE with low battery when OS did not restart.
-  [PreviousSessionInfo sharedInstance].deviceBatteryLevel = 0.01;
+  [PreviousSessionInfo sharedInstance].deviceBatteryLevel =
+      kCriticallyLowBatteryLevel;
+  [PreviousSessionInfo sharedInstance].deviceBatteryState =
+      previous_session_info_constants::DeviceBatteryState::kUnplugged;
   histogram_tester = std::make_unique<base::HistogramTester>();
   metrics_provider_->ProvidePreviousSessionData(nullptr);
   histogram_tester->ExpectUniqueSample(
@@ -222,6 +225,15 @@
       "Stability.iOS.UTE.OSRestartedAfterPreviousSession", true, 1);
   histogram_tester->ExpectUniqueSample(
       "Stability.iOS.UTE.HasPossibleExplanation", true, 1);
+
+  // Test UTE with normal battery when OS restarted after previous session.
+  [PreviousSessionInfo sharedInstance].deviceBatteryLevel = 50;
+  histogram_tester = std::make_unique<base::HistogramTester>();
+  metrics_provider_->ProvidePreviousSessionData(nullptr);
+  histogram_tester->ExpectUniqueSample(
+      "Stability.iOS.UTE.OSRestartedAfterPreviousSession", true, 1);
+  histogram_tester->ExpectUniqueSample(
+      "Stability.iOS.UTE.HasPossibleExplanation", false, 1);
 }
 
 INSTANTIATE_TEST_SUITE_P(/* No InstantiationName */,
diff --git a/ios/chrome/browser/metrics/previous_session_info_private.h b/ios/chrome/browser/metrics/previous_session_info_private.h
index 3c94d04..fb7f5a1 100644
--- a/ios/chrome/browser/metrics/previous_session_info_private.h
+++ b/ios/chrome/browser/metrics/previous_session_info_private.h
@@ -11,6 +11,8 @@
 @property(nonatomic, assign) BOOL didSeeMemoryWarningShortlyBeforeTerminating;
 @property(nonatomic, assign) BOOL isFirstSessionAfterUpgrade;
 @property(nonatomic, assign) float deviceBatteryLevel;
+@property(nonatomic, assign)
+    previous_session_info_constants::DeviceBatteryState deviceBatteryState;
 @property(nonatomic, assign) BOOL OSRestartedAfterPreviousSession;
 
 + (void)resetSharedInstanceForTesting;
diff --git a/ios/chrome/browser/net/ios_chrome_url_request_context_getter.cc b/ios/chrome/browser/net/ios_chrome_url_request_context_getter.cc
index 6ccfee6..84ce0ae 100644
--- a/ios/chrome/browser/net/ios_chrome_url_request_context_getter.cc
+++ b/ios/chrome/browser/net/ios_chrome_url_request_context_getter.cc
@@ -51,32 +51,6 @@
   ProtocolHandlerMap protocol_handlers_;
 };
 
-// Factory that creates the URLRequestContext for a given isolated app.
-class FactoryForIsolatedApp : public IOSChromeURLRequestContextFactory {
- public:
-  FactoryForIsolatedApp(const ChromeBrowserStateIOData* io_data,
-                        const base::FilePath& partition_path,
-                        net::URLRequestContextGetter* main_context)
-      : io_data_(io_data),
-        partition_path_(partition_path),
-        main_request_context_getter_(main_context) {}
-
-  net::URLRequestContext* Create() override {
-    // We will copy most of the state from the main request context.
-    //
-    // Note that this factory is one-shot.  After Create() is called once, the
-    // factory is actually destroyed. Thus it is safe to destructively pass
-    // state onwards.
-    return io_data_->GetIsolatedAppRequestContext(
-        main_request_context_getter_->GetURLRequestContext(), partition_path_);
-  }
-
- private:
-  const ChromeBrowserStateIOData* const io_data_;
-  const base::FilePath partition_path_;
-  scoped_refptr<net::URLRequestContextGetter> main_request_context_getter_;
-};
-
 }  // namespace
 
 // ----------------------------------------------------------------------------
@@ -129,14 +103,3 @@
   return new IOSChromeURLRequestContextGetter(
       std::make_unique<FactoryForMain>(io_data, protocol_handlers));
 }
-
-// static
-IOSChromeURLRequestContextGetter*
-IOSChromeURLRequestContextGetter::CreateForIsolatedApp(
-    net::URLRequestContextGetter* main_context,
-    const ChromeBrowserStateIOData* io_data,
-    const base::FilePath& partition_path) {
-  return new IOSChromeURLRequestContextGetter(
-      std::make_unique<FactoryForIsolatedApp>(io_data, partition_path,
-                                              main_context));
-}
diff --git a/ios/chrome/browser/net/ios_chrome_url_request_context_getter.h b/ios/chrome/browser/net/ios_chrome_url_request_context_getter.h
index 3fbbe29..2fde0ae 100644
--- a/ios/chrome/browser/net/ios_chrome_url_request_context_getter.h
+++ b/ios/chrome/browser/net/ios_chrome_url_request_context_getter.h
@@ -44,13 +44,6 @@
       const ChromeBrowserStateIOData* io_data,
       ProtocolHandlerMap* protocol_handlers);
 
-  // Create an instance for an original profile for an app with isolated
-  // storage. This is expected to get called on UI thread.
-  static IOSChromeURLRequestContextGetter* CreateForIsolatedApp(
-      net::URLRequestContextGetter* main_context,
-      const ChromeBrowserStateIOData* io_data,
-      const base::FilePath& partition_path);
-
   // Discard reference to URLRequestContext and inform observers of shutdown.
   // Must be called before destruction. May only be called on IO thread.
   void NotifyContextShuttingDown();
diff --git a/ios/chrome/browser/overlays/BUILD.gn b/ios/chrome/browser/overlays/BUILD.gn
index 22c60786..164b467 100644
--- a/ios/chrome/browser/overlays/BUILD.gn
+++ b/ios/chrome/browser/overlays/BUILD.gn
@@ -13,14 +13,18 @@
     "public/overlay_presenter_observer.h",
     "public/overlay_presenter_observer_bridge.h",
     "public/overlay_request.h",
+    "public/overlay_request_cancel_handler.h",
     "public/overlay_request_queue.h",
     "public/overlay_response.h",
     "public/overlay_user_data.h",
   ]
   sources = [
+    "default_overlay_request_cancel_handler.h",
+    "default_overlay_request_cancel_handler.mm",
     "overlay_presenter_impl.h",
     "overlay_presenter_impl.mm",
     "overlay_presenter_observer_bridge.mm",
+    "overlay_request_cancel_handler.mm",
     "overlay_request_impl.cc",
     "overlay_request_impl.h",
     "overlay_request_queue_impl.h",
@@ -44,6 +48,7 @@
 source_set("unit_tests") {
   testonly = true
   sources = [
+    "default_overlay_request_cancel_handler_unittest.mm",
     "overlay_presenter_impl_unittest.mm",
     "overlay_presenter_observer_bridge_unittest.mm",
     "overlay_request_impl_unittest.cc",
diff --git a/ios/chrome/browser/overlays/default_overlay_request_cancel_handler.h b/ios/chrome/browser/overlays/default_overlay_request_cancel_handler.h
new file mode 100644
index 0000000..c5d0967
--- /dev/null
+++ b/ios/chrome/browser/overlays/default_overlay_request_cancel_handler.h
@@ -0,0 +1,48 @@
+// 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_DEFAULT_OVERLAY_REQUEST_CANCEL_HANDLER_H_
+#define IOS_CHROME_BROWSER_OVERLAYS_DEFAULT_OVERLAY_REQUEST_CANCEL_HANDLER_H_
+
+#include "base/scoped_observer.h"
+#import "ios/chrome/browser/overlays/public/overlay_request_cancel_handler.h"
+#import "ios/web/public/web_state.h"
+#include "ios/web/public/web_state_observer.h"
+
+// A default implementation of OverlayRequestCancelHandler.  Cancels the request
+// for committed, document-changing navigations.
+class DefaultOverlayRequestCancelHandler : public OverlayRequestCancelHandler {
+ public:
+  DefaultOverlayRequestCancelHandler(OverlayRequest* request,
+                                     OverlayRequestQueue* queue,
+                                     web::WebState* web_state);
+  ~DefaultOverlayRequestCancelHandler() override;
+
+ private:
+  // Cancels the request for navigation events.
+  void Cancel();
+
+  // Helper object that intercepts navigation events to trigger cancellation.
+  class NavigationHelper : web::WebStateObserver {
+   public:
+    NavigationHelper(DefaultOverlayRequestCancelHandler* cancel_handler,
+                     web::WebState* web_state);
+    ~NavigationHelper() override;
+
+    // web::WebStateObserver:
+    void DidFinishNavigation(
+        web::WebState* web_state,
+        web::NavigationContext* navigation_context) override;
+    void RenderProcessGone(web::WebState* web_state) override;
+    void WebStateDestroyed(web::WebState* web_state) override;
+
+   private:
+    DefaultOverlayRequestCancelHandler* cancel_handler_ = nullptr;
+    ScopedObserver<web::WebState, web::WebStateObserver> scoped_observer_;
+  };
+
+  NavigationHelper navigation_helper_;
+};
+
+#endif  // IOS_CHROME_BROWSER_OVERLAYS_DEFAULT_OVERLAY_REQUEST_CANCEL_HANDLER_H_
diff --git a/ios/chrome/browser/overlays/default_overlay_request_cancel_handler.mm b/ios/chrome/browser/overlays/default_overlay_request_cancel_handler.mm
new file mode 100644
index 0000000..f1765ee8
--- /dev/null
+++ b/ios/chrome/browser/overlays/default_overlay_request_cancel_handler.mm
@@ -0,0 +1,58 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#import "ios/chrome/browser/overlays/default_overlay_request_cancel_handler.h"
+
+#include "base/logging.h"
+#import "ios/web/public/navigation/navigation_context.h"
+
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
+DefaultOverlayRequestCancelHandler::DefaultOverlayRequestCancelHandler(
+    OverlayRequest* request,
+    OverlayRequestQueue* queue,
+    web::WebState* web_state)
+    : OverlayRequestCancelHandler(request, queue),
+      navigation_helper_(this, web_state) {}
+
+DefaultOverlayRequestCancelHandler::~DefaultOverlayRequestCancelHandler() =
+    default;
+
+void DefaultOverlayRequestCancelHandler::Cancel() {
+  CancelRequest();
+}
+
+DefaultOverlayRequestCancelHandler::NavigationHelper::NavigationHelper(
+    DefaultOverlayRequestCancelHandler* cancel_handler,
+    web::WebState* web_state)
+    : cancel_handler_(cancel_handler), scoped_observer_(this) {
+  DCHECK(cancel_handler);
+  DCHECK(web_state);
+  scoped_observer_.Add(web_state);
+}
+
+DefaultOverlayRequestCancelHandler::NavigationHelper::~NavigationHelper() =
+    default;
+
+void DefaultOverlayRequestCancelHandler::NavigationHelper::DidFinishNavigation(
+    web::WebState* web_state,
+    web::NavigationContext* navigation_context) {
+  if (navigation_context->HasCommitted() &&
+      !navigation_context->IsSameDocument()) {
+    cancel_handler_->Cancel();
+  }
+}
+
+void DefaultOverlayRequestCancelHandler::NavigationHelper::RenderProcessGone(
+    web::WebState* web_state) {
+  cancel_handler_->Cancel();
+}
+
+void DefaultOverlayRequestCancelHandler::NavigationHelper::WebStateDestroyed(
+    web::WebState* web_state) {
+  cancel_handler_->Cancel();
+  scoped_observer_.RemoveAll();
+}
diff --git a/ios/chrome/browser/overlays/default_overlay_request_cancel_handler_unittest.mm b/ios/chrome/browser/overlays/default_overlay_request_cancel_handler_unittest.mm
new file mode 100644
index 0000000..a8c42d5
--- /dev/null
+++ b/ios/chrome/browser/overlays/default_overlay_request_cancel_handler_unittest.mm
@@ -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.
+
+#import "ios/chrome/browser/overlays/default_overlay_request_cancel_handler.h"
+
+#include "ios/chrome/browser/overlays/public/overlay_request.h"
+#include "ios/chrome/browser/overlays/public/overlay_request_queue.h"
+#include "ios/chrome/browser/overlays/test/fake_overlay_user_data.h"
+#import "ios/web/public/test/fakes/fake_navigation_context.h"
+#import "ios/web/public/test/fakes/test_web_state.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/platform_test.h"
+
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
+// Test fixture for DefaultOverlayRequestCancelHandler.
+class DefaultOverlayRequestCancelHandlerTest : public PlatformTest {
+ public:
+  DefaultOverlayRequestCancelHandlerTest() : PlatformTest() {
+    std::unique_ptr<OverlayRequest> request =
+        OverlayRequest::CreateWithConfig<FakeOverlayUserData>(nullptr);
+    queue()->AddRequest(std::move(request));
+  }
+
+  OverlayRequestQueue* queue() {
+    return OverlayRequestQueue::FromWebState(&web_state_,
+                                             OverlayModality::kWebContentArea);
+  }
+  web::TestWebState* web_state() { return &web_state_; }
+
+ private:
+  web::TestWebState web_state_;
+};
+
+// Tests that the request is removed from the queue for committed, document-
+// changing navigations.
+TEST_F(DefaultOverlayRequestCancelHandlerTest, CancelForNavigations) {
+  // Simulate a navigation.
+  web::FakeNavigationContext context;
+  context.SetHasCommitted(true);
+  context.SetIsSameDocument(false);
+  web_state()->OnNavigationFinished(&context);
+
+  // Verify that the queue is empty.
+  EXPECT_FALSE(queue()->front_request());
+}
+
+// Tests that the request is removed from the queue for renderer crashes.
+TEST_F(DefaultOverlayRequestCancelHandlerTest, CancelForRendererCrashes) {
+  web_state()->OnRenderProcessGone();
+
+  // Verify that the queue is empty.
+  EXPECT_FALSE(queue()->front_request());
+}
diff --git a/ios/chrome/browser/overlays/overlay_request_cancel_handler.mm b/ios/chrome/browser/overlays/overlay_request_cancel_handler.mm
new file mode 100644
index 0000000..26a590b
--- /dev/null
+++ b/ios/chrome/browser/overlays/overlay_request_cancel_handler.mm
@@ -0,0 +1,24 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#import "ios/chrome/browser/overlays/public/overlay_request_cancel_handler.h"
+
+#include "base/logging.h"
+#import "ios/chrome/browser/overlays/public/overlay_request_queue.h"
+
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
+OverlayRequestCancelHandler::OverlayRequestCancelHandler(
+    OverlayRequest* request,
+    OverlayRequestQueue* queue)
+    : request_(request), queue_(queue) {
+  DCHECK(request_);
+  DCHECK(queue_);
+}
+
+void OverlayRequestCancelHandler::CancelRequest() {
+  queue_->CancelRequest(request_);
+}
diff --git a/ios/chrome/browser/overlays/overlay_request_queue_impl.h b/ios/chrome/browser/overlays/overlay_request_queue_impl.h
index 33ac1d95..ccefbbc 100644
--- a/ios/chrome/browser/overlays/overlay_request_queue_impl.h
+++ b/ios/chrome/browser/overlays/overlay_request_queue_impl.h
@@ -62,9 +62,9 @@
   base::WeakPtr<OverlayRequestQueueImpl> GetWeakPtr();
 
   // Whether the queue is empty.
-  bool empty() const { return requests_.empty(); }
+  bool empty() const { return request_storages_.empty(); }
   // The number of requests in the queue.
-  size_t size() const { return requests_.size(); }
+  size_t size() const { return request_storages_.size(); }
 
   // Removes the front or back request from the queue, transferring ownership of
   // the request to the caller.  Must be called on a non-empty queue.
@@ -72,36 +72,36 @@
   std::unique_ptr<OverlayRequest> PopBackRequest();
 
   // OverlayRequestQueue:
+  void AddRequest(
+      std::unique_ptr<OverlayRequest> request,
+      std::unique_ptr<OverlayRequestCancelHandler> cancel_handler) override;
   void AddRequest(std::unique_ptr<OverlayRequest> request) override;
   OverlayRequest* front_request() const override;
   void CancelAllRequests() override;
+  void CancelRequest(OverlayRequest* request) override;
 
  private:
   // Private constructor called by container.
   explicit OverlayRequestQueueImpl(web::WebState* web_state);
 
-  // Helper object that cancels requests for navigation events.
-  class RequestCancellationHelper : public web::WebStateObserver {
-   public:
-    RequestCancellationHelper(OverlayRequestQueueImpl* queue,
-                              web::WebState* web_state);
+  // Helper object that stores OverlayRequests along with their cancellation
+  // handlers.
+  struct OverlayRequestStorage {
+    OverlayRequestStorage(
+        std::unique_ptr<OverlayRequest> request,
+        std::unique_ptr<OverlayRequestCancelHandler> cancel_handler);
+    ~OverlayRequestStorage();
 
-   private:
-    // web::WebStateObserver:
-    void DidFinishNavigation(
-        web::WebState* web_state,
-        web::NavigationContext* navigation_context) override;
-    void RenderProcessGone(web::WebState* web_state) override;
-    void WebStateDestroyed(web::WebState* web_state) override;
-
-    OverlayRequestQueueImpl* queue_ = nullptr;
+    std::unique_ptr<OverlayRequest> request;
+    std::unique_ptr<OverlayRequestCancelHandler> cancel_handler;
   };
 
-  RequestCancellationHelper cancellation_helper_;
+  web::WebState* web_state_ = nullptr;
   base::ObserverList<Observer, /* check_empty= */ true> observers_;
   // The queue used to hold the received requests.  Stored as a circular dequeue
   // to allow performant pop events from the front of the queue.
-  base::circular_deque<std::unique_ptr<OverlayRequest>> requests_;
+  base::circular_deque<std::unique_ptr<OverlayRequestStorage>>
+      request_storages_;
   base::WeakPtrFactory<OverlayRequestQueueImpl> weak_factory_;
 };
 
diff --git a/ios/chrome/browser/overlays/overlay_request_queue_impl.mm b/ios/chrome/browser/overlays/overlay_request_queue_impl.mm
index 4e893fa..b1c21a24 100644
--- a/ios/chrome/browser/overlays/overlay_request_queue_impl.mm
+++ b/ios/chrome/browser/overlays/overlay_request_queue_impl.mm
@@ -8,6 +8,7 @@
 
 #include "base/logging.h"
 #include "base/memory/ptr_util.h"
+#import "ios/chrome/browser/overlays/default_overlay_request_cancel_handler.h"
 #include "ios/chrome/browser/overlays/public/overlay_request.h"
 #import "ios/web/public/navigation/navigation_context.h"
 
@@ -44,7 +45,7 @@
 #pragma mark - OverlayRequestQueueImpl
 
 OverlayRequestQueueImpl::OverlayRequestQueueImpl(web::WebState* web_state)
-    : cancellation_helper_(this, web_state), weak_factory_(this) {}
+    : web_state_(web_state), weak_factory_(this) {}
 OverlayRequestQueueImpl::~OverlayRequestQueueImpl() = default;
 
 #pragma mark Public
@@ -62,31 +63,46 @@
 }
 
 std::unique_ptr<OverlayRequest> OverlayRequestQueueImpl::PopFrontRequest() {
-  DCHECK(!requests_.empty());
-  std::unique_ptr<OverlayRequest> request = std::move(requests_.front());
-  requests_.pop_front();
+  DCHECK(!request_storages_.empty());
+  std::unique_ptr<OverlayRequest> request =
+      std::move(request_storages_.front()->request);
+  request_storages_.pop_front();
   return request;
 }
 
 std::unique_ptr<OverlayRequest> OverlayRequestQueueImpl::PopBackRequest() {
-  DCHECK(!requests_.empty());
-  std::unique_ptr<OverlayRequest> request = std::move(requests_.back());
-  requests_.pop_back();
+  DCHECK(!request_storages_.empty());
+  std::unique_ptr<OverlayRequest> request =
+      std::move(request_storages_.back()->request);
+  request_storages_.pop_back();
   return request;
 }
 
 #pragma mark OverlayRequestQueue
 
 void OverlayRequestQueueImpl::AddRequest(
-    std::unique_ptr<OverlayRequest> request) {
-  requests_.push_back(std::move(request));
+    std::unique_ptr<OverlayRequest> request,
+    std::unique_ptr<OverlayRequestCancelHandler> cancel_handler) {
+  DCHECK(request.get());
+  DCHECK(cancel_handler.get());
+  request_storages_.push_back(std::make_unique<OverlayRequestStorage>(
+      std::move(request), std::move(cancel_handler)));
   for (auto& observer : observers_) {
-    observer.RequestAddedToQueue(this, requests_.back().get());
+    observer.RequestAddedToQueue(this, request_storages_.back()->request.get());
   }
 }
 
+void OverlayRequestQueueImpl::AddRequest(
+    std::unique_ptr<OverlayRequest> request) {
+  std::unique_ptr<OverlayRequestCancelHandler> cancel_handler =
+      std::make_unique<DefaultOverlayRequestCancelHandler>(request.get(), this,
+                                                           web_state_);
+  AddRequest(std::move(request), std::move(cancel_handler));
+}
+
 OverlayRequest* OverlayRequestQueueImpl::front_request() const {
-  return requests_.empty() ? nullptr : requests_.front().get();
+  return request_storages_.empty() ? nullptr
+                                   : request_storages_.front()->request.get();
 }
 
 void OverlayRequestQueueImpl::CancelAllRequests() {
@@ -94,37 +110,37 @@
     // Requests are cancelled in reverse order to prevent attempting to present
     // subsequent requests after the dismissal of the front request's UI.
     for (auto& observer : observers_) {
-      observer.QueuedRequestCancelled(this, requests_.back().get());
+      observer.QueuedRequestCancelled(this,
+                                      request_storages_.back()->request.get());
     }
     PopBackRequest();
   }
 }
 
-#pragma mark RequestCancellationHelper
-
-OverlayRequestQueueImpl::RequestCancellationHelper::RequestCancellationHelper(
-    OverlayRequestQueueImpl* queue,
-    web::WebState* web_state)
-    : queue_(queue) {
-  web_state->AddObserver(this);
-}
-
-void OverlayRequestQueueImpl::RequestCancellationHelper::DidFinishNavigation(
-    web::WebState* web_state,
-    web::NavigationContext* navigation_context) {
-  if (navigation_context->HasCommitted() &&
-      !navigation_context->IsSameDocument()) {
-    queue_->CancelAllRequests();
+void OverlayRequestQueueImpl::CancelRequest(OverlayRequest* request) {
+  // Find the iterator for the storage holding |request|.
+  auto storage_iter = request_storages_.begin();
+  auto end = request_storages_.end();
+  while (storage_iter != end) {
+    if ((*storage_iter)->request.get() == request)
+      break;
+    ++storage_iter;
   }
+  if (storage_iter == end)
+    return;
+
+  // Notify observers of cancellation and remove the storage.
+  for (auto& observer : observers_) {
+    observer.QueuedRequestCancelled(this, request);
+  }
+  request_storages_.erase(storage_iter);
 }
 
-void OverlayRequestQueueImpl::RequestCancellationHelper::RenderProcessGone(
-    web::WebState* web_state) {
-  queue_->CancelAllRequests();
-}
+#pragma mark OverlayRequestStorage
 
-void OverlayRequestQueueImpl::RequestCancellationHelper::WebStateDestroyed(
-    web::WebState* web_state) {
-  queue_->CancelAllRequests();
-  web_state->RemoveObserver(this);
-}
+OverlayRequestQueueImpl::OverlayRequestStorage::OverlayRequestStorage(
+    std::unique_ptr<OverlayRequest> request,
+    std::unique_ptr<OverlayRequestCancelHandler> cancel_handler)
+    : request(std::move(request)), cancel_handler(std::move(cancel_handler)) {}
+
+OverlayRequestQueueImpl::OverlayRequestStorage::~OverlayRequestStorage() {}
diff --git a/ios/chrome/browser/overlays/overlay_request_queue_impl_unittest.mm b/ios/chrome/browser/overlays/overlay_request_queue_impl_unittest.mm
index ad14f05..f92bc732 100644
--- a/ios/chrome/browser/overlays/overlay_request_queue_impl_unittest.mm
+++ b/ios/chrome/browser/overlays/overlay_request_queue_impl_unittest.mm
@@ -5,6 +5,7 @@
 #import "ios/chrome/browser/overlays/overlay_request_queue_impl.h"
 
 #include "ios/chrome/browser/overlays/public/overlay_request.h"
+#import "ios/chrome/browser/overlays/public/overlay_request_cancel_handler.h"
 #include "ios/chrome/browser/overlays/test/fake_overlay_user_data.h"
 #import "ios/web/public/test/fakes/test_web_state.h"
 #include "testing/gmock/include/gmock/gmock.h"
@@ -27,6 +28,16 @@
   MOCK_METHOD2(QueuedRequestCancelled,
                void(OverlayRequestQueueImpl*, OverlayRequest*));
 };
+
+// Custom cancel handler that can be manually triggered.
+class FakeCancelHandler : public OverlayRequestCancelHandler {
+ public:
+  FakeCancelHandler(OverlayRequest* request, OverlayRequestQueue* queue)
+      : OverlayRequestCancelHandler(request, queue) {}
+
+  // Cancels the associated request.
+  void TriggerCancellation() { CancelRequest(); }
+};
 }  // namespace
 
 // Test fixture for RequestQueueImpl.
@@ -110,3 +121,25 @@
   EXPECT_EQ(0U, queue()->size());
   EXPECT_TRUE(queue()->empty());
 }
+
+// Tests that state is updated correctly and observer callbacks are received
+// when cancelling a request with a custom cancel handler.
+TEST_F(OverlayRequestQueueImplTest, CustomCancelHandler) {
+  std::unique_ptr<OverlayRequest> passed_request =
+      OverlayRequest::CreateWithConfig<FakeOverlayUserData>(nullptr);
+  OverlayRequest* request = passed_request.get();
+  std::unique_ptr<FakeCancelHandler> passed_cancel_handler =
+      std::make_unique<FakeCancelHandler>(request, queue());
+  FakeCancelHandler* cancel_handler = passed_cancel_handler.get();
+  EXPECT_CALL(observer(), RequestAddedToQueue(queue(), request));
+  queue()->AddRequest(std::move(passed_request),
+                      std::move(passed_cancel_handler));
+
+  // Trigger cancellation via the cancel handler, and verify that the request is
+  // correctly removed.
+  EXPECT_CALL(observer(), QueuedRequestCancelled(queue(), request));
+  cancel_handler->TriggerCancellation();
+
+  EXPECT_EQ(0U, queue()->size());
+  EXPECT_TRUE(queue()->empty());
+}
diff --git a/ios/chrome/browser/overlays/public/overlay_request_cancel_handler.h b/ios/chrome/browser/overlays/public/overlay_request_cancel_handler.h
new file mode 100644
index 0000000..72f7bf2d
--- /dev/null
+++ b/ios/chrome/browser/overlays/public/overlay_request_cancel_handler.h
@@ -0,0 +1,31 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef IOS_CHROME_BROWSER_OVERLAYS_PUBLIC_OVERLAY_REQUEST_CANCEL_HANDLER_H_
+#define IOS_CHROME_BROWSER_OVERLAYS_PUBLIC_OVERLAY_REQUEST_CANCEL_HANDLER_H_
+
+#include <memory>
+
+class OverlayRequest;
+class OverlayRequestQueue;
+
+// Handles the cancellation of OverlayRequests added to an OverlayRequestQueue.
+class OverlayRequestCancelHandler {
+ public:
+  virtual ~OverlayRequestCancelHandler() = default;
+
+ protected:
+  // Constructor for a cancellation handler that cancels |request| from |queue|.
+  OverlayRequestCancelHandler(OverlayRequest* request,
+                              OverlayRequestQueue* queue);
+
+  // Called by subclasses to cancel the associated request.
+  void CancelRequest();
+
+ private:
+  OverlayRequest* request_ = nullptr;
+  OverlayRequestQueue* queue_ = nullptr;
+};
+
+#endif  // IOS_CHROME_BROWSER_OVERLAYS_PUBLIC_OVERLAY_REQUEST_CANCEL_HANDLER_H_
diff --git a/ios/chrome/browser/overlays/public/overlay_request_queue.h b/ios/chrome/browser/overlays/public/overlay_request_queue.h
index c239f44..60f01c8 100644
--- a/ios/chrome/browser/overlays/public/overlay_request_queue.h
+++ b/ios/chrome/browser/overlays/public/overlay_request_queue.h
@@ -6,6 +6,7 @@
 #define IOS_CHROME_BROWSER_OVERLAYS_PUBLIC_OVERLAY_REQUEST_QUEUE_H_
 
 #include "ios/chrome/browser/overlays/public/overlay_modality.h"
+#import "ios/chrome/browser/overlays/public/overlay_request_cancel_handler.h"
 
 class OverlayRequest;
 namespace web {
@@ -22,7 +23,14 @@
                                            OverlayModality modality);
 
   // Adds |request| to be displayed alongside the content area of queue's
-  // corresponding WebState.
+  // corresponding WebState.  The request may be cancelled by
+  // |cancel_handler|.
+  virtual void AddRequest(
+      std::unique_ptr<OverlayRequest> request,
+      std::unique_ptr<OverlayRequestCancelHandler> cancel_handler) = 0;
+
+  // Adds |request| to the queue using a default cancellation handler that
+  // cancels requests for committed, document-changing navigations.
   virtual void AddRequest(std::unique_ptr<OverlayRequest> request) = 0;
 
   // Returns the front request in the queue, or nullptr if the queue is empty.
@@ -37,7 +45,11 @@
   OverlayRequestQueue() = default;
 
  private:
+  friend class OverlayRequestCancelHandler;
   DISALLOW_COPY_AND_ASSIGN(OverlayRequestQueue);
+
+  // Called by cancellation handlers to cancel |request|.
+  virtual void CancelRequest(OverlayRequest* request) = 0;
 };
 
 #endif  // IOS_CHROME_BROWSER_OVERLAYS_PUBLIC_OVERLAY_REQUEST_QUEUE_H_
diff --git a/ios/chrome/browser/ui/autofill/manual_fill/fallback_coordinator_egtest.mm b/ios/chrome/browser/ui/autofill/manual_fill/fallback_coordinator_egtest.mm
index 93024921..db2e528 100644
--- a/ios/chrome/browser/ui/autofill/manual_fill/fallback_coordinator_egtest.mm
+++ b/ios/chrome/browser/ui/autofill/manual_fill/fallback_coordinator_egtest.mm
@@ -446,6 +446,11 @@
 // Tests that the manual fallback view concedes preference to the system picker
 // for selection elements.
 - (void)testPickerDismissesManualFallback {
+  if (@available(iOS 13, *)) {
+    // TODO(crbug.com/1007455): Enable this test.
+    EARL_GREY_TEST_DISABLED(@"The test is flaky on iOS 13");
+  }
+
   // Add the profile to be used.
   AddAutofillProfile(_personalDataManager);
 
@@ -708,7 +713,8 @@
 // Tests the mediator stops observing objects when the incognito BVC is
 // destroyed. Waiting for dealloc was causing a race condition with the
 // autorelease pool, and some times a DCHECK will be hit.
-- (void)testOpeningIncognitoTabsDoNotLeak {
+// TODO(crbug.com/1007421): This test is flaky on all configurations.
+- (void)FLAKY_testOpeningIncognitoTabsDoNotLeak {
   const GURL URL = self.testServer->GetURL(kFormHTMLFile);
   std::string webViewText("Profile form");
   AddAutofillProfile(_personalDataManager);
diff --git a/ios/chrome/browser/ui/overlays/web_content_area/app_launcher/BUILD.gn b/ios/chrome/browser/ui/overlays/web_content_area/app_launcher/BUILD.gn
index e4e6de1..5e2e724 100644
--- a/ios/chrome/browser/ui/overlays/web_content_area/app_launcher/BUILD.gn
+++ b/ios/chrome/browser/ui/overlays/web_content_area/app_launcher/BUILD.gn
@@ -20,9 +20,8 @@
     "//ios/chrome/browser/overlays",
     "//ios/chrome/browser/overlays/public/web_content_area",
     "//ios/chrome/browser/ui/alert_view_controller",
-    "//ios/chrome/browser/ui/elements",
     "//ios/chrome/browser/ui/overlays:coordinators",
-    "//ios/chrome/browser/ui/presenters",
+    "//ios/chrome/browser/ui/overlays/common/alerts",
     "//ui/base",
   ]
 }
@@ -47,7 +46,7 @@
     "//ios/chrome/browser/ui/alert_view_controller",
     "//ios/chrome/browser/ui/alert_view_controller/test",
     "//ios/chrome/browser/ui/dialogs",
-    "//ios/chrome/browser/ui/elements",
+    "//ios/chrome/browser/ui/overlays/common/alerts/test",
     "//ios/chrome/browser/ui/overlays/test",
     "//testing/gmock",
     "//testing/gtest",
diff --git a/ios/chrome/browser/ui/overlays/web_content_area/app_launcher/app_launcher_alert_overlay_coordinator.h b/ios/chrome/browser/ui/overlays/web_content_area/app_launcher/app_launcher_alert_overlay_coordinator.h
index 95e9785..5cc07a7 100644
--- a/ios/chrome/browser/ui/overlays/web_content_area/app_launcher/app_launcher_alert_overlay_coordinator.h
+++ b/ios/chrome/browser/ui/overlays/web_content_area/app_launcher/app_launcher_alert_overlay_coordinator.h
@@ -5,11 +5,11 @@
 #ifndef IOS_CHROME_BROWSER_UI_OVERLAYS_WEB_CONTENT_AREA_APP_LAUNCHER_APP_LAUNCHER_ALERT_OVERLAY_COORDINATOR_H_
 #define IOS_CHROME_BROWSER_UI_OVERLAYS_WEB_CONTENT_AREA_APP_LAUNCHER_APP_LAUNCHER_ALERT_OVERLAY_COORDINATOR_H_
 
-#import "ios/chrome/browser/ui/overlays/overlay_request_coordinator.h"
+#import "ios/chrome/browser/ui/overlays/common/alerts/alert_overlay_coordinator.h"
 
 // A coordinator that is used to display UI for HTTP authentication dialogs via
 // OverlayPresenter.
-@interface AppLauncherAlertOverlayCoordinator : OverlayRequestCoordinator
+@interface AppLauncherAlertOverlayCoordinator : AlertOverlayCoordinator
 @end
 
 #endif  // IOS_CHROME_BROWSER_UI_OVERLAYS_WEB_CONTENT_AREA_APP_LAUNCHER_APP_LAUNCHER_ALERT_OVERLAY_COORDINATOR_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 c67146f..40f9539f 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
@@ -6,97 +6,27 @@
 
 #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_request_coordinator_delegate.h"
+#import "ios/chrome/browser/ui/overlays/common/alerts/alert_overlay_coordinator+subclassing.h"
 #import "ios/chrome/browser/ui/overlays/web_content_area/app_launcher/app_launcher_alert_overlay_mediator.h"
-#import "ios/chrome/browser/ui/presenters/contained_presenter_delegate.h"
-#import "ios/chrome/browser/ui/presenters/non_modal_view_controller_presenter.h"
 
 #if !defined(__has_feature) || !__has_feature(objc_arc)
 #error "This file requires ARC support."
 #endif
 
-@interface AppLauncherAlertOverlayCoordinator () <
-    AppLauncherAlertOverlayMediatorDelegate,
-    ContainedPresenterDelegate>
-// Whether the coordinator has been started.
-@property(nonatomic, getter=isStarted) BOOL started;
-
-@property(nonatomic) AlertViewController* alertViewController;
-@property(nonatomic) NonModalViewControllerPresenter* presenter;
-@property(nonatomic) AppLauncherAlertOverlayMediator* mediator;
-@end
-
 @implementation AppLauncherAlertOverlayCoordinator
 
-#pragma mark - Accessors
-
-- (void)setMediator:(AppLauncherAlertOverlayMediator*)mediator {
-  if (_mediator == mediator)
-    return;
-  _mediator.delegate = nil;
-  _mediator = mediator;
-  _mediator.delegate = self;
-}
-
-#pragma mark - AppLauncherAlertOverlayMediatorDelegate
-
-- (void)stopDialogForMediator:(AppLauncherAlertOverlayMediator*)mediator {
-  DCHECK_EQ(self.mediator, mediator);
-  [self stopAnimated:YES];
-}
-
-#pragma mark - ContainedPresenterDelegate
-
-- (void)containedPresenterDidPresent:(id<ContainedPresenter>)presenter {
-  self.delegate->OverlayUIDidFinishPresentation(self.request);
-}
-
-- (void)containedPresenterDidDismiss:(id<ContainedPresenter>)presenter {
-  self.alertViewController = nil;
-  self.presenter = nil;
-  self.delegate->OverlayUIDidFinishDismissal(self.request);
-}
-
-#pragma mark - OverlayCoordinator
-
 + (BOOL)supportsRequest:(OverlayRequest*)request {
   return !!request->GetConfig<AppLauncherAlertOverlayRequestConfig>();
 }
 
-+ (BOOL)usesChildViewController {
-  return YES;
-}
+@end
 
-- (UIViewController*)viewController {
-  return self.alertViewController;
-}
+@implementation AppLauncherAlertOverlayCoordinator (Subclassing)
 
-- (void)startAnimated:(BOOL)animated {
-  if (self.started)
-    return;
-  self.alertViewController = [[AlertViewController alloc] init];
-  self.alertViewController.modalPresentationStyle =
-      UIModalPresentationOverCurrentContext;
-  self.alertViewController.modalTransitionStyle =
-      UIModalTransitionStyleCrossDissolve;
-  self.mediator =
-      [[AppLauncherAlertOverlayMediator alloc] initWithRequest:self.request];
-  self.mediator.consumer = self.alertViewController;
-  self.presenter = [[NonModalViewControllerPresenter alloc] init];
-  self.presenter.delegate = self;
-  self.presenter.baseViewController = self.baseViewController;
-  self.presenter.presentedViewController = self.alertViewController;
-  [self.presenter prepareForPresentation];
-  [self.presenter presentAnimated:animated];
-  self.started = YES;
-}
+#pragma mark - AlertOverlayCoordinator
 
-- (void)stopAnimated:(BOOL)animated {
-  if (!self.started)
-    return;
-  [self.presenter dismissAnimated:animated];
-  self.started = NO;
+- (AlertOverlayMediator*)newMediator {
+  return [[AppLauncherAlertOverlayMediator alloc] initWithRequest:self.request];
 }
 
 @end
diff --git a/ios/chrome/browser/ui/overlays/web_content_area/app_launcher/app_launcher_alert_overlay_mediator.h b/ios/chrome/browser/ui/overlays/web_content_area/app_launcher/app_launcher_alert_overlay_mediator.h
index cebd594..a3699f7f 100644
--- a/ios/chrome/browser/ui/overlays/web_content_area/app_launcher/app_launcher_alert_overlay_mediator.h
+++ b/ios/chrome/browser/ui/overlays/web_content_area/app_launcher/app_launcher_alert_overlay_mediator.h
@@ -5,38 +5,20 @@
 #ifndef IOS_CHROME_BROWSER_UI_OVERLAYS_WEB_CONTENT_AREA_APP_LAUNCHER_APP_LAUNCHER_ALERT_OVERLAY_MEDIATOR_H_
 #define IOS_CHROME_BROWSER_UI_OVERLAYS_WEB_CONTENT_AREA_APP_LAUNCHER_APP_LAUNCHER_ALERT_OVERLAY_MEDIATOR_H_
 
-#import <Foundation/Foundation.h>
+#import "ios/chrome/browser/ui/overlays/common/alerts/alert_overlay_mediator.h"
 
 class OverlayRequest;
-@protocol AppLauncherAlertOverlayMediatorDelegate;
-@protocol AppLauncherAlertOverlayMediatorDataSource;
-@protocol AlertConsumer;
 
 // Mediator object that uses a AppLauncherAlertOverlayRequestConfig to set up
 // the UI for an alert notifying the user that a navigation will open an
 // external app.
-@interface AppLauncherAlertOverlayMediator : NSObject
-
-// The consumer to be updated by this mediator.  Setting to a new value uses the
-// AppLauncherAlertOverlayRequestConfig to update the new consumer.
-@property(nonatomic, weak) id<AlertConsumer> consumer;
-
-// The delegate that handles action button functionality set up by the mediator.
-@property(nonatomic, weak) id<AppLauncherAlertOverlayMediatorDelegate> delegate;
+@interface AppLauncherAlertOverlayMediator : AlertOverlayMediator
 
 // Designated initializer for a mediator that uses |request|'s configuration to
 // set up an AlertConsumer.
-- (instancetype)initWithRequest:(OverlayRequest*)request;
-
-@end
-
-// Protocol used by the actions set up by the
-// AppLauncherAlertOverlayMediator.
-@protocol AppLauncherAlertOverlayMediatorDelegate <NSObject>
-
-// Called by |mediator| to dismiss the dialog overlay when
-// an action is tapped.
-- (void)stopDialogForMediator:(AppLauncherAlertOverlayMediator*)mediator;
+- (instancetype)initWithRequest:(OverlayRequest*)request
+    NS_DESIGNATED_INITIALIZER;
+- (instancetype)init NS_UNAVAILABLE;
 
 @end
 
diff --git a/ios/chrome/browser/ui/overlays/web_content_area/app_launcher/app_launcher_alert_overlay_mediator.mm b/ios/chrome/browser/ui/overlays/web_content_area/app_launcher/app_launcher_alert_overlay_mediator.mm
index e1048d1..5556b44 100644
--- a/ios/chrome/browser/ui/overlays/web_content_area/app_launcher/app_launcher_alert_overlay_mediator.mm
+++ b/ios/chrome/browser/ui/overlays/web_content_area/app_launcher/app_launcher_alert_overlay_mediator.mm
@@ -10,8 +10,7 @@
 #include "ios/chrome/browser/overlays/public/overlay_response.h"
 #include "ios/chrome/browser/overlays/public/web_content_area/app_launcher_alert_overlay.h"
 #import "ios/chrome/browser/ui/alert_view_controller/alert_action.h"
-#import "ios/chrome/browser/ui/alert_view_controller/alert_consumer.h"
-#import "ios/chrome/browser/ui/elements/text_field_configuration.h"
+#import "ios/chrome/browser/ui/overlays/common/alerts/alert_overlay_mediator+subclassing.h"
 #include "ios/chrome/grit/ios_strings.h"
 #include "ui/base/l10n/l10n_util.h"
 
@@ -22,6 +21,10 @@
 @interface AppLauncherAlertOverlayMediator ()
 @property(nonatomic, readonly) OverlayRequest* request;
 @property(nonatomic, readonly) AppLauncherAlertOverlayRequestConfig* config;
+
+// Sets the OverlayResponse. |allowAppLaunch| indicates whether the alert's
+// allow button was tapped to allow the navigation to open in another app.
+- (void)updateResponseAllowingAppLaunch:(BOOL)allowAppLaunch;
 @end
 
 @implementation AppLauncherAlertOverlayMediator
@@ -42,26 +45,32 @@
   return self.request->GetConfig<AppLauncherAlertOverlayRequestConfig>();
 }
 
-- (void)setConsumer:(id<AlertConsumer>)consumer {
-  if (self.consumer == consumer)
-    return;
-  _consumer = consumer;
+#pragma mark - Response helpers
 
-  NSString* message = nil;
-  NSString* allowActionTitle = nil;
+- (void)updateResponseAllowingAppLaunch:(BOOL)allowAppLaunch {
+  self.request->set_response(
+      OverlayResponse::CreateWithInfo<AppLauncherAlertOverlayResponseInfo>(
+          allowAppLaunch));
+}
+
+@end
+
+@implementation AppLauncherAlertOverlayMediator (Subclassing)
+
+- (NSString*)alertMessage {
+  return self.config->is_repeated_request()
+             ? l10n_util::GetNSString(IDS_IOS_OPEN_REPEATEDLY_ANOTHER_APP)
+             : l10n_util::GetNSString(IDS_IOS_OPEN_IN_ANOTHER_APP);
+}
+
+- (NSArray<AlertAction*>*)alertActions {
   NSString* rejectActionTitle = l10n_util::GetNSString(IDS_CANCEL);
-  if (self.config->is_repeated_request()) {
-    message = l10n_util::GetNSString(IDS_IOS_OPEN_REPEATEDLY_ANOTHER_APP);
-    allowActionTitle =
-        l10n_util::GetNSString(IDS_IOS_OPEN_REPEATEDLY_ANOTHER_APP_ALLOW);
-  } else {
-    message = l10n_util::GetNSString(IDS_IOS_OPEN_IN_ANOTHER_APP);
-    allowActionTitle =
-        l10n_util::GetNSString(IDS_IOS_APP_LAUNCHER_OPEN_APP_BUTTON_LABEL);
-  }
-  [self.consumer setMessage:message];
+  NSString* allowActionTitle =
+      self.config->is_repeated_request()
+          ? l10n_util::GetNSString(IDS_IOS_OPEN_REPEATEDLY_ANOTHER_APP_ALLOW)
+          : l10n_util::GetNSString(IDS_IOS_APP_LAUNCHER_OPEN_APP_BUTTON_LABEL);
   __weak __typeof__(self) weakSelf = self;
-  [self.consumer setActions:@[
+  return @[
     [AlertAction actionWithTitle:allowActionTitle
                            style:UIAlertActionStyleDefault
                          handler:^(AlertAction* action) {
@@ -78,17 +87,7 @@
                            [strongSelf.delegate
                                stopDialogForMediator:strongSelf];
                          }],
-  ]];
-}
-
-#pragma mark - Response helpers
-
-// Sets the OverlayResponse. |allowAppLaunch| indicates whether the alert's
-// allow button was tapped to allow the navigation to open in another app.
-- (void)updateResponseAllowingAppLaunch:(BOOL)allowAppLaunch {
-  self.request->set_response(
-      OverlayResponse::CreateWithInfo<AppLauncherAlertOverlayResponseInfo>(
-          allowAppLaunch));
+  ];
 }
 
 @end
diff --git a/ios/chrome/browser/ui/overlays/web_content_area/app_launcher/app_launcher_alert_overlay_mediator_unittest.mm b/ios/chrome/browser/ui/overlays/web_content_area/app_launcher/app_launcher_alert_overlay_mediator_unittest.mm
index 0340f22..12d9ce1 100644
--- a/ios/chrome/browser/ui/overlays/web_content_area/app_launcher/app_launcher_alert_overlay_mediator_unittest.mm
+++ b/ios/chrome/browser/ui/overlays/web_content_area/app_launcher/app_launcher_alert_overlay_mediator_unittest.mm
@@ -10,50 +10,41 @@
 #import "ios/chrome/browser/overlays/public/web_content_area/app_launcher_alert_overlay.h"
 #import "ios/chrome/browser/ui/alert_view_controller/alert_action.h"
 #import "ios/chrome/browser/ui/alert_view_controller/test/fake_alert_consumer.h"
+#import "ios/chrome/browser/ui/overlays/common/alerts/test/alert_overlay_mediator_test.h"
 #include "ios/chrome/grit/ios_strings.h"
 #include "testing/gtest_mac.h"
-#include "testing/platform_test.h"
 #include "ui/base/l10n/l10n_util.h"
 
 #if !defined(__has_feature) || !__has_feature(objc_arc)
 #error "This file requires ARC support."
 #endif
 
-class AppLauncherAlertOverlayMediatorTest : public PlatformTest {
+class AppLauncherAlertOverlayMediatorTest : public AlertOverlayMediatorTest {
  protected:
-  AppLauncherAlertOverlayMediatorTest()
-      : consumer_([[FakeAlertConsumer alloc] init]) {
-    UpdateConsumer();
-  }
-
-  FakeAlertConsumer* consumer() const { return consumer_; }
+  AppLauncherAlertOverlayMediatorTest() { UpdateMediator(); }
 
   // Setter for whether the test is for a repeated app launch request.
   void set_is_repeated_request(bool is_repeated_request) {
     if (is_repeated_request_ == is_repeated_request)
       return;
     is_repeated_request_ = is_repeated_request;
-    UpdateConsumer();
+    UpdateMediator();
   }
 
  private:
   // Instantiates |request_| with an OverlayRequest configured with an
   // AppLauncherAlertOverlayRequestConfig set up using |is_repeated_request_|.
-  // Resets |mediator_| to a new AppLauncherAlertOverlayMediator created with
-  // |request_| and sets its consumer to |consumer_|.
-  void UpdateConsumer() {
+  // Creates a new mediator using using |request_|.
+  void UpdateMediator() {
     request_ =
         OverlayRequest::CreateWithConfig<AppLauncherAlertOverlayRequestConfig>(
             is_repeated_request_);
-    mediator_ = [[AppLauncherAlertOverlayMediator alloc]
-        initWithRequest:request_.get()];
-    mediator_.consumer = consumer_;
+    SetMediator([[AppLauncherAlertOverlayMediator alloc]
+        initWithRequest:request_.get()]);
   }
 
-  FakeAlertConsumer* consumer_;
   bool is_repeated_request_ = false;
   std::unique_ptr<OverlayRequest> request_;
-  AppLauncherAlertOverlayMediator* mediator_ = nil;
 };
 
 // Tests that the consumer values are set correctly for the first app launch
diff --git a/ios/chrome/browser/ui/overlays/web_content_area/http_auth_dialogs/BUILD.gn b/ios/chrome/browser/ui/overlays/web_content_area/http_auth_dialogs/BUILD.gn
index d857701a..0415e2f 100644
--- a/ios/chrome/browser/ui/overlays/web_content_area/http_auth_dialogs/BUILD.gn
+++ b/ios/chrome/browser/ui/overlays/web_content_area/http_auth_dialogs/BUILD.gn
@@ -22,7 +22,7 @@
     "//ios/chrome/browser/ui/alert_view_controller",
     "//ios/chrome/browser/ui/elements",
     "//ios/chrome/browser/ui/overlays:coordinators",
-    "//ios/chrome/browser/ui/presenters",
+    "//ios/chrome/browser/ui/overlays/common/alerts",
     "//ui/base",
   ]
 }
@@ -48,6 +48,7 @@
     "//ios/chrome/browser/ui/alert_view_controller/test",
     "//ios/chrome/browser/ui/dialogs",
     "//ios/chrome/browser/ui/elements",
+    "//ios/chrome/browser/ui/overlays/common/alerts/test",
     "//ios/chrome/browser/ui/overlays/test",
     "//ios/chrome/browser/ui/overlays/web_content_area/java_script_dialogs/test",
     "//ios/web/public",
diff --git a/ios/chrome/browser/ui/overlays/web_content_area/http_auth_dialogs/http_auth_dialog_overlay_coordinator.h b/ios/chrome/browser/ui/overlays/web_content_area/http_auth_dialogs/http_auth_dialog_overlay_coordinator.h
index e56c2f01..391cb699 100644
--- a/ios/chrome/browser/ui/overlays/web_content_area/http_auth_dialogs/http_auth_dialog_overlay_coordinator.h
+++ b/ios/chrome/browser/ui/overlays/web_content_area/http_auth_dialogs/http_auth_dialog_overlay_coordinator.h
@@ -5,11 +5,11 @@
 #ifndef IOS_CHROME_BROWSER_UI_OVERLAYS_WEB_CONTENT_AREA_HTTP_AUTH_DIALOGS_HTTP_AUTH_DIALOG_OVERLAY_COORDINATOR_H_
 #define IOS_CHROME_BROWSER_UI_OVERLAYS_WEB_CONTENT_AREA_HTTP_AUTH_DIALOGS_HTTP_AUTH_DIALOG_OVERLAY_COORDINATOR_H_
 
-#import "ios/chrome/browser/ui/overlays/overlay_request_coordinator.h"
+#import "ios/chrome/browser/ui/overlays/common/alerts/alert_overlay_coordinator.h"
 
 // A coordinator that is used to display UI for HTTP authentication dialogs via
 // OverlayPresenter.
-@interface HTTPAuthDialogOverlayCoordinator : OverlayRequestCoordinator
+@interface HTTPAuthDialogOverlayCoordinator : AlertOverlayCoordinator
 @end
 
 #endif  // IOS_CHROME_BROWSER_UI_OVERLAYS_WEB_CONTENT_AREA_HTTP_AUTH_DIALOGS_HTTP_AUTH_DIALOG_OVERLAY_COORDINATOR_H_
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 a613ba2..eb460f9 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,113 +7,30 @@
 #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_request_coordinator_delegate.h"
+#import "ios/chrome/browser/ui/overlays/common/alerts/alert_overlay_coordinator+subclassing.h"
 #import "ios/chrome/browser/ui/overlays/web_content_area/http_auth_dialogs/http_auth_dialog_overlay_mediator.h"
-#import "ios/chrome/browser/ui/presenters/contained_presenter_delegate.h"
-#import "ios/chrome/browser/ui/presenters/non_modal_view_controller_presenter.h"
 
 #if !defined(__has_feature) || !__has_feature(objc_arc)
 #error "This file requires ARC support."
 #endif
 
-@interface HTTPAuthDialogOverlayCoordinator () <
-    ContainedPresenterDelegate,
-    HTTPAuthDialogOverlayMediatorDataSource,
-    HTTPAuthDialogOverlayMediatorDelegate>
-// Whether the coordinator has been started.
-@property(nonatomic, getter=isStarted) BOOL started;
-
-@property(nonatomic) AlertViewController* alertViewController;
-@property(nonatomic) NonModalViewControllerPresenter* presenter;
-@property(nonatomic) HTTPAuthDialogOverlayMediator* mediator;
+@interface HTTPAuthDialogOverlayCoordinator ()
 @end
 
 @implementation HTTPAuthDialogOverlayCoordinator
 
-#pragma mark - Accessors
-
-- (void)setMediator:(HTTPAuthDialogOverlayMediator*)mediator {
-  if (_mediator == mediator)
-    return;
-  _mediator.delegate = nil;
-  _mediator.dataSource = nil;
-  _mediator = mediator;
-  _mediator.dataSource = self;
-  _mediator.delegate = self;
-}
-
-#pragma mark - ContainedPresenterDelegate
-
-- (void)containedPresenterDidPresent:(id<ContainedPresenter>)presenter {
-  self.delegate->OverlayUIDidFinishPresentation(self.request);
-}
-
-- (void)containedPresenterDidDismiss:(id<ContainedPresenter>)presenter {
-  self.alertViewController = nil;
-  self.presenter = nil;
-  self.delegate->OverlayUIDidFinishDismissal(self.request);
-}
-
-#pragma mark - HTTPAuthDialogOverlayMediatorDataSource
-
-- (NSString*)userForMediator:(HTTPAuthDialogOverlayMediator*)mediator {
-  DCHECK(!self.alertViewController ||
-         self.alertViewController.textFieldResults.count == 2);
-  return self.alertViewController.textFieldResults[0];
-}
-
-- (NSString*)passwordForMediator:(HTTPAuthDialogOverlayMediator*)mediator {
-  DCHECK(!self.alertViewController ||
-         self.alertViewController.textFieldResults.count == 2);
-  return self.alertViewController.textFieldResults[1];
-}
-
-#pragma mark - HTTPAuthDialogOverlayMediatorDelegate
-
-- (void)stopDialogForMediator:(HTTPAuthDialogOverlayMediator*)mediator {
-  DCHECK_EQ(self.mediator, mediator);
-  [self stopAnimated:YES];
-}
-
 #pragma mark - OverlayCoordinator
 
 + (BOOL)supportsRequest:(OverlayRequest*)request {
   return !!request->GetConfig<HTTPAuthOverlayRequestConfig>();
 }
 
-+ (BOOL)usesChildViewController {
-  return YES;
-}
+@end
 
-- (UIViewController*)viewController {
-  return self.alertViewController;
-}
+@implementation HTTPAuthDialogOverlayCoordinator (Subclassing)
 
-- (void)startAnimated:(BOOL)animated {
-  if (self.started)
-    return;
-  self.alertViewController = [[AlertViewController alloc] init];
-  self.alertViewController.modalPresentationStyle =
-      UIModalPresentationOverCurrentContext;
-  self.alertViewController.modalTransitionStyle =
-      UIModalTransitionStyleCrossDissolve;
-  self.mediator =
-      [[HTTPAuthDialogOverlayMediator alloc] initWithRequest:self.request];
-  self.mediator.consumer = self.alertViewController;
-  self.presenter = [[NonModalViewControllerPresenter alloc] init];
-  self.presenter.delegate = self;
-  self.presenter.baseViewController = self.baseViewController;
-  self.presenter.presentedViewController = self.alertViewController;
-  [self.presenter prepareForPresentation];
-  [self.presenter presentAnimated:animated];
-  self.started = YES;
-}
-
-- (void)stopAnimated:(BOOL)animated {
-  if (!self.started)
-    return;
-  [self.presenter dismissAnimated:animated];
-  self.started = NO;
+- (AlertOverlayMediator*)newMediator {
+  return [[HTTPAuthDialogOverlayMediator alloc] initWithRequest:self.request];
 }
 
 @end
diff --git a/ios/chrome/browser/ui/overlays/web_content_area/http_auth_dialogs/http_auth_dialog_overlay_mediator.h b/ios/chrome/browser/ui/overlays/web_content_area/http_auth_dialogs/http_auth_dialog_overlay_mediator.h
index 26fbbf1b..a75dbf18 100644
--- a/ios/chrome/browser/ui/overlays/web_content_area/http_auth_dialogs/http_auth_dialog_overlay_mediator.h
+++ b/ios/chrome/browser/ui/overlays/web_content_area/http_auth_dialogs/http_auth_dialog_overlay_mediator.h
@@ -5,53 +5,20 @@
 #ifndef IOS_CHROME_BROWSER_UI_OVERLAYS_WEB_CONTENT_AREA_HTTP_AUTH_DIALOGS_HTTP_AUTH_DIALOG_OVERLAY_MEDIATOR_H_
 #define IOS_CHROME_BROWSER_UI_OVERLAYS_WEB_CONTENT_AREA_HTTP_AUTH_DIALOGS_HTTP_AUTH_DIALOG_OVERLAY_MEDIATOR_H_
 
-#import <Foundation/Foundation.h>
+#import "ios/chrome/browser/ui/overlays/common/alerts/alert_overlay_mediator.h"
 
 class OverlayRequest;
-class HTTPAuthOverlayRequestConfig;
-@protocol HTTPAuthDialogOverlayMediatorDelegate;
 @protocol HTTPAuthDialogOverlayMediatorDataSource;
-@protocol AlertConsumer;
 
 // Mediator object that uses a HTTPAuthOverlayRequestConfig to set up the UI for
 // an HTTP authentication dialog.
-@interface HTTPAuthDialogOverlayMediator : NSObject
-
-// The consumer to be updated by this mediator.  Setting to a new value uses the
-// HTTPAuthOverlayRequestConfig to update the new consumer.
-@property(nonatomic, weak) id<AlertConsumer> consumer;
-
-// The delegate that handles action button functionality set up by the mediator.
-@property(nonatomic, weak) id<HTTPAuthDialogOverlayMediatorDelegate> delegate;
-
-// The datasource for prompt input values.
-@property(nonatomic, weak) id<HTTPAuthDialogOverlayMediatorDataSource>
-    dataSource;
+@interface HTTPAuthDialogOverlayMediator : AlertOverlayMediator
 
 // Designated initializer for a mediator that uses |request|'s configuration to
 // set up an AlertConsumer.
-- (instancetype)initWithRequest:(OverlayRequest*)request;
-
-@end
-
-// Protocol used by the actions set up by the
-// HTTPAuthDialogOverlayMediator.
-@protocol HTTPAuthDialogOverlayMediatorDelegate <NSObject>
-
-// Called by |mediator| to dismiss the dialog overlay when
-// an action is tapped.
-- (void)stopDialogForMediator:(HTTPAuthDialogOverlayMediator*)mediator;
-
-@end
-
-// Protocol used to provide the text input from the HTTP authentication UI to
-// the mediator.
-@protocol HTTPAuthDialogOverlayMediatorDataSource <NSObject>
-
-// Fetches the current value of the username and password text fields from the
-// UI that was set up by |mediator|.
-- (NSString*)userForMediator:(HTTPAuthDialogOverlayMediator*)mediator;
-- (NSString*)passwordForMediator:(HTTPAuthDialogOverlayMediator*)mediator;
+- (instancetype)initWithRequest:(OverlayRequest*)request
+    NS_DESIGNATED_INITIALIZER;
+- (instancetype)init NS_UNAVAILABLE;
 
 @end
 
diff --git a/ios/chrome/browser/ui/overlays/web_content_area/http_auth_dialogs/http_auth_dialog_overlay_mediator.mm b/ios/chrome/browser/ui/overlays/web_content_area/http_auth_dialogs/http_auth_dialog_overlay_mediator.mm
index 7dd0091..32fe261 100644
--- a/ios/chrome/browser/ui/overlays/web_content_area/http_auth_dialogs/http_auth_dialog_overlay_mediator.mm
+++ b/ios/chrome/browser/ui/overlays/web_content_area/http_auth_dialogs/http_auth_dialog_overlay_mediator.mm
@@ -13,6 +13,7 @@
 #import "ios/chrome/browser/ui/alert_view_controller/alert_action.h"
 #import "ios/chrome/browser/ui/alert_view_controller/alert_consumer.h"
 #import "ios/chrome/browser/ui/elements/text_field_configuration.h"
+#import "ios/chrome/browser/ui/overlays/common/alerts/alert_overlay_mediator+subclassing.h"
 #include "ios/chrome/grit/ios_strings.h"
 #include "ui/base/l10n/l10n_util.h"
 
@@ -22,8 +23,11 @@
 
 @interface HTTPAuthDialogOverlayMediator ()
 @property(nonatomic, readonly) OverlayRequest* request;
-// The HTTP authentication config.
 @property(nonatomic, readonly) HTTPAuthOverlayRequestConfig* config;
+
+// Sets the OverlayResponse using the user input from the prompt UI.
+// |cancelled| indicates whether the alert's cancel button was tapped.
+- (void)updateResponseCancelled:(BOOL)cancelled;
 @end
 
 @implementation HTTPAuthDialogOverlayMediator
@@ -44,16 +48,59 @@
   return self.request->GetConfig<HTTPAuthOverlayRequestConfig>();
 }
 
-- (void)setConsumer:(id<AlertConsumer>)consumer {
-  if (self.consumer == consumer)
-    return;
-  _consumer = consumer;
-  [_consumer setTitle:l10n_util::GetNSStringWithFixup(IDS_LOGIN_DIALOG_TITLE)];
-  [self.consumer setMessage:base::SysUTF8ToNSString(self.config->message())];
+#pragma mark - Response helpers
+
+- (void)updateResponseCancelled:(BOOL)cancelled {
+  std::unique_ptr<OverlayResponse> response;
+  if (!cancelled) {
+    std::string user =
+        base::SysNSStringToUTF8([self.dataSource textFieldInputForMediator:self
+                                                            textFieldIndex:0]);
+    std::string password =
+        base::SysNSStringToUTF8([self.dataSource textFieldInputForMediator:self
+                                                            textFieldIndex:1]);
+    response = OverlayResponse::CreateWithInfo<HTTPAuthOverlayResponseInfo>(
+        user, password);
+  }
+  self.request->set_response(std::move(response));
+}
+
+@end
+
+@implementation HTTPAuthDialogOverlayMediator (Subclassing)
+
+- (NSString*)alertTitle {
+  return l10n_util::GetNSStringWithFixup(IDS_LOGIN_DIALOG_TITLE);
+}
+
+- (NSString*)alertMessage {
+  return base::SysUTF8ToNSString(self.config->message());
+}
+
+- (NSArray<TextFieldConfiguration*>*)alertTextFieldConfigurations {
+  NSString* defaultUsername =
+      base::SysUTF8ToNSString(self.config->default_username());
+  NSString* usernamePlaceholder =
+      l10n_util::GetNSString(IDS_IOS_HTTP_LOGIN_DIALOG_USERNAME_PLACEHOLDER);
+  NSString* passwordPlaceholder =
+      l10n_util::GetNSString(IDS_IOS_HTTP_LOGIN_DIALOG_PASSWORD_PLACEHOLDER);
+  return @[
+    [[TextFieldConfiguration alloc] initWithText:defaultUsername
+                                     placeholder:usernamePlaceholder
+                         accessibilityIdentifier:nil
+                                 secureTextEntry:NO],
+    [[TextFieldConfiguration alloc] initWithText:nil
+                                     placeholder:passwordPlaceholder
+                         accessibilityIdentifier:nil
+                                 secureTextEntry:YES]
+  ];
+}
+
+- (NSArray<AlertAction*>*)alertActions {
   __weak __typeof__(self) weakSelf = self;
   NSString* OKLabel =
       l10n_util::GetNSStringWithFixup(IDS_LOGIN_DIALOG_OK_BUTTON_LABEL);
-  [self.consumer setActions:@[
+  return @[
     [AlertAction actionWithTitle:OKLabel
                            style:UIAlertActionStyleDefault
                          handler:^(AlertAction* action) {
@@ -70,41 +117,7 @@
                            [strongSelf.delegate
                                stopDialogForMediator:strongSelf];
                          }],
-  ]];
-
-  NSString* defaultUsername =
-      base::SysUTF8ToNSString(self.config->default_username());
-  NSString* usernamePlaceholder =
-      l10n_util::GetNSString(IDS_IOS_HTTP_LOGIN_DIALOG_USERNAME_PLACEHOLDER);
-  NSString* passwordPlaceholder =
-      l10n_util::GetNSString(IDS_IOS_HTTP_LOGIN_DIALOG_PASSWORD_PLACEHOLDER);
-  [self.consumer setTextFieldConfigurations:@[
-    [[TextFieldConfiguration alloc] initWithText:defaultUsername
-                                     placeholder:usernamePlaceholder
-                         accessibilityIdentifier:nil
-                                 secureTextEntry:NO],
-    [[TextFieldConfiguration alloc] initWithText:nil
-                                     placeholder:passwordPlaceholder
-                         accessibilityIdentifier:nil
-                                 secureTextEntry:YES]
-  ]];
-}
-
-#pragma mark - Response helpers
-
-// Sets the OverlayResponse using the user input from the prompt UI.
-// |cancelled| indicates whether the alert's cancel button was tapped.
-- (void)updateResponseCancelled:(BOOL)cancelled {
-  std::unique_ptr<OverlayResponse> response;
-  if (!cancelled) {
-    std::string user =
-        base::SysNSStringToUTF8([self.dataSource userForMediator:self]);
-    std::string password =
-        base::SysNSStringToUTF8([self.dataSource passwordForMediator:self]);
-    response = OverlayResponse::CreateWithInfo<HTTPAuthOverlayResponseInfo>(
-        user, password);
-  }
-  self.request->set_response(std::move(response));
+  ];
 }
 
 @end
diff --git a/ios/chrome/browser/ui/overlays/web_content_area/http_auth_dialogs/http_auth_dialog_overlay_mediator_unittest.mm b/ios/chrome/browser/ui/overlays/web_content_area/http_auth_dialogs/http_auth_dialog_overlay_mediator_unittest.mm
index d369970..5b2ba67 100644
--- a/ios/chrome/browser/ui/overlays/web_content_area/http_auth_dialogs/http_auth_dialog_overlay_mediator_unittest.mm
+++ b/ios/chrome/browser/ui/overlays/web_content_area/http_auth_dialogs/http_auth_dialog_overlay_mediator_unittest.mm
@@ -14,6 +14,7 @@
 #import "ios/chrome/browser/ui/alert_view_controller/test/fake_alert_consumer.h"
 #import "ios/chrome/browser/ui/dialogs/java_script_dialog_blocking_state.h"
 #import "ios/chrome/browser/ui/elements/text_field_configuration.h"
+#import "ios/chrome/browser/ui/overlays/common/alerts/test/alert_overlay_mediator_test.h"
 #include "ios/chrome/grit/ios_strings.h"
 #include "testing/gtest_mac.h"
 #include "testing/platform_test.h"
@@ -23,49 +24,45 @@
 #error "This file requires ARC support."
 #endif
 
-class HTTPAuthDialogOverlayMediatorTest : public PlatformTest {
+class HTTPAuthDialogOverlayMediatorTest : public AlertOverlayMediatorTest {
  public:
   HTTPAuthDialogOverlayMediatorTest()
-      : consumer_([[FakeAlertConsumer alloc] init]),
-        message_("Message"),
+      : message_("Message"),
         default_user_text_("Default Text"),
         request_(OverlayRequest::CreateWithConfig<HTTPAuthOverlayRequestConfig>(
             message_,
-            default_user_text_)),
-        mediator_([[HTTPAuthDialogOverlayMediator alloc]
-            initWithRequest:request_.get()]) {
-    mediator_.consumer = consumer_;
+            default_user_text_)) {
+    SetMediator(
+        [[HTTPAuthDialogOverlayMediator alloc] initWithRequest:request_.get()]);
   }
 
  protected:
-  FakeAlertConsumer* consumer_ = nil;
   const std::string message_;
   const std::string default_user_text_;
   std::unique_ptr<OverlayRequest> request_;
-  HTTPAuthDialogOverlayMediator* mediator_ = nil;
 };
 
 // Tests that the consumer values are set correctly for confirmations.
 TEST_F(HTTPAuthDialogOverlayMediatorTest, AlertSetup) {
   // Verify the consumer values.
-  EXPECT_NSEQ(base::SysUTF8ToNSString(message_), consumer_.message);
-  EXPECT_EQ(2U, consumer_.textFieldConfigurations.count);
+  EXPECT_NSEQ(base::SysUTF8ToNSString(message_), consumer().message);
+  EXPECT_EQ(2U, consumer().textFieldConfigurations.count);
   EXPECT_NSEQ(base::SysUTF8ToNSString(default_user_text_),
-              consumer_.textFieldConfigurations[0].text);
+              consumer().textFieldConfigurations[0].text);
   NSString* user_placeholer =
       l10n_util::GetNSString(IDS_IOS_HTTP_LOGIN_DIALOG_USERNAME_PLACEHOLDER);
   EXPECT_NSEQ(user_placeholer,
-              consumer_.textFieldConfigurations[0].placeholder);
-  EXPECT_FALSE(!!consumer_.textFieldConfigurations[1].text);
+              consumer().textFieldConfigurations[0].placeholder);
+  EXPECT_FALSE(!!consumer().textFieldConfigurations[1].text);
   NSString* password_placeholder =
       l10n_util::GetNSString(IDS_IOS_HTTP_LOGIN_DIALOG_PASSWORD_PLACEHOLDER);
   EXPECT_NSEQ(password_placeholder,
-              consumer_.textFieldConfigurations[1].placeholder);
-  ASSERT_EQ(2U, consumer_.actions.count);
-  EXPECT_EQ(UIAlertActionStyleDefault, consumer_.actions[0].style);
+              consumer().textFieldConfigurations[1].placeholder);
+  ASSERT_EQ(2U, consumer().actions.count);
+  EXPECT_EQ(UIAlertActionStyleDefault, consumer().actions[0].style);
   NSString* sign_in_label =
       l10n_util::GetNSStringWithFixup(IDS_LOGIN_DIALOG_OK_BUTTON_LABEL);
-  EXPECT_NSEQ(sign_in_label, consumer_.actions[0].title);
-  EXPECT_EQ(UIAlertActionStyleCancel, consumer_.actions[1].style);
-  EXPECT_NSEQ(l10n_util::GetNSString(IDS_CANCEL), consumer_.actions[1].title);
+  EXPECT_NSEQ(sign_in_label, consumer().actions[0].title);
+  EXPECT_EQ(UIAlertActionStyleCancel, consumer().actions[1].style);
+  EXPECT_NSEQ(l10n_util::GetNSString(IDS_CANCEL), consumer().actions[1].title);
 }
diff --git a/ios/chrome/browser/ui/payments/payment_request_journey_logger_egtest.mm b/ios/chrome/browser/ui/payments/payment_request_journey_logger_egtest.mm
index 1657642..383c1900 100644
--- a/ios/chrome/browser/ui/payments/payment_request_journey_logger_egtest.mm
+++ b/ios/chrome/browser/ui/payments/payment_request_journey_logger_egtest.mm
@@ -66,6 +66,11 @@
 #define MAYBE_testSelectedPaymentMethod testSelectedPaymentMethod
 #endif
 - (void)MAYBE_testSelectedPaymentMethod {
+  if (@available(iOS 13, *)) {
+    // TODO(crbug.com/1007432): Enable this test.
+    EARL_GREY_TEST_DISABLED(@"The test is flaky on iOS 13");
+  }
+
   chrome_test_util::HistogramTester histogramTester;
 
   [self addProfiles];
@@ -279,6 +284,11 @@
   testAllSectionStats_NumberOfSuggestionsShown_Completed
 #endif
 - (void)MAYBE_testAllSectionStats_NumberOfSuggestionsShown_Completed {
+  if (@available(iOS 13, *)) {
+    // TODO(crbug.com/1007432): Enable this test.
+    EARL_GREY_TEST_DISABLED(@"The test is flaky on iOS 13");
+  }
+
   chrome_test_util::HistogramTester histogramTester;
 
   [self addProfiles];
@@ -432,6 +442,11 @@
   testNoShippingSectionStats_NumberOfSuggestionsShown_Completed
 #endif
 - (void)MAYBE_testNoShippingSectionStats_NumberOfSuggestionsShown_Completed {
+  if (@available(iOS 13, *)) {
+    // TODO(crbug.com/1007432): Enable this test.
+    EARL_GREY_TEST_DISABLED(@"The test is flaky on iOS 13");
+  }
+
   chrome_test_util::HistogramTester histogramTester;
 
   [self addProfiles];
@@ -588,6 +603,11 @@
 #endif
 - (void)
     MAYBE_testNoContactDetailSectionStats_NumberOfSuggestionsShown_Completed {
+  if (@available(iOS 13, *)) {
+    // TODO(crbug.com/1007432): Enable this test.
+    EARL_GREY_TEST_DISABLED(@"The test is flaky on iOS 13");
+  }
+
   chrome_test_util::HistogramTester histogramTester;
 
   [self addProfiles];
diff --git a/ios/chrome/browser/ui/payments/payment_request_use_stats_egtest.mm b/ios/chrome/browser/ui/payments/payment_request_use_stats_egtest.mm
index 8ecc092..4303c7d 100644
--- a/ios/chrome/browser/ui/payments/payment_request_use_stats_egtest.mm
+++ b/ios/chrome/browser/ui/payments/payment_request_use_stats_egtest.mm
@@ -90,6 +90,11 @@
 // properly updated upon completion. The use stats for the billing address
 // associated with the card is expected not to change.
 - (void)testRecordUseOfCard {
+  if (@available(iOS 13, *)) {
+    // TODO(crbug.com/1007432): Enable this test.
+    EARL_GREY_TEST_DISABLED(@"The test is flaky on iOS 13");
+  }
+
   autofill::TestAutofillClock testClock;
   testClock.SetNow(kSomeDate);
 
@@ -133,6 +138,11 @@
 // Tests that use stats for the shipping address used in a Payment Request are
 // properly updated upon completion.
 - (void)testRecordUseOfShippingAddress {
+  if (@available(iOS 13, *)) {
+    // TODO(crbug.com/1007432): Enable this test.
+    EARL_GREY_TEST_DISABLED(@"The test is flaky on iOS 13");
+  }
+
   autofill::TestAutofillClock testClock;
   testClock.SetNow(kSomeDate);
 
@@ -165,6 +175,11 @@
 // Tests that use stats for the contact address used in a Payment Request are
 // properly updated upon completion.
 - (void)testRecordUseOfContactAddress {
+  if (@available(iOS 13, *)) {
+    // TODO(crbug.com/1007432): Enable this test.
+    EARL_GREY_TEST_DISABLED(@"The test is flaky on iOS 13");
+  }
+
   autofill::TestAutofillClock testClock;
   testClock.SetNow(kSomeDate);
 
@@ -197,6 +212,11 @@
 // Tests that use stats for an address that was used both as a shipping and
 // contact address in a Payment Request are properly updated upon completion.
 - (void)testRecordUseOfContactAndShippingAddress {
+  if (@available(iOS 13, *)) {
+    // TODO(crbug.com/1007432): Enable this test.
+    EARL_GREY_TEST_DISABLED(@"The test is flaky on iOS 13");
+  }
+
   autofill::TestAutofillClock testClock;
   testClock.SetNow(kSomeDate);
 
diff --git a/ios/web_view/BUILD.gn b/ios/web_view/BUILD.gn
index af9779c..8c519a6 100644
--- a/ios/web_view/BUILD.gn
+++ b/ios/web_view/BUILD.gn
@@ -89,6 +89,7 @@
     "public/cwv_credit_card.h",
     "public/cwv_credit_card_saver.h",
     "public/cwv_credit_card_verifier.h",
+    "public/cwv_password.h",
     "public/cwv_preferences_autofill.h",
     "public/cwv_web_view_autofill.h",
     "public/cwv_web_view_configuration_autofill.h",
@@ -250,6 +251,8 @@
     "internal/autofill/cwv_credit_card_saver_internal.h",
     "internal/autofill/cwv_credit_card_verifier.mm",
     "internal/autofill/cwv_credit_card_verifier_internal.h",
+    "internal/passwords/cwv_password.mm",
+    "internal/passwords/cwv_password_internal.h",
     "internal/passwords/cwv_password_controller.h",
     "internal/passwords/cwv_password_controller.mm",
   ]
@@ -418,6 +421,7 @@
     "internal/cwv_scroll_view_unittest.mm",
     "internal/cwv_ssl_status_unittest.mm",
     "internal/cwv_web_view_configuration_unittest.mm",
+    "internal/passwords/cwv_password_unittest.mm",
     "internal/signin/cwv_identity_unittest.mm",
     "internal/sync/cwv_sync_controller_unittest.mm",
     "internal/translate/cwv_translation_controller_unittest.mm",
diff --git a/ios/web_view/internal/autofill/cwv_autofill_client_ios_bridge.h b/ios/web_view/internal/autofill/cwv_autofill_client_ios_bridge.h
index db1ba6a..63d92ea 100644
--- a/ios/web_view/internal/autofill/cwv_autofill_client_ios_bridge.h
+++ b/ios/web_view/internal/autofill/cwv_autofill_client_ios_bridge.h
@@ -17,6 +17,7 @@
 #include "components/autofill/core/browser/payments/legal_message_line.h"
 
 namespace autofill {
+class AutofillProfile;
 class CreditCard;
 class FormStructure;
 }  // namespace autofill
@@ -24,6 +25,10 @@
 // WebView extension of AutofillClientIOSBridge.
 @protocol CWVAutofillClientIOSBridge<AutofillClientIOSBridge>
 
+// Bridge for AutofillClient's method |ConfirmSaveAutofillProfile|.
+- (void)confirmSaveAutofillProfile:(const autofill::AutofillProfile&)profile
+                          callback:(base::OnceClosure)callback;
+
 // Bridge for AutofillClient's method |ConfirmSaveCreditCardLocally|.
 - (void)confirmSaveCreditCardLocally:(const autofill::CreditCard&)creditCard
                saveCreditCardOptions:
diff --git a/ios/web_view/internal/autofill/cwv_autofill_controller.mm b/ios/web_view/internal/autofill/cwv_autofill_controller.mm
index 52bc26a..fc0ef481 100644
--- a/ios/web_view/internal/autofill/cwv_autofill_controller.mm
+++ b/ios/web_view/internal/autofill/cwv_autofill_controller.mm
@@ -35,6 +35,7 @@
 #include "ios/web_view/internal/app/application_context.h"
 #import "ios/web_view/internal/autofill/cwv_autofill_client_ios_bridge.h"
 #import "ios/web_view/internal/autofill/cwv_autofill_form_internal.h"
+#import "ios/web_view/internal/autofill/cwv_autofill_profile_internal.h"
 #import "ios/web_view/internal/autofill/cwv_autofill_suggestion_internal.h"
 #import "ios/web_view/internal/autofill/cwv_credit_card_internal.h"
 #import "ios/web_view/internal/autofill/cwv_credit_card_saver_internal.h"
@@ -420,6 +421,26 @@
   [_autofillAgent hideAutofillPopup];
 }
 
+- (void)confirmSaveAutofillProfile:(const autofill::AutofillProfile&)profile
+                          callback:(base::OnceClosure)callback {
+  if (![_delegate respondsToSelector:@selector
+                  (autofillController:
+                      decideSavePolicyForAutofillProfile:decisionHandler:)]) {
+    return;
+  }
+
+  __block base::OnceClosure scopedCallback = std::move(callback);
+  CWVAutofillProfile* autofillProfile =
+      [[CWVAutofillProfile alloc] initWithProfile:profile];
+  [_delegate autofillController:self
+      decideSavePolicyForAutofillProfile:autofillProfile
+                         decisionHandler:^(BOOL save) {
+                           if (save) {
+                             std::move(scopedCallback).Run();
+                           }
+                         }];
+}
+
 - (void)confirmSaveCreditCardLocally:(const autofill::CreditCard&)creditCard
                saveCreditCardOptions:
                    (autofill::AutofillClient::SaveCreditCardOptions)
diff --git a/ios/web_view/internal/autofill/web_view_autofill_client_ios.mm b/ios/web_view/internal/autofill/web_view_autofill_client_ios.mm
index 4aa8e2d7..70f70ad 100644
--- a/ios/web_view/internal/autofill/web_view_autofill_client_ios.mm
+++ b/ios/web_view/internal/autofill/web_view_autofill_client_ios.mm
@@ -171,9 +171,7 @@
 void WebViewAutofillClientIOS::ConfirmSaveAutofillProfile(
     const AutofillProfile& profile,
     base::OnceClosure callback) {
-  // Since there is no confirmation needed to save an Autofill Profile,
-  // running |callback| will proceed with saving |profile|.
-  std::move(callback).Run();
+  [bridge_ confirmSaveAutofillProfile:profile callback:std::move(callback)];
 }
 
 void WebViewAutofillClientIOS::ConfirmSaveCreditCardLocally(
diff --git a/ios/web_view/internal/passwords/cwv_password.mm b/ios/web_view/internal/passwords/cwv_password.mm
new file mode 100644
index 0000000..1797120a
--- /dev/null
+++ b/ios/web_view/internal/passwords/cwv_password.mm
@@ -0,0 +1,69 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#import "ios/web_view/internal/passwords/cwv_password_internal.h"
+
+#import <objc/runtime.h>
+
+#include "base/strings/sys_string_conversions.h"
+#include "components/autofill/core/common/password_form.h"
+#include "components/password_manager/core/browser/password_ui_utils.h"
+#import "ios/web_view/internal/utils/nsobject_description_utils.h"
+
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
+@implementation CWVPassword {
+  autofill::PasswordForm _passwordForm;
+}
+
+- (instancetype)initWithPasswordForm:
+    (const autofill::PasswordForm&)passwordForm {
+  self = [super init];
+  if (self) {
+    _passwordForm = passwordForm;
+    auto name_and_link =
+        password_manager::GetShownOriginAndLinkUrl(_passwordForm);
+    _title = base::SysUTF8ToNSString(name_and_link.first);
+    _site = base::SysUTF8ToNSString(name_and_link.second.spec());
+  }
+  return self;
+}
+
+#pragma mark - Public
+
+- (NSString*)username {
+  if (self.blacklisted) {
+    return nil;
+  }
+  return base::SysUTF16ToNSString(_passwordForm.username_value);
+}
+
+- (NSString*)password {
+  if (self.blacklisted) {
+    return nil;
+  }
+  return base::SysUTF16ToNSString(_passwordForm.password_value);
+}
+
+- (BOOL)isBlacklisted {
+  return _passwordForm.blacklisted_by_user;
+}
+
+#pragma mark - NSObject
+
+- (NSString*)debugDescription {
+  NSString* debugDescription = [super debugDescription];
+  return [debugDescription
+      stringByAppendingFormat:@"\n%@", CWVPropertiesDescription(self)];
+}
+
+#pragma mark - Internal
+
+- (autofill::PasswordForm*)internalPasswordForm {
+  return &_passwordForm;
+}
+
+@end
diff --git a/ios/web_view/internal/passwords/cwv_password_internal.h b/ios/web_view/internal/passwords/cwv_password_internal.h
new file mode 100644
index 0000000..e2f4b416
--- /dev/null
+++ b/ios/web_view/internal/passwords/cwv_password_internal.h
@@ -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.
+
+#ifndef IOS_WEB_VIEW_INTERNAL_PASSWORDS_CWV_PASSWORD_INTERNAL_H_
+#define IOS_WEB_VIEW_INTERNAL_PASSWORDS_CWV_PASSWORD_INTERNAL_H_
+
+#include "components/autofill/core/common/password_form.h"
+#import "ios/web_view/public/cwv_password.h"
+
+@interface CWVPassword ()
+
+- (instancetype)initWithPasswordForm:(const autofill::PasswordForm&)passwordForm
+    NS_DESIGNATED_INITIALIZER;
+
+// The internal autofill credit card that is wrapped by this object.
+// Intentionally not declared as a property to avoid issues when read by
+// -[NSObject valueForKey:].
+- (autofill::PasswordForm*)internalPasswordForm;
+
+@end
+
+#endif  // IOS_WEB_VIEW_INTERNAL_PASSWORDS_CWV_PASSWORD_INTERNAL_H_
diff --git a/ios/web_view/internal/passwords/cwv_password_unittest.mm b/ios/web_view/internal/passwords/cwv_password_unittest.mm
new file mode 100644
index 0000000..e8d74fc
--- /dev/null
+++ b/ios/web_view/internal/passwords/cwv_password_unittest.mm
@@ -0,0 +1,80 @@
+// 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/web_view/internal/passwords/cwv_password_internal.h"
+
+#include "base/strings/sys_string_conversions.h"
+#include "components/autofill/core/common/password_form.h"
+#include "components/password_manager/core/browser/password_ui_utils.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#import "testing/gtest_mac.h"
+#include "testing/platform_test.h"
+
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
+namespace ios_web_view {
+
+using CWVPasswordTest = PlatformTest;
+
+// Tests CWVPassword initialization for a blacklisted site.
+TEST_F(CWVPasswordTest, Blacklisted) {
+  autofill::PasswordForm password_form;
+  password_form.origin = GURL("http://www.example.com/accounts/LoginAuth");
+  password_form.action = GURL("http://www.example.com/accounts/Login");
+  password_form.username_element = base::SysNSStringToUTF16(@"Email");
+  password_form.username_value = base::SysNSStringToUTF16(@"test@egmail.com");
+  password_form.password_element = base::SysNSStringToUTF16(@"Passwd");
+  password_form.password_value = base::SysNSStringToUTF16(@"test");
+  password_form.submit_element = base::SysNSStringToUTF16(@"signIn");
+  password_form.signon_realm = "http://www.example.com/";
+  password_form.preferred = false;
+  password_form.scheme = autofill::PasswordForm::Scheme::kHtml;
+  password_form.blacklisted_by_user = true;
+  auto name_and_link =
+      password_manager::GetShownOriginAndLinkUrl(password_form);
+
+  CWVPassword* password =
+      [[CWVPassword alloc] initWithPasswordForm:password_form];
+
+  EXPECT_EQ(password_form, *[password internalPasswordForm]);
+  EXPECT_NSEQ(base::SysUTF8ToNSString(name_and_link.first), password.title);
+  EXPECT_NSEQ(base::SysUTF8ToNSString(name_and_link.second.spec()),
+              password.site);
+  EXPECT_TRUE(password.blacklisted);
+  EXPECT_FALSE(password.username);
+  EXPECT_FALSE(password.password);
+}
+
+// Tests CWVPassword initialization for a non-blacklisted site.
+TEST_F(CWVPasswordTest, NonBlacklisted) {
+  autofill::PasswordForm password_form;
+  password_form.origin = GURL("http://www.example.com/accounts/LoginAuth");
+  password_form.action = GURL("http://www.example.com/accounts/Login");
+  password_form.username_element = base::SysNSStringToUTF16(@"Email");
+  password_form.username_value = base::SysNSStringToUTF16(@"test@egmail.com");
+  password_form.password_element = base::SysNSStringToUTF16(@"Passwd");
+  password_form.password_value = base::SysNSStringToUTF16(@"test");
+  password_form.submit_element = base::SysNSStringToUTF16(@"signIn");
+  password_form.signon_realm = "http://www.example.com/";
+  password_form.preferred = false;
+  password_form.scheme = autofill::PasswordForm::Scheme::kHtml;
+  password_form.blacklisted_by_user = false;
+  auto name_and_link =
+      password_manager::GetShownOriginAndLinkUrl(password_form);
+
+  CWVPassword* password =
+      [[CWVPassword alloc] initWithPasswordForm:password_form];
+
+  EXPECT_EQ(password_form, *[password internalPasswordForm]);
+  EXPECT_NSEQ(base::SysUTF8ToNSString(name_and_link.first), password.title);
+  EXPECT_NSEQ(base::SysUTF8ToNSString(name_and_link.second.spec()),
+              password.site);
+  EXPECT_FALSE(password.blacklisted);
+  EXPECT_NSEQ(@"test@egmail.com", password.username);
+  EXPECT_NSEQ(@"test", password.password);
+}
+
+}  // namespace ios_web_view
diff --git a/ios/web_view/internal/signin/web_view_identity_manager_factory.mm b/ios/web_view/internal/signin/web_view_identity_manager_factory.mm
index 385cf1e0..4f210a5 100644
--- a/ios/web_view/internal/signin/web_view_identity_manager_factory.mm
+++ b/ios/web_view/internal/signin/web_view_identity_manager_factory.mm
@@ -65,6 +65,7 @@
   // ChromeWebView's signin state.
   PrefService* pref_service = browser_state->GetPrefs();
   pref_service->ClearPref(prefs::kGoogleServicesAccountId);
+  pref_service->ClearPref(prefs::kGoogleServicesConsentedToSync);
 
   IOSWebViewSigninClient* client =
       WebViewSigninClientFactory::GetForBrowserState(browser_state);
diff --git a/ios/web_view/public/cwv_autofill_controller_delegate.h b/ios/web_view/public/cwv_autofill_controller_delegate.h
index 5b3052a3..5b41efa6 100644
--- a/ios/web_view/public/cwv_autofill_controller_delegate.h
+++ b/ios/web_view/public/cwv_autofill_controller_delegate.h
@@ -11,6 +11,7 @@
 
 @class CWVAutofillController;
 @class CWVAutofillFormSuggestion;
+@class CWVAutofillProfile;
 @class CWVCreditCard;
 @class CWVCreditCardSaver;
 @class CWVCreditCardVerifier;
@@ -76,6 +77,14 @@
 - (void)autofillControllerDidInsertFormElements:
     (CWVAutofillController*)autofillController;
 
+// Called when it is possible to save a new autofill profile used in filling
+// address forms. This is usually invoked after successfully submitting an
+// address form with a new profile. The profile will only be saved if this
+// method is implemented and |decisionHandler| is called with |YES|.
+- (void)autofillController:(CWVAutofillController*)autofillController
+    decideSavePolicyForAutofillProfile:(CWVAutofillProfile*)autofillProfile
+                       decisionHandler:(void (^)(BOOL save))decisionHandler;
+
 // Called when it is possible to save a new credit card. This is usually called
 // after a new card was entered in a form and submitted.
 // |saver| encapsulates information needed to assist with this save attempt.
@@ -113,6 +122,7 @@
                             decisionHandler:
                                 (void (^)(CWVPasswordUserDecision decision))
                                     decisionHandler;
+
 @end
 
 NS_ASSUME_NONNULL_END
diff --git a/ios/web_view/public/cwv_password.h b/ios/web_view/public/cwv_password.h
new file mode 100644
index 0000000..c082199
--- /dev/null
+++ b/ios/web_view/public/cwv_password.h
@@ -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.
+
+#ifndef IOS_WEB_VIEW_PUBLIC_CWV_PASSWORD_H_
+#define IOS_WEB_VIEW_PUBLIC_CWV_PASSWORD_H_
+
+#import <Foundation/Foundation.h>
+
+#import "cwv_export.h"
+
+NS_ASSUME_NONNULL_BEGIN
+
+// Represents a password for autofilling login forms.
+CWV_EXPORT
+@interface CWVPassword : NSObject
+
+// Display friendly title for this object.
+@property(nonatomic, copy, readonly) NSString* title;
+// The url for which this password can be used in its login form.
+@property(nonatomic, copy, readonly) NSString* site;
+
+// Whether or not |site| has been blacklisted by the user. This means password
+// autofill will never occur.
+@property(nonatomic, readonly, getter=isBlacklisted) BOOL blacklisted;
+
+// The login username. This is nil if |blacklisted| or if only the |password|
+// was saved.
+@property(nonatomic, nullable, copy, readonly) NSString* username;
+// The login password. This is nil iff |blacklisted|.
+// Note that you should only display this after the user authenticates via iOS'
+// native authentication mechanism. e.g. Passcode and/or Touch/Face ID.
+@property(nonatomic, nullable, copy, readonly) NSString* password;
+
+- (instancetype)init NS_UNAVAILABLE;
+
+@end
+
+NS_ASSUME_NONNULL_END
+
+#endif  // IOS_WEB_VIEW_PUBLIC_CWV_PASSWORD_H_
diff --git a/ios/web_view/test/web_view_autofill_inttest.mm b/ios/web_view/test/web_view_autofill_inttest.mm
index 30d1887..e5eb3b8 100644
--- a/ios/web_view/test/web_view_autofill_inttest.mm
+++ b/ios/web_view/test/web_view_autofill_inttest.mm
@@ -64,7 +64,20 @@
 // Tests autofill features in CWVWebViews.
 class WebViewAutofillTest : public WebViewInttestBase {
  protected:
-  WebViewAutofillTest() : autofill_controller_(web_view_.autofillController) {}
+  WebViewAutofillTest() : autofill_controller_(web_view_.autofillController) {
+    // Ensure CWVAutofillProfiles are saved by default.
+    id delegate = OCMProtocolMock(@protocol(CWVAutofillControllerDelegate));
+    autofill_controller_.delegate = delegate;
+    [[delegate stub] autofillController:autofill_controller_
+        decideSavePolicyForAutofillProfile:[OCMArg any]
+                           decisionHandler:[OCMArg
+                                               checkWithBlock:^BOOL(id param) {
+                                                 void (^decisionHandler)(BOOL) =
+                                                     param;
+                                                 decisionHandler(YES);
+                                                 return YES;
+                                               }]];
+  }
 
   bool LoadTestPage() WARN_UNUSED_RESULT {
     std::string html = base::SysNSStringToUTF8(kTestFormHtml);
diff --git a/media/cdm/library_cdm/clear_key_cdm/cdm_video_decoder.cc b/media/cdm/library_cdm/clear_key_cdm/cdm_video_decoder.cc
index def538bc..7e62ba0 100644
--- a/media/cdm/library_cdm/clear_key_cdm/cdm_video_decoder.cc
+++ b/media/cdm/library_cdm/clear_key_cdm/cdm_video_decoder.cc
@@ -12,14 +12,14 @@
 #include "base/containers/queue.h"
 #include "base/macros.h"
 #include "base/memory/weak_ptr.h"
-#include "base/message_loop/message_loop.h"
-#include "base/message_loop/message_loop_current.h"
 #include "base/no_destructor.h"
 #include "base/optional.h"
 // Necessary to convert async media::VideoDecoder to sync CdmVideoDecoder.
 // Typically not recommended for production code, but is ok here since
 // ClearKeyCdm is only for testing.
 #include "base/run_loop.h"
+#include "base/task/single_thread_task_executor.h"
+#include "base/threading/thread_task_runner_handle.h"
 #include "media/base/decode_status.h"
 #include "media/base/media_util.h"
 #include "media/cdm/cdm_type_conversion.h"
@@ -141,9 +141,10 @@
 // the CDM and the host is depending on the same base/ target. In static build,
 // they will not be available and we have to setup it by ourselves.
 void SetupGlobalEnvironmentIfNeeded() {
-  // Creating a base::MessageLoop to setup base::ThreadTaskRunnerHandle.
-  if (!base::MessageLoopCurrent::IsSet()) {
-    static base::NoDestructor<base::MessageLoop> message_loop;
+  // Creating a base::SingleThreadTaskExecutor to setup
+  // base::ThreadTaskRunnerHandle.
+  if (!base::ThreadTaskRunnerHandle::IsSet()) {
+    static base::NoDestructor<base::SingleThreadTaskExecutor> task_executor;
   }
 
   if (!base::CommandLine::InitializedForCurrentProcess())
diff --git a/mojo/core/data_pipe_unittest.cc b/mojo/core/data_pipe_unittest.cc
index 8367fdf..f8210b77 100644
--- a/mojo/core/data_pipe_unittest.cc
+++ b/mojo/core/data_pipe_unittest.cc
@@ -1758,8 +1758,8 @@
     // Send some data before serialising and sending the data pipe over.
     // This is the first write so we don't need to use WriteAllData.
     uint32_t num_bytes = kTestDataSize;
-    ASSERT_EQ(MOJO_RESULT_OK, WriteData(kMultiprocessTestData, &num_bytes,
-                                        MOJO_WRITE_DATA_FLAG_ALL_OR_NONE));
+    ASSERT_EQ(MOJO_RESULT_OK,
+              WriteData(kMultiprocessTestData, &num_bytes, true));
     ASSERT_EQ(kTestDataSize, num_bytes);
 
     // Send child process the data pipe.
diff --git a/mojo/public/cpp/bindings/lib/connector.cc b/mojo/public/cpp/bindings/lib/connector.cc
index d8bea67..a92580b 100644
--- a/mojo/public/cpp/bindings/lib/connector.cc
+++ b/mojo/public/cpp/bindings/lib/connector.cc
@@ -7,6 +7,7 @@
 #include <stdint.h>
 
 #include "base/bind.h"
+#include "base/debug/dump_without_crashing.h"
 #include "base/location.h"
 #include "base/logging.h"
 #include "base/macros.h"
@@ -63,6 +64,11 @@
     100  // Use a 100 message quote by default.
 };
 
+const base::FeatureParam<int> kMojoRecordUnreadMessageCountCrashThreshold = {
+    &features::kMojoRecordUnreadMessageCount, "CrashThreshold",
+    0  // Set to zero to disable crash dumps by default.
+};
+
 int UnreadMessageCountQuota() {
   static const bool enabled =
       base::FeatureList::IsEnabled(features::kMojoRecordUnreadMessageCount);
@@ -77,6 +83,26 @@
   return quota;
 }
 
+void MaybeDumpWithoutCrashing(int quota_used) {
+  static const int crash_theshold =
+      kMojoRecordUnreadMessageCountCrashThreshold.Get();
+  if (crash_theshold == 0)
+    return;
+
+  static bool have_crashed = false;
+  if (have_crashed)
+    return;
+
+  // Only crash once per process/per run. Note that this is slightly racy
+  // against concurrent quota overruns on multiple threads, but that's fine.
+  have_crashed = true;
+
+  // This is happening because the user of the interface implicated on the crash
+  // stack has queued up an unreasonable number of messages, namely
+  // |quota_used|.
+  base::debug::DumpWithoutCrashing();
+}
+
 }  // namespace
 
 // Used to efficiently maintain a doubly-linked list of all Connectors
@@ -368,8 +394,10 @@
     MojoResult rv = MojoQueryQuota(message_pipe_.get().value(),
                                    MOJO_QUOTA_TYPE_UNREAD_MESSAGE_COUNT,
                                    nullptr, &limit, &usage);
-    if (rv == MOJO_RESULT_OK && usage > max_unread_message_quota_used_)
+    if (rv == MOJO_RESULT_OK && usage > max_unread_message_quota_used_) {
+      MaybeDumpWithoutCrashing(usage);
       max_unread_message_quota_used_ = usage;
+    }
   }
 
   MojoResult rv =
diff --git a/net/BUILD.gn b/net/BUILD.gn
index 1cfbf2596..93c3b8b 100644
--- a/net/BUILD.gn
+++ b/net/BUILD.gn
@@ -1060,6 +1060,8 @@
       "quic/quic_crypto_client_stream_factory.cc",
       "quic/quic_crypto_client_stream_factory.h",
       "quic/quic_flags_list.h",
+      "quic/quic_http3_logger.cc",
+      "quic/quic_http3_logger.h",
       "quic/quic_http_stream.cc",
       "quic/quic_http_stream.h",
       "quic/quic_http_utils.cc",
diff --git a/net/base/mime_sniffer_perftest.cc b/net/base/mime_sniffer_perftest.cc
index 8637964a..fc35efdd 100644
--- a/net/base/mime_sniffer_perftest.cc
+++ b/net/base/mime_sniffer_perftest.cc
@@ -10,6 +10,7 @@
 #include "base/logging.h"
 #include "base/timer/elapsed_timer.h"
 #include "testing/gtest/include/gtest/gtest.h"
+#include "testing/perf/perf_result_reporter.h"
 
 namespace net {
 namespace {
@@ -94,9 +95,12 @@
   RunLooksLikeBinary(plaintext, kWarmupIterations);
   base::ElapsedTimer elapsed_timer;
   RunLooksLikeBinary(plaintext, kMeasuredIterations);
-  LOG(INFO) << (elapsed_timer.Elapsed().InMicroseconds() * 1000 * 1024 /
-                (static_cast<int64_t>(plaintext.size()) * kMeasuredIterations))
-            << "ns per KB";
+  perf_test::PerfResultReporter reporter("MimeSniffer.", "PlainText");
+  reporter.RegisterImportantMetric("throughput",
+                                   "bytesPerSecond_biggerIsBetter");
+  reporter.AddResult("throughput", static_cast<int64_t>(plaintext.size()) *
+                                       kMeasuredIterations /
+                                       elapsed_timer.Elapsed().InSecondsF());
 }
 
 }  // namespace
diff --git a/net/cookies/cookie_monster_perftest.cc b/net/cookies/cookie_monster_perftest.cc
index 6880c75..8ac5ec1 100644
--- a/net/cookies/cookie_monster_perftest.cc
+++ b/net/cookies/cookie_monster_perftest.cc
@@ -11,15 +11,16 @@
 #include "base/run_loop.h"
 #include "base/strings/string_util.h"
 #include "base/strings/stringprintf.h"
-#include "base/test/perf_time_logger.h"
 #include "base/test/task_environment.h"
 #include "base/time/time.h"
+#include "base/timer/elapsed_timer.h"
 #include "net/cookies/canonical_cookie.h"
 #include "net/cookies/cookie_monster.h"
 #include "net/cookies/cookie_monster_store_test.h"
 #include "net/cookies/cookie_util.h"
 #include "net/cookies/parsed_cookie.h"
 #include "testing/gtest/include/gtest/gtest.h"
+#include "testing/perf/perf_result_reporter.h"
 #include "url/gurl.h"
 
 namespace net {
@@ -30,6 +31,36 @@
 const char kCookieLine[] = "A  = \"b=;\\\"\"  ;secure;;;";
 const char kGoogleURL[] = "http://www.foo.com";
 
+static constexpr char kMetricPrefixParsedCookie[] = "ParsedCookie.";
+static constexpr char kMetricPrefixCookieMonster[] = "CookieMonster.";
+static constexpr char kMetricParseTimeMs[] = "parse_time";
+static constexpr char kMetricAddTimeMs[] = "add_time";
+static constexpr char kMetricQueryTimeMs[] = "query_time";
+static constexpr char kMetricDeleteAllTimeMs[] = "delete_all_time";
+static constexpr char kMetricQueryDomainTimeMs[] = "query_domain_time";
+static constexpr char kMetricImportTimeMs[] = "import_time";
+static constexpr char kMetricGetKeyTimeMs[] = "get_key_time";
+static constexpr char kMetricGCTimeMs[] = "gc_time";
+
+perf_test::PerfResultReporter SetUpParseReporter(const std::string& story) {
+  perf_test::PerfResultReporter reporter(kMetricPrefixParsedCookie, story);
+  reporter.RegisterImportantMetric(kMetricParseTimeMs, "ms");
+  return reporter;
+}
+
+perf_test::PerfResultReporter SetUpCookieMonsterReporter(
+    const std::string& story) {
+  perf_test::PerfResultReporter reporter(kMetricPrefixCookieMonster, story);
+  reporter.RegisterImportantMetric(kMetricAddTimeMs, "ms");
+  reporter.RegisterImportantMetric(kMetricQueryTimeMs, "ms");
+  reporter.RegisterImportantMetric(kMetricDeleteAllTimeMs, "ms");
+  reporter.RegisterImportantMetric(kMetricQueryDomainTimeMs, "ms");
+  reporter.RegisterImportantMetric(kMetricImportTimeMs, "ms");
+  reporter.RegisterImportantMetric(kMetricGetKeyTimeMs, "ms");
+  reporter.RegisterImportantMetric(kMetricGCTimeMs, "ms");
+  return reporter;
+}
+
 class CookieMonsterTest : public testing::Test {
  public:
   CookieMonsterTest() {}
@@ -122,23 +153,25 @@
 
 TEST(ParsedCookieTest, TestParseCookies) {
   std::string cookie(kCookieLine);
-  base::PerfTimeLogger timer("Parsed_cookie_parse_cookies");
+  auto reporter = SetUpParseReporter("parse_cookies");
+  base::ElapsedTimer timer;
   for (int i = 0; i < kNumCookies; ++i) {
     ParsedCookie pc(cookie);
     EXPECT_TRUE(pc.IsValid());
   }
-  timer.Done();
+  reporter.AddResult(kMetricParseTimeMs, timer.Elapsed().InMillisecondsF());
 }
 
 TEST(ParsedCookieTest, TestParseBigCookies) {
   std::string cookie(3800, 'z');
   cookie += kCookieLine;
-  base::PerfTimeLogger timer("Parsed_cookie_parse_big_cookies");
+  auto reporter = SetUpParseReporter("parse_big_cookies");
+  base::ElapsedTimer timer;
   for (int i = 0; i < kNumCookies; ++i) {
     ParsedCookie pc(cookie);
     EXPECT_TRUE(pc.IsValid());
   }
-  timer.Done();
+  reporter.AddResult(kMetricParseTimeMs, timer.Elapsed().InMillisecondsF());
 }
 
 TEST_F(CookieMonsterTest, TestAddCookiesOnSingleHost) {
@@ -151,27 +184,30 @@
   SetCookieCallback setCookieCallback;
 
   // Add a bunch of cookies on a single host
-  base::PerfTimeLogger timer("Cookie_monster_add_single_host");
+  auto reporter = SetUpCookieMonsterReporter("single_host");
+  base::ElapsedTimer add_timer;
 
   for (std::vector<std::string>::const_iterator it = cookies.begin();
        it != cookies.end(); ++it) {
     setCookieCallback.SetCookie(cm.get(), GURL(kGoogleURL), *it);
   }
-  timer.Done();
+  reporter.AddResult(kMetricAddTimeMs, add_timer.Elapsed().InMillisecondsF());
 
   GetCookieListCallback getCookieListCallback;
 
-  base::PerfTimeLogger timer2("Cookie_monster_query_single_host");
+  base::ElapsedTimer query_timer;
   for (std::vector<std::string>::const_iterator it = cookies.begin();
        it != cookies.end(); ++it) {
     getCookieListCallback.GetCookieList(cm.get(), GURL(kGoogleURL));
   }
-  timer2.Done();
+  reporter.AddResult(kMetricQueryTimeMs,
+                     query_timer.Elapsed().InMillisecondsF());
 
-  base::PerfTimeLogger timer3("Cookie_monster_deleteall_single_host");
+  base::ElapsedTimer delete_all_timer;
   cm->DeleteAllAsync(CookieMonster::DeleteCallback());
   base::RunLoop().RunUntilIdle();
-  timer3.Done();
+  reporter.AddResult(kMetricDeleteAllTimeMs,
+                     delete_all_timer.Elapsed().InMillisecondsF());
 }
 
 TEST_F(CookieMonsterTest, TestAddCookieOnManyHosts) {
@@ -185,26 +221,29 @@
   SetCookieCallback setCookieCallback;
 
   // Add a cookie on a bunch of host
-  base::PerfTimeLogger timer("Cookie_monster_add_many_hosts");
+  auto reporter = SetUpCookieMonsterReporter("many_hosts");
+  base::ElapsedTimer add_timer;
   for (std::vector<GURL>::const_iterator it = gurls.begin(); it != gurls.end();
        ++it) {
     setCookieCallback.SetCookie(cm.get(), *it, cookie);
   }
-  timer.Done();
+  reporter.AddResult(kMetricAddTimeMs, add_timer.Elapsed().InMillisecondsF());
 
   GetCookieListCallback getCookieListCallback;
 
-  base::PerfTimeLogger timer2("Cookie_monster_query_many_hosts");
+  base::ElapsedTimer query_timer;
   for (std::vector<GURL>::const_iterator it = gurls.begin(); it != gurls.end();
        ++it) {
     getCookieListCallback.GetCookieList(cm.get(), *it);
   }
-  timer2.Done();
+  reporter.AddResult(kMetricQueryTimeMs,
+                     query_timer.Elapsed().InMillisecondsF());
 
-  base::PerfTimeLogger timer3("Cookie_monster_deleteall_many_hosts");
+  base::ElapsedTimer delete_all_timer;
   cm->DeleteAllAsync(CookieMonster::DeleteCallback());
   base::RunLoop().RunUntilIdle();
-  timer3.Done();
+  reporter.AddResult(kMetricDeleteAllTimeMs,
+                     delete_all_timer.Elapsed().InMillisecondsF());
 }
 
 TEST_F(CookieMonsterTest, TestDomainTree) {
@@ -256,11 +295,13 @@
       getCookieListCallback.GetCookieList(cm.get(), probe_gurl);
   EXPECT_EQ(5u, cookie_list.size())
       << CanonicalCookie::BuildCookieLine(cookie_list);
-  base::PerfTimeLogger timer("Cookie_monster_query_domain_tree");
+  auto reporter = SetUpCookieMonsterReporter("tree");
+  base::ElapsedTimer query_domain_timer;
   for (int i = 0; i < kNumCookies; i++) {
     getCookieListCallback.GetCookieList(cm.get(), probe_gurl);
   }
-  timer.Done();
+  reporter.AddResult(kMetricQueryDomainTimeMs,
+                     query_domain_timer.Elapsed().InMillisecondsF());
 }
 
 TEST_F(CookieMonsterTest, TestDomainLine) {
@@ -296,11 +337,13 @@
   const CookieList& cookie_list =
       getCookieListCallback.GetCookieList(cm.get(), probe_gurl);
   EXPECT_EQ(32u, cookie_list.size());
-  base::PerfTimeLogger timer2("Cookie_monster_query_domain_line");
+  auto reporter = SetUpCookieMonsterReporter("line");
+  base::ElapsedTimer query_domain_timer;
   for (int i = 0; i < kNumCookies; i++) {
     getCookieListCallback.GetCookieList(cm.get(), probe_gurl);
   }
-  timer2.Done();
+  reporter.AddResult(kMetricQueryDomainTimeMs,
+                     query_domain_timer.Elapsed().InMillisecondsF());
 }
 
 TEST_F(CookieMonsterTest, TestImport) {
@@ -330,9 +373,11 @@
   // Import will happen on first access.
   GURL gurl("www.foo.com");
   CookieOptions options;
-  base::PerfTimeLogger timer("Cookie_monster_import_from_store");
+  auto reporter = SetUpCookieMonsterReporter("from_store");
+  base::ElapsedTimer import_timer;
   getCookieListCallback.GetCookieList(cm.get(), gurl);
-  timer.Done();
+  reporter.AddResult(kMetricImportTimeMs,
+                     import_timer.Elapsed().InMillisecondsF());
 
   // Just confirm keys were set as expected.
   EXPECT_EQ("domain_1.com", cm->GetKey("www.Domain_1.com"));
@@ -340,10 +385,12 @@
 
 TEST_F(CookieMonsterTest, TestGetKey) {
   std::unique_ptr<CookieMonster> cm(new CookieMonster(nullptr, nullptr));
-  base::PerfTimeLogger timer("Cookie_monster_get_key");
+  auto reporter = SetUpCookieMonsterReporter("baseline_story");
+  base::ElapsedTimer get_key_timer;
   for (int i = 0; i < kNumCookies; i++)
     cm->GetKey("www.foo.com");
-  timer.Done();
+  reporter.AddResult(kMetricGetKeyTimeMs,
+                     get_key_timer.Elapsed().InMillisecondsF());
 }
 
 // This test is probing for whether garbage collection happens when it
@@ -403,10 +450,11 @@
     // Trigger the Garbage collection we're allowed.
     setCookieCallback.SetCookie(cm.get(), gurl, cookie_line);
 
-    base::PerfTimeLogger timer((std::string("GC_") + test_case.name).c_str());
+    auto reporter = SetUpCookieMonsterReporter(test_case.name);
+    base::ElapsedTimer gc_timer;
     for (int i = 0; i < kNumCookies; i++)
       setCookieCallback.SetCookie(cm.get(), gurl, cookie_line);
-    timer.Done();
+    reporter.AddResult(kMetricGCTimeMs, gc_timer.Elapsed().InMillisecondsF());
   }
 }
 
diff --git a/net/disk_cache/disk_cache_perftest.cc b/net/disk_cache/disk_cache_perftest.cc
index 56fef9b..43583a3 100644
--- a/net/disk_cache/disk_cache_perftest.cc
+++ b/net/disk_cache/disk_cache_perftest.cc
@@ -16,9 +16,9 @@
 #include "base/run_loop.h"
 #include "base/strings/string_number_conversions.h"
 #include "base/strings/string_util.h"
-#include "base/test/perf_time_logger.h"
 #include "base/test/test_file_util.h"
 #include "base/threading/thread.h"
+#include "base/timer/elapsed_timer.h"
 #include "build/build_config.h"
 #include "net/base/cache_type.h"
 #include "net/base/completion_repeating_callback.h"
@@ -35,6 +35,7 @@
 #include "net/disk_cache/simple/simple_index.h"
 #include "net/disk_cache/simple/simple_index_file.h"
 #include "testing/gtest/include/gtest/gtest.h"
+#include "testing/perf/perf_result_reporter.h"
 #include "testing/platform_test.h"
 
 using base::Time;
@@ -52,6 +53,50 @@
 // As of 2017-01-12, this is a typical per-tab limit on HTTP connections.
 const int kMaxParallelOperations = 10;
 
+static constexpr char kMetricPrefixDiskCache[] = "DiskCache.";
+static constexpr char kMetricPrefixSimpleIndex[] = "SimpleIndex.";
+static constexpr char kMetricCacheEntriesWriteTimeMs[] =
+    "cache_entries_write_time";
+static constexpr char kMetricCacheHeadersReadTimeColdMs[] =
+    "cache_headers_read_time_cold";
+static constexpr char kMetricCacheHeadersReadTimeWarmMs[] =
+    "cache_headers_read_time_warm";
+static constexpr char kMetricCacheEntriesReadTimeColdMs[] =
+    "cache_entries_read_time_cold";
+static constexpr char kMetricCacheEntriesReadTimeWarmMs[] =
+    "cache_entries_read_time_warm";
+static constexpr char kMetricCacheKeysHashTimeMs[] = "cache_keys_hash_time";
+static constexpr char kMetricFillBlocksTimeMs[] = "fill_sequential_blocks_time";
+static constexpr char kMetricCreateDeleteBlocksTimeMs[] =
+    "create_and_delete_random_blocks_time";
+static constexpr char kMetricSimpleCacheInitTotalTimeMs[] =
+    "simple_cache_initial_read_total_time";
+static constexpr char kMetricSimpleCacheInitPerEntryTimeUs[] =
+    "simple_cache_initial_read_per_entry_time";
+static constexpr char kMetricAverageEvictionTimeMs[] = "average_eviction_time";
+
+perf_test::PerfResultReporter SetUpDiskCacheReporter(const std::string& story) {
+  perf_test::PerfResultReporter reporter(kMetricPrefixDiskCache, story);
+  reporter.RegisterImportantMetric(kMetricCacheEntriesWriteTimeMs, "ms");
+  reporter.RegisterImportantMetric(kMetricCacheHeadersReadTimeColdMs, "ms");
+  reporter.RegisterImportantMetric(kMetricCacheHeadersReadTimeWarmMs, "ms");
+  reporter.RegisterImportantMetric(kMetricCacheEntriesReadTimeColdMs, "ms");
+  reporter.RegisterImportantMetric(kMetricCacheEntriesReadTimeWarmMs, "ms");
+  reporter.RegisterImportantMetric(kMetricCacheKeysHashTimeMs, "ms");
+  reporter.RegisterImportantMetric(kMetricFillBlocksTimeMs, "ms");
+  reporter.RegisterImportantMetric(kMetricCreateDeleteBlocksTimeMs, "ms");
+  reporter.RegisterImportantMetric(kMetricSimpleCacheInitTotalTimeMs, "ms");
+  reporter.RegisterImportantMetric(kMetricSimpleCacheInitPerEntryTimeUs, "us");
+  return reporter;
+}
+
+perf_test::PerfResultReporter SetUpSimpleIndexReporter(
+    const std::string& story) {
+  perf_test::PerfResultReporter reporter(kMetricPrefixSimpleIndex, story);
+  reporter.RegisterImportantMetric(kMetricAverageEvictionTimeMs, "ms");
+  return reporter;
+}
+
 void MaybeIncreaseFdLimitTo(unsigned int max_descriptors) {
 #if defined(OS_POSIX)
   base::IncreaseFdLimitTo(max_descriptors);
@@ -76,8 +121,10 @@
 
  protected:
   // Helper methods for constructing tests.
-  bool TimeWrites();
-  bool TimeReads(WhatToRead what_to_read, const char* timer_message);
+  bool TimeWrites(const std::string& story);
+  bool TimeReads(WhatToRead what_to_read,
+                 const std::string& metric,
+                 const std::string& story);
   void ResetAndEvictSystemDiskCache();
 
   // Callbacks used within tests for intermediate operations.
@@ -90,7 +137,7 @@
                      int result);
 
   // Complete perf tests.
-  void CacheBackendPerformance();
+  void CacheBackendPerformance(const std::string& story);
 
   const size_t kFdLimitForCacheTests = 8192;
 
@@ -350,7 +397,7 @@
   return false;
 }
 
-bool DiskCachePerfTest::TimeWrites() {
+bool DiskCachePerfTest::TimeWrites(const std::string& story) {
   for (size_t i = 0; i < kNumEntries; i++) {
     TestEntry entry;
     entry.key = GenerateKey(true);
@@ -360,30 +407,40 @@
 
   net::TestCompletionCallback cb;
 
-  base::PerfTimeLogger timer("Write disk cache entries");
+  auto reporter = SetUpDiskCacheReporter(story);
+  base::ElapsedTimer write_timer;
 
   WriteHandler write_handler(this, cache_.get(), cb.callback());
   write_handler.Run();
-  return cb.WaitForResult() == net::OK;
+  auto result = cb.WaitForResult();
+  reporter.AddResult(kMetricCacheEntriesWriteTimeMs,
+                     write_timer.Elapsed().InMillisecondsF());
+  return result == net::OK;
 }
 
 bool DiskCachePerfTest::TimeReads(WhatToRead what_to_read,
-                                  const char* timer_message) {
-  base::PerfTimeLogger timer(timer_message);
+                                  const std::string& metric,
+                                  const std::string& story) {
+  auto reporter = SetUpDiskCacheReporter(story);
+  base::ElapsedTimer timer;
 
   net::TestCompletionCallback cb;
   ReadHandler read_handler(this, what_to_read, cache_.get(), cb.callback());
   read_handler.Run();
-  return cb.WaitForResult() == net::OK;
+  auto result = cb.WaitForResult();
+  reporter.AddResult(metric, timer.Elapsed().InMillisecondsF());
+  return result == net::OK;
 }
 
 TEST_F(DiskCachePerfTest, BlockfileHashes) {
-  base::PerfTimeLogger timer("Hash disk cache keys");
+  auto reporter = SetUpDiskCacheReporter("baseline_story");
+  base::ElapsedTimer timer;
   for (int i = 0; i < 300000; i++) {
     std::string key = GenerateKey(true);
     base::Hash(key);
   }
-  timer.Done();
+  reporter.AddResult(kMetricCacheKeysHashTimeMs,
+                     timer.Elapsed().InMillisecondsF());
 }
 
 void DiskCachePerfTest::ResetAndEvictSystemDiskCache() {
@@ -412,41 +469,41 @@
   InitCache();
 }
 
-void DiskCachePerfTest::CacheBackendPerformance() {
+void DiskCachePerfTest::CacheBackendPerformance(const std::string& story) {
   LOG(ERROR) << "Using cache at:" << cache_path_.MaybeAsASCII();
   SetMaxSize(500 * 1024 * 1024);
   InitCache();
-  EXPECT_TRUE(TimeWrites());
+  EXPECT_TRUE(TimeWrites(story));
 
   disk_cache::SimpleBackendImpl::FlushWorkerPoolForTesting();
   base::RunLoop().RunUntilIdle();
 
   ResetAndEvictSystemDiskCache();
   EXPECT_TRUE(TimeReads(WhatToRead::HEADERS_ONLY,
-                        "Read disk cache headers only (cold)"));
+                        kMetricCacheHeadersReadTimeColdMs, story));
   EXPECT_TRUE(TimeReads(WhatToRead::HEADERS_ONLY,
-                        "Read disk cache headers only (warm)"));
+                        kMetricCacheHeadersReadTimeWarmMs, story));
 
   disk_cache::SimpleBackendImpl::FlushWorkerPoolForTesting();
   base::RunLoop().RunUntilIdle();
 
   ResetAndEvictSystemDiskCache();
   EXPECT_TRUE(TimeReads(WhatToRead::HEADERS_AND_BODY,
-                        "Read disk cache entries (cold)"));
+                        kMetricCacheEntriesReadTimeColdMs, story));
   EXPECT_TRUE(TimeReads(WhatToRead::HEADERS_AND_BODY,
-                        "Read disk cache entries (warm)"));
+                        kMetricCacheEntriesReadTimeWarmMs, story));
 
   disk_cache::SimpleBackendImpl::FlushWorkerPoolForTesting();
   base::RunLoop().RunUntilIdle();
 }
 
 TEST_F(DiskCachePerfTest, CacheBackendPerformance) {
-  CacheBackendPerformance();
+  CacheBackendPerformance("blockfile_cache");
 }
 
 TEST_F(DiskCachePerfTest, SimpleCacheBackendPerformance) {
   SetSimpleCacheMode();
-  CacheBackendPerformance();
+  CacheBackendPerformance("simple_cache");
 }
 
 // Creating and deleting "entries" on a block-file is something quite frequent
@@ -463,7 +520,8 @@
   const int kNumBlocks = 60000;
   disk_cache::Addr address[kNumBlocks];
 
-  base::PerfTimeLogger timer1("Fill three block-files");
+  auto reporter = SetUpDiskCacheReporter("blockfile_cache");
+  base::ElapsedTimer sequential_timer;
 
   // Fill up the 32-byte block file (use three files).
   for (int i = 0; i < kNumBlocks; i++) {
@@ -472,8 +530,9 @@
         files.CreateBlock(disk_cache::RANKINGS, block_size, &address[i]));
   }
 
-  timer1.Done();
-  base::PerfTimeLogger timer2("Create and delete blocks");
+  reporter.AddResult(kMetricFillBlocksTimeMs,
+                     sequential_timer.Elapsed().InMillisecondsF());
+  base::ElapsedTimer random_timer;
 
   for (int i = 0; i < 200000; i++) {
     int block_size = base::RandInt(1, 4);
@@ -484,7 +543,8 @@
         files.CreateBlock(disk_cache::RANKINGS, block_size, &address[entry]));
   }
 
-  timer2.Done();
+  reporter.AddResult(kMetricCreateDeleteBlocksTimeMs,
+                     random_timer.Elapsed().InMillisecondsF());
   base::RunLoop().RunUntilIdle();
 }
 
@@ -567,12 +627,14 @@
 
   disk_cache::SimpleBackendImpl::FlushWorkerPoolForTesting();
   base::RunLoop().RunUntilIdle();
-  LOG(ERROR) << "Early portion:" << elapsed_early << " ms";
-  LOG(ERROR) << "\tPer entry:"
-             << 1000 * (elapsed_early / (kIterations * kBatchSize)) << " us";
-  LOG(ERROR) << "Event loop portion: " << elapsed_late << " ms";
-  LOG(ERROR) << "\tPer entry:"
-             << 1000 * (elapsed_late / (kIterations * kBatchSize)) << " us";
+  auto reporter = SetUpDiskCacheReporter("early_portion");
+  reporter.AddResult(kMetricSimpleCacheInitTotalTimeMs, elapsed_early);
+  reporter.AddResult(kMetricSimpleCacheInitPerEntryTimeUs,
+                     1000 * (elapsed_early / (kIterations * kBatchSize)));
+  reporter = SetUpDiskCacheReporter("event_loop_portion");
+  reporter.AddResult(kMetricSimpleCacheInitTotalTimeMs, elapsed_late);
+  reporter.AddResult(kMetricSimpleCacheInitPerEntryTimeUs,
+                     1000 * (elapsed_late / (kIterations * kBatchSize)));
 }
 
 // Measures how quickly SimpleIndex can compute which entries to evict.
@@ -612,8 +674,9 @@
     evict_elapsed_ms += timer.Elapsed().InMillisecondsF();
   }
 
-  LOG(ERROR) << "Average time to evict:" << (evict_elapsed_ms / iterations)
-             << "ms";
+  auto reporter = SetUpSimpleIndexReporter("baseline_story");
+  reporter.AddResult(kMetricAverageEvictionTimeMs,
+                     evict_elapsed_ms / iterations);
 }
 
 }  // namespace
diff --git a/net/extras/sqlite/sqlite_persistent_cookie_store_perftest.cc b/net/extras/sqlite/sqlite_persistent_cookie_store_perftest.cc
index 46850fbc..2181dd8 100644
--- a/net/extras/sqlite/sqlite_persistent_cookie_store_perftest.cc
+++ b/net/extras/sqlite/sqlite_persistent_cookie_store_perftest.cc
@@ -14,15 +14,15 @@
 #include "base/strings/stringprintf.h"
 #include "base/synchronization/waitable_event.h"
 #include "base/task/post_task.h"
-#include "base/test/perf_time_logger.h"
 #include "base/test/task_environment.h"
+#include "base/timer/elapsed_timer.h"
 #include "net/base/test_completion_callback.h"
 #include "net/cookies/canonical_cookie.h"
 #include "net/cookies/cookie_constants.h"
 #include "net/extras/sqlite/cookie_crypto_delegate.h"
 #include "net/log/net_log_with_source.h"
 #include "testing/gtest/include/gtest/gtest.h"
-#include "testing/perf/perf_test.h"
+#include "testing/perf/perf_result_reporter.h"
 #include "url/gurl.h"
 
 namespace net {
@@ -43,6 +43,15 @@
 static_assert(kRandomSeed > 10 * kCookiesPerDomain,
               "kRandomSeed not high enough for number of cookies per domain");
 
+static constexpr char kMetricPrefixSQLPCS[] = "SQLitePersistentCookieStore.";
+static constexpr char kMetricOperationDurationMs[] = "operation_duration";
+
+perf_test::PerfResultReporter SetUpSQLPCSReporter(const std::string& story) {
+  perf_test::PerfResultReporter reporter(kMetricPrefixSQLPCS, story);
+  reporter.RegisterImportantMetric(kMetricOperationDurationMs, "ms");
+  return reporter;
+}
+
 }  // namespace
 
 class SQLitePersistentCookieStorePerfTest : public testing::Test {
@@ -127,15 +136,12 @@
     perf_measurement_start_ = base::Time::Now();
   }
 
-  void EndPerfMeasurement() {
+  void EndPerfMeasurement(const std::string& story) {
     DCHECK(!perf_measurement_start_.is_null());
     base::TimeDelta elapsed = base::Time::Now() - perf_measurement_start_;
     perf_measurement_start_ = base::Time();
-    const ::testing::TestInfo* test_info =
-        ::testing::UnitTest::GetInstance()->current_test_info();
-    perf_test::PrintResult(
-        test_info->test_case_name(), std::string(".") + test_info->name(),
-        "time", static_cast<double>(elapsed.InMilliseconds()), "ms", true);
+    auto reporter = SetUpSQLPCSReporter(story);
+    reporter.AddResult(kMetricOperationDurationMs, elapsed.InMillisecondsF());
   }
 
  protected:
@@ -165,7 +171,7 @@
         base::BindOnce(&SQLitePersistentCookieStorePerfTest::OnKeyLoaded,
                        base::Unretained(this)));
     key_loaded_event_.Wait();
-    EndPerfMeasurement();
+    EndPerfMeasurement("load_for_key");
 
     ASSERT_EQ(50U, cookies_.size());
   }
@@ -175,7 +181,7 @@
 TEST_F(SQLitePersistentCookieStorePerfTest, TestLoadPerformance) {
   StartPerfMeasurement();
   Load();
-  EndPerfMeasurement();
+  EndPerfMeasurement("load");
 
   ASSERT_EQ(kNumDomains * kCookiesPerDomain, static_cast<int>(cookies_.size()));
 }
@@ -214,7 +220,7 @@
     store_->Flush(test_closure.closure());
     test_closure.WaitForResult();
   }
-  EndPerfMeasurement();
+  EndPerfMeasurement("delete");
 }
 
 // Test update performance.
@@ -241,7 +247,7 @@
     store_->Flush(test_closure.closure());
     test_closure.WaitForResult();
   }
-  EndPerfMeasurement();
+  EndPerfMeasurement("update");
 }
 
 }  // namespace net
diff --git a/net/log/net_log.cc b/net/log/net_log.cc
index 9fbe4e6..7afade5 100644
--- a/net/log/net_log.cc
+++ b/net/log/net_log.cc
@@ -56,11 +56,6 @@
   return base::subtle::NoBarrier_AtomicIncrement(&last_id_, 1);
 }
 
-bool NetLog::IsCapturing() const {
-  CheckAlive();
-  return GetObserverCaptureModes() != 0;
-}
-
 void NetLog::AddObserver(NetLog::ThreadSafeObserver* observer,
                          NetLogCaptureMode capture_mode) {
   base::AutoLock lock(lock_);
@@ -203,10 +198,6 @@
   }
 }
 
-NetLogCaptureModeSet NetLog::GetObserverCaptureModes() const {
-  return base::subtle::NoBarrier_Load(&observer_capture_modes_);
-}
-
 void NetLog::AddEntryWithMaterializedParams(NetLogEventType type,
                                             const NetLogSource& source,
                                             NetLogEventPhase phase,
diff --git a/net/log/net_log.h b/net/log/net_log.h
index dd04c0ec..fe2b4cfb7 100644
--- a/net/log/net_log.h
+++ b/net/log/net_log.h
@@ -228,7 +228,10 @@
   //
   // TODO(eroman): Survey current callsites; most are probably not necessary,
   // and may even be harmful.
-  bool IsCapturing() const;
+  bool IsCapturing() const {
+    CheckAlive();
+    return GetObserverCaptureModes() != 0;
+  }
 
   // Adds an observer and sets its log capture mode.  The observer must not be
   // watching any NetLog, including this one, when this is called.
@@ -295,7 +298,9 @@
                         const GetParamsInterface* get_params);
 
   // Returns the set of all capture modes being observed.
-  NetLogCaptureModeSet GetObserverCaptureModes() const;
+  NetLogCaptureModeSet GetObserverCaptureModes() const {
+    return base::subtle::NoBarrier_Load(&observer_capture_modes_);
+  }
 
   // Adds an entry using already materialized parameters, when it is already
   // known that the log is capturing (goes straight to acquiring observer lock).
diff --git a/net/log/net_log_event_type_list.h b/net/log/net_log_event_type_list.h
index c856f371..da791ad 100644
--- a/net/log/net_log_event_type_list.h
+++ b/net/log/net_log_event_type_list.h
@@ -3234,3 +3234,18 @@
 //  {
 //  }
 EVENT_TYPE(COOKIE_SET_BLOCKED_BY_NETWORK_DELEGATE)
+
+//
+// HTTP/3 events.
+//
+// Event emitted when peer created control stream type is received.
+EVENT_TYPE(HTTP3_PEER_CONTROL_STREAM_CREATED)
+
+// Event emitted when peer created QPACK encoder stream type is received.
+EVENT_TYPE(HTTP3_PEER_QPACK_ENCODER_STREAM_CREATED)
+
+// Event emitted when peer created QPACK decoder stream type is received.
+EVENT_TYPE(HTTP3_PEER_QPACK_DECODER_STREAM_CREATED)
+
+// Event emitted when SETTINGS frame is received.
+EVENT_TYPE(HTTP3_SETTINGS_RECEIVED)
diff --git a/net/log/net_log_source_type_list.h b/net/log/net_log_source_type_list.h
index e36b547..2abd3ace 100644
--- a/net/log/net_log_source_type_list.h
+++ b/net/log/net_log_source_type_list.h
@@ -43,3 +43,4 @@
 SOURCE_TYPE(TRIAL_CERT_VERIFIER_JOB)
 SOURCE_TYPE(COOKIE_STORE)
 SOURCE_TYPE(HTTP_AUTH_CONTROLLER)
+SOURCE_TYPE(HTTP3_SESSION)
diff --git a/net/log/net_log_with_source.cc b/net/log/net_log_with_source.cc
index 5b2742a..3b36b5f 100644
--- a/net/log/net_log_with_source.cc
+++ b/net/log/net_log_with_source.cc
@@ -33,13 +33,25 @@
 
 }  // namespace
 
+NetLogWithSource::NetLogWithSource() {
+  // Conceptually, default NetLogWithSource have no NetLog*, and will return
+  // nullptr when calling |net_log()|. However for performance reasons, we
+  // always store a non-null member to the NetLog in order to avoid needing
+  // null checks for critical codepaths.
+  //
+  // The "dummy" net log used here will always return false for IsCapturing(),
+  // and have no sideffects should its method be called. In practice the only
+  // method that will get called on it is IsCapturing().
+  static NetLog* dummy = new NetLog();
+  DCHECK(!dummy->IsCapturing());
+  non_null_net_log_ = dummy;
+}
+
 NetLogWithSource::~NetLogWithSource() {}
 
 void NetLogWithSource::AddEntry(NetLogEventType type,
                                 NetLogEventPhase phase) const {
-  if (!net_log_)
-    return;
-  net_log_->AddEntry(type, source_, phase);
+  non_null_net_log_->AddEntry(type, source_, phase);
 }
 
 void NetLogWithSource::AddEvent(NetLogEventType type) const {
@@ -138,10 +150,6 @@
   });
 }
 
-bool NetLogWithSource::IsCapturing() const {
-  return net_log_ && net_log_->IsCapturing();
-}
-
 // static
 NetLogWithSource NetLogWithSource::Make(NetLog* net_log,
                                         NetLogSourceType source_type) {
@@ -152,4 +160,10 @@
   return NetLogWithSource(source, net_log);
 }
 
+NetLog* NetLogWithSource::net_log() const {
+  if (source_.IsValid())
+    return non_null_net_log_;
+  return nullptr;
+}
+
 }  // namespace net
diff --git a/net/log/net_log_with_source.h b/net/log/net_log_with_source.h
index 48627900..b970eec 100644
--- a/net/log/net_log_with_source.h
+++ b/net/log/net_log_with_source.h
@@ -18,7 +18,7 @@
 // output log messages without needing to pass in the source.
 class NET_EXPORT NetLogWithSource {
  public:
-  NetLogWithSource() : net_log_(nullptr) {}
+  NetLogWithSource();
   ~NetLogWithSource();
 
   // Adds a log entry to the NetLog for the bound source.
@@ -29,10 +29,7 @@
   void AddEntry(NetLogEventType type,
                 NetLogEventPhase phase,
                 const ParametersCallback& get_params) const {
-    // TODO(eroman): Should merge the nullity check with
-    // GetObserverCaptureModes() to reduce expanded code size.
-    if (net_log_)
-      net_log_->AddEntry(type, source_, phase, get_params);
+    non_null_net_log_->AddEntry(type, source_, phase, get_params);
   }
 
   // Convenience methods that call AddEntry with a fixed "capture phase"
@@ -119,7 +116,7 @@
                             int byte_count,
                             const char* bytes) const;
 
-  bool IsCapturing() const;
+  bool IsCapturing() const { return non_null_net_log_->IsCapturing(); }
 
   // Helper to create a NetLogWithSource given a NetLog and a NetLogSourceType.
   // Takes care of creating a unique source ID, and handles
@@ -127,14 +124,28 @@
   static NetLogWithSource Make(NetLog* net_log, NetLogSourceType source_type);
 
   const NetLogSource& source() const { return source_; }
-  NetLog* net_log() const { return net_log_; }
+
+  // Returns the bound NetLog*, or nullptr.
+  NetLog* net_log() const;
 
  private:
-  NetLogWithSource(const NetLogSource& source, NetLog* net_log)
-      : source_(source), net_log_(net_log) {}
+  NetLogWithSource(const NetLogSource& source, NetLog* non_null_net_log)
+      : source_(source), non_null_net_log_(non_null_net_log) {}
 
   NetLogSource source_;
-  NetLog* net_log_;
+
+  // There are two types of NetLogWithSource:
+  //
+  // (a) An ordinary NetLogWithSource for which |source().IsValid()| and
+  //     |net_log() != nullptr|
+  //
+  // (b) A default constructed NetLogWithSource for which
+  //     |!source().IsValid()| and |net_log() == nullptr|.
+  //
+  // As an optimization, both types internally store a non-null NetLog*. This
+  // way no null checks are needed before dispatching to the (possibly dummy)
+  // NetLog
+  NetLog* non_null_net_log_;
 };
 
 }  // namespace net
diff --git a/net/quic/quic_chromium_client_session.cc b/net/quic/quic_chromium_client_session.cc
index 888f3b6..acac95a 100644
--- a/net/quic/quic_chromium_client_session.cc
+++ b/net/quic/quic_chromium_client_session.cc
@@ -766,6 +766,9 @@
                                        connection_description,
                                        std::move(socket_performance_watcher),
                                        net_log_)),
+      http3_logger_(VersionHasStreamType(connection->transport_version())
+                        ? new QuicHttp3Logger(net_log_)
+                        : nullptr),
       going_away_(false),
       port_migration_detected_(false),
       push_delegate_(push_delegate),
@@ -800,6 +803,8 @@
           std::make_unique<ProofVerifyContextChromium>(cert_verify_flags,
                                                        net_log_),
           crypto_config_->GetConfig()));
+  if (VersionHasStreamType(transport_version()))
+    set_debug_visitor(http3_logger_.get());
   connection->set_debug_visitor(logger_.get());
   connection->set_creator_debug_delegate(logger_.get());
   migrate_back_to_default_timer_.SetTaskRunner(task_runner_);
diff --git a/net/quic/quic_chromium_client_session.h b/net/quic/quic_chromium_client_session.h
index 40c0e69..973f811 100644
--- a/net/quic/quic_chromium_client_session.h
+++ b/net/quic/quic_chromium_client_session.h
@@ -35,6 +35,7 @@
 #include "net/quic/quic_connection_logger.h"
 #include "net/quic/quic_connectivity_probing_manager.h"
 #include "net/quic/quic_crypto_client_config_handle.h"
+#include "net/quic/quic_http3_logger.h"
 #include "net/quic/quic_session_key.h"
 #include "net/socket/socket_performance_watcher.h"
 #include "net/spdy/http2_priority_dependencies.h"
@@ -822,6 +823,7 @@
   std::vector<std::unique_ptr<QuicChromiumPacketReader>> packet_readers_;
   LoadTimingInfo::ConnectTiming connect_timing_;
   std::unique_ptr<QuicConnectionLogger> logger_;
+  std::unique_ptr<QuicHttp3Logger> http3_logger_;
   // True when the session is going away, and streams may no longer be created
   // on this session. Existing stream will continue to be processed.
   bool going_away_;
diff --git a/net/quic/quic_connection_logger.cc b/net/quic/quic_connection_logger.cc
index 34109ff..268d767 100644
--- a/net/quic/quic_connection_logger.cc
+++ b/net/quic/quic_connection_logger.cc
@@ -56,6 +56,8 @@
               NetLogNumberValue(serialized_packet.packet_number.ToUint64()));
   dict.SetInteger("size", serialized_packet.encrypted_length);
   dict.SetKey("sent_time_us", NetLogNumberValue(sent_time.ToDebuggingValue()));
+  dict.SetString("encryption_level", quic::QuicUtils::EncryptionLevelToString(
+                                         serialized_packet.encryption_level));
   return std::move(dict);
 }
 
diff --git a/net/quic/quic_http3_logger.cc b/net/quic/quic_http3_logger.cc
new file mode 100644
index 0000000..8c43eb7
--- /dev/null
+++ b/net/quic/quic_http3_logger.cc
@@ -0,0 +1,86 @@
+// Copyright (c) 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+#include "net/quic/quic_http3_logger.h"
+
+#include <algorithm>
+#include <memory>
+#include <utility>
+#include <vector>
+
+#include "base/strings/string_number_conversions.h"
+#include "net/log/net_log_capture_mode.h"
+#include "net/log/net_log_event_type.h"
+#include "net/log/net_log_values.h"
+
+namespace net {
+
+namespace {
+
+base::Value NetLogPeerControlStreamParams(quic::QuicStreamId id) {
+  base::DictionaryValue dict;
+  dict.SetInteger("stream_id", id);
+  return std::move(dict);
+}
+
+base::Value NetLogPeerQpackEncoderStreamParams(quic::QuicStreamId id) {
+  base::DictionaryValue dict;
+  dict.SetInteger("stream_id", id);
+  return std::move(dict);
+}
+
+base::Value NetLogPeerQpackDecoderStreamParams(quic::QuicStreamId id) {
+  base::DictionaryValue dict;
+  dict.SetInteger("stream_id", id);
+  return std::move(dict);
+}
+
+base::Value NetLogSettingsParams(const quic::SettingsFrame& frame) {
+  base::DictionaryValue dict;
+  // TODO(renjietang): Use string literal for setting identifiers.
+  for (auto setting : frame.values) {
+    dict.SetInteger(base::NumberToString(setting.first), setting.second);
+  }
+  return std::move(dict);
+}
+
+}  // namespace
+
+QuicHttp3Logger::QuicHttp3Logger(const NetLogWithSource& net_log)
+    : net_log_(net_log) {}
+
+QuicHttp3Logger::~QuicHttp3Logger() {}
+
+void QuicHttp3Logger::OnPeerControlStreamCreated(quic::QuicStreamId stream_id) {
+  if (!net_log_.IsCapturing())
+    return;
+  net_log_.AddEvent(NetLogEventType::HTTP3_PEER_CONTROL_STREAM_CREATED,
+                    [&] { return NetLogPeerControlStreamParams(stream_id); });
+}
+
+void QuicHttp3Logger::OnPeerQpackEncoderStreamCreated(
+    quic::QuicStreamId stream_id) {
+  if (!net_log_.IsCapturing())
+    return;
+  net_log_.AddEvent(
+      NetLogEventType::HTTP3_PEER_QPACK_ENCODER_STREAM_CREATED,
+      [&] { return NetLogPeerQpackEncoderStreamParams(stream_id); });
+}
+
+void QuicHttp3Logger::OnPeerQpackDecoderStreamCreated(
+    quic::QuicStreamId stream_id) {
+  if (!net_log_.IsCapturing())
+    return;
+  net_log_.AddEvent(
+      NetLogEventType::HTTP3_PEER_QPACK_DECODER_STREAM_CREATED,
+      [&] { return NetLogPeerQpackDecoderStreamParams(stream_id); });
+}
+
+void QuicHttp3Logger::OnSettingsFrame(const quic::SettingsFrame& frame) {
+  if (!net_log_.IsCapturing())
+    return;
+  net_log_.AddEvent(NetLogEventType::HTTP3_SETTINGS_RECEIVED,
+                    [&] { return NetLogSettingsParams(frame); });
+}
+
+}  // namespace net
diff --git a/net/quic/quic_http3_logger.h b/net/quic/quic_http3_logger.h
new file mode 100644
index 0000000..6eb3e49
--- /dev/null
+++ b/net/quic/quic_http3_logger.h
@@ -0,0 +1,41 @@
+// Copyright (c) 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 NET_QUIC_QUIC_HTTP3_LOGGER_H_
+#define NET_QUIC_QUIC_HTTP3_LOGGER_H_
+
+#include <stddef.h>
+
+#include <bitset>
+#include <string>
+
+#include "base/macros.h"
+#include "base/timer/timer.h"
+#include "net/log/net_log_with_source.h"
+#include "net/third_party/quiche/src/quic/core/http/quic_spdy_session.h"
+
+namespace net {
+
+// This class is a debug visitor of a quic::QuicSpdySession which logs events
+// to |net_log|.
+class NET_EXPORT_PRIVATE QuicHttp3Logger : public quic::Http3DebugVisitor {
+ public:
+  QuicHttp3Logger(const NetLogWithSource& net_log);
+
+  ~QuicHttp3Logger() override;
+
+  // Implementation of Http3DebugVisitor.
+  void OnPeerControlStreamCreated(quic::QuicStreamId stream_id) override;
+  void OnPeerQpackEncoderStreamCreated(quic::QuicStreamId stream_id) override;
+  void OnPeerQpackDecoderStreamCreated(quic::QuicStreamId stream_id) override;
+  void OnSettingsFrame(const quic::SettingsFrame& frame) override;
+
+ private:
+  NetLogWithSource net_log_;
+
+  DISALLOW_COPY_AND_ASSIGN(QuicHttp3Logger);
+};
+}  // namespace net
+
+#endif  // NET_QUIC_QUIC_HTTP3_LOGGER_H_
diff --git a/net/socket/udp_socket_perftest.cc b/net/socket/udp_socket_perftest.cc
index b3d5a70..bbb78f692 100644
--- a/net/socket/udp_socket_perftest.cc
+++ b/net/socket/udp_socket_perftest.cc
@@ -5,8 +5,8 @@
 #include "base/bind.h"
 #include "base/memory/weak_ptr.h"
 #include "base/run_loop.h"
-#include "base/test/perf_time_logger.h"
 #include "base/test/task_environment.h"
+#include "base/timer/elapsed_timer.h"
 #include "net/base/io_buffer.h"
 #include "net/base/ip_endpoint.h"
 #include "net/base/net_errors.h"
@@ -19,6 +19,7 @@
 #include "net/traffic_annotation/network_traffic_annotation_test_helper.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
+#include "testing/perf/perf_result_reporter.h"
 #include "testing/platform_test.h"
 
 using net::test::IsOk;
@@ -27,6 +28,18 @@
 
 namespace {
 
+static constexpr char kMetricPrefixUDPSocket[] = "UDPSocketWrite.";
+static constexpr char kMetricElapsedTimeMs[] = "elapsed_time";
+static constexpr char kMetricWriteSpeedBytesPerSecond[] = "write_speed";
+
+perf_test::PerfResultReporter SetUpUDPSocketReporter(const std::string& story) {
+  perf_test::PerfResultReporter reporter(kMetricPrefixUDPSocket, story);
+  reporter.RegisterImportantMetric(kMetricElapsedTimeMs, "ms");
+  reporter.RegisterImportantMetric(kMetricWriteSpeedBytesPerSecond,
+                                   "bytesPerSecond_biggerIsBetter");
+  return reporter;
+}
+
 class UDPSocketPerfTest : public PlatformTest {
  public:
   UDPSocketPerfTest()
@@ -91,6 +104,7 @@
 }
 
 void UDPSocketPerfTest::WriteBenchmark(bool use_nonblocking_io) {
+  base::ElapsedTimer total_elapsed_timer;
   base::test::SingleThreadTaskEnvironment task_environment(
       base::test::SingleThreadTaskEnvironment::MainThreadType::IO);
   const uint16_t kPort = 9999;
@@ -116,23 +130,26 @@
   EXPECT_THAT(rv, IsOk());
 
   base::RunLoop run_loop;
-  base::TimeTicks start_ticks = base::TimeTicks::Now();
+  base::ElapsedTimer write_elapsed_timer;
   int packets = 100000;
   client->SetSendBufferSize(1024);
   WritePacketsToSocket(client.get(), packets, run_loop.QuitClosure());
   run_loop.Run();
 
-  double elapsed = (base::TimeTicks::Now() - start_ticks).InSecondsF();
-  LOG(INFO) << "Write speed: " << packets / 1024 / elapsed << " MB/s";
+  double write_elapsed = write_elapsed_timer.Elapsed().InSecondsF();
+  double total_elapsed = total_elapsed_timer.Elapsed().InMillisecondsF();
+  auto reporter =
+      SetUpUDPSocketReporter(use_nonblocking_io ? "nonblocking" : "blocking");
+  reporter.AddResult(kMetricElapsedTimeMs, total_elapsed);
+  reporter.AddResult(kMetricWriteSpeedBytesPerSecond,
+                     packets * 1024 / write_elapsed);
 }
 
 TEST_F(UDPSocketPerfTest, Write) {
-  base::PerfTimeLogger timer("UDP_socket_write");
   WriteBenchmark(false);
 }
 
 TEST_F(UDPSocketPerfTest, WriteNonBlocking) {
-  base::PerfTimeLogger timer("UDP_socket_write_nonblocking");
   WriteBenchmark(true);
 }
 
diff --git a/net/url_request/url_request_quic_perftest.cc b/net/url_request/url_request_quic_perftest.cc
index 30e6fc4..90f2d97 100644
--- a/net/url_request/url_request_quic_perftest.cc
+++ b/net/url_request/url_request_quic_perftest.cc
@@ -39,7 +39,7 @@
 #include "net/url_request/url_request_test_util.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
-#include "testing/perf/perf_test.h"
+#include "testing/perf/perf_result_reporter.h"
 #include "url/gurl.h"
 
 using testing::_;
@@ -62,6 +62,20 @@
 const char kHelloOriginResponse[] = "Hello from TCP Server";
 const int kHelloStatus = 200;
 
+static constexpr char kMetricPrefixURLRequestQuick[] = "URLRequestQuic.";
+static constexpr char kMetricRequestTimeMs[] = "request_time";
+static constexpr char kMetricActiveQuicJobsCount[] = "active_quic_jobs";
+static constexpr char kMetricActiveQuicSessionsCount[] = "active_quic_sessions";
+
+perf_test::PerfResultReporter SetUpURLRequestQuicReporter(
+    const std::string& story) {
+  perf_test::PerfResultReporter reporter(kMetricPrefixURLRequestQuick, story);
+  reporter.RegisterImportantMetric(kMetricRequestTimeMs, "ms");
+  reporter.RegisterImportantMetric(kMetricActiveQuicJobsCount, "count");
+  reporter.RegisterImportantMetric(kMetricActiveQuicSessionsCount, "count");
+  return reporter;
+}
+
 std::unique_ptr<test_server::HttpResponse> HandleRequest(
     const test_server::HttpRequest& request) {
   std::unique_ptr<test_server::BasicHttpResponse> http_response(
@@ -78,16 +92,6 @@
   return std::move(http_response);
 }
 
-void PrintPerfTest(const std::string& name,
-                   int value,
-                   const std::string& unit) {
-  const ::testing::TestInfo* test_info =
-      ::testing::UnitTest::GetInstance()->current_test_info();
-  perf_test::PrintResult(test_info->test_case_name(),
-                         std::string(".") + test_info->name(), name,
-                         static_cast<double>(value), unit, true);
-}
-
 class URLRequestQuicPerfTest : public ::testing::Test {
  protected:
   URLRequestQuicPerfTest()
@@ -221,7 +225,9 @@
     }
   }
   base::TimeTicks end = base::TimeTicks::Now();
-  PrintPerfTest("time", (end - start).InMilliseconds() / kNumRequest, "ms");
+  auto reporter = SetUpURLRequestQuicReporter("get");
+  reporter.AddResult(kMetricRequestTimeMs,
+                     (end - start).InMillisecondsF() / kNumRequest);
 
   EXPECT_TRUE(quic_succeeded);
   base::trace_event::MemoryDumpManager::GetInstance()->SetupForTracing(
@@ -259,11 +265,9 @@
                           base::trace_event::MemoryAllocatorDump::kUnitsObjects,
                           0);
 
-        PrintPerfTest("active_quic_jobs", 0, "count");
         CheckScalarInDump(quic_stream_factory_dump, "all_sessions",
                           base::trace_event::MemoryAllocatorDump::kUnitsObjects,
                           1);
-        PrintPerfTest("active_quic_sessions", 1, "count");
 
         std::string stream_factory_dump_name = base::StringPrintf(
             "net/http_network_session_0x%" PRIxPTR "/stream_factory",
@@ -271,7 +275,6 @@
                 context->http_transaction_factory()->GetSession()));
         ASSERT_EQ(0u, allocator_dumps.count(stream_factory_dump_name));
         quit_closure.Run();
-
       };
   base::trace_event::MemoryDumpManager::GetInstance()->CreateProcessDump(
       args,
diff --git a/net/websockets/websocket_frame_perftest.cc b/net/websockets/websocket_frame_perftest.cc
index 55ff21d..7321046b 100644
--- a/net/websockets/websocket_frame_perftest.cc
+++ b/net/websockets/websocket_frame_perftest.cc
@@ -8,8 +8,9 @@
 #include <vector>
 
 #include "base/stl_util.h"
-#include "base/test/perf_time_logger.h"
+#include "base/timer/elapsed_timer.h"
 #include "testing/gtest/include/gtest/gtest.h"
+#include "testing/perf/perf_result_reporter.h"
 
 namespace net {
 
@@ -19,13 +20,23 @@
 const int kLongPayloadSize = 1 << 16;
 const char kMaskingKey[] = "\xFE\xED\xBE\xEF";
 
+static constexpr char kMetricPrefixWebSocketFrame[] = "WebSocketFrameMask.";
+static constexpr char kMetricMaskTimeMs[] = "mask_time";
+
+perf_test::PerfResultReporter SetUpWebSocketFrameMaskReporter(
+    const std::string& story) {
+  perf_test::PerfResultReporter reporter(kMetricPrefixWebSocketFrame, story);
+  reporter.RegisterImportantMetric(kMetricMaskTimeMs, "ms");
+  return reporter;
+}
+
 static_assert(base::size(kMaskingKey) ==
                   WebSocketFrameHeader::kMaskingKeyLength + 1,
               "incorrect masking key size");
 
 class WebSocketFrameTestMaskBenchmark : public ::testing::Test {
  protected:
-  void Benchmark(const char* const name,
+  void Benchmark(const char* const story,
                  const char* const payload,
                  size_t size) {
     std::vector<char> scratch(payload, payload + size);
@@ -33,24 +44,24 @@
     std::copy(kMaskingKey,
               kMaskingKey + WebSocketFrameHeader::kMaskingKeyLength,
               masking_key.key);
-    base::PerfTimeLogger timer(name);
+    auto reporter = SetUpWebSocketFrameMaskReporter(story);
+    base::ElapsedTimer timer;
     for (int x = 0; x < kIterations; ++x) {
       MaskWebSocketFramePayload(
           masking_key, x % size, &scratch.front(), scratch.size());
     }
-    timer.Done();
+    reporter.AddResult(kMetricMaskTimeMs, timer.Elapsed().InMillisecondsF());
   }
 };
 
 TEST_F(WebSocketFrameTestMaskBenchmark, BenchmarkMaskShortPayload) {
   static const char kShortPayload[] = "Short Payload";
-  Benchmark("Frame_mask_short_payload", kShortPayload,
-            base::size(kShortPayload));
+  Benchmark("short_payload", kShortPayload, base::size(kShortPayload));
 }
 
 TEST_F(WebSocketFrameTestMaskBenchmark, BenchmarkMaskLongPayload) {
   std::vector<char> payload(kLongPayloadSize, 'a');
-  Benchmark("Frame_mask_long_payload", &payload.front(), payload.size());
+  Benchmark("long_payload", &payload.front(), payload.size());
 }
 
 // A 31-byte payload is guaranteed to do 7 byte mask operations and 3 vector
@@ -58,7 +69,7 @@
 // back to the byte-only code path and do 31 byte mask operations.
 TEST_F(WebSocketFrameTestMaskBenchmark, Benchmark31BytePayload) {
   std::vector<char> payload(31, 'a');
-  Benchmark("Frame_mask_31_payload", &payload.front(), payload.size());
+  Benchmark("31_payload", &payload.front(), payload.size());
 }
 
 }  // namespace
diff --git a/pdf/pdfium/accessibility_unittest.cc b/pdf/pdfium/accessibility_unittest.cc
index 8852f2e..7a181fc7 100644
--- a/pdf/pdfium/accessibility_unittest.cc
+++ b/pdf/pdfium/accessibility_unittest.cc
@@ -358,6 +358,10 @@
     disposition_ = disposition;
   }
 
+  void ScrollToX(int x_in_screen_coords) override {
+    x_scroll_offset_ = x_in_screen_coords;
+  }
+
   void ScrollToY(int y_in_screen_coords, bool compensate_for_toolbar) override {
     y_scroll_offset_ = y_in_screen_coords;
     compensate_for_toolbar_ = compensate_for_toolbar;
@@ -365,12 +369,14 @@
 
   const std::string& url() const { return url_; }
   WindowOpenDisposition disposition() const { return disposition_; }
+  int x_scroll_offset() const { return x_scroll_offset_; }
   int y_scroll_offset() const { return y_scroll_offset_; }
   bool compensate_for_toolbar() const { return compensate_for_toolbar_; }
 
  private:
   std::string url_;
   WindowOpenDisposition disposition_ = WindowOpenDisposition::UNKNOWN;
+  int x_scroll_offset_ = 0;
   int y_scroll_offset_ = 0;
   bool compensate_for_toolbar_ = false;
 };
@@ -401,6 +407,7 @@
   action_data.page_index = 0;
   action_data.link_index = 1;
   engine->HandleAccessibilityAction(action_data);
+  EXPECT_EQ(266, client.x_scroll_offset());
   EXPECT_EQ(1159, client.y_scroll_offset());
   EXPECT_TRUE(client.compensate_for_toolbar());
   EXPECT_TRUE(client.url().empty());
diff --git a/pdf/pdfium/pdfium_engine.cc b/pdf/pdfium/pdfium_engine.cc
index 4eb5d24d..1628b4c 100644
--- a/pdf/pdfium/pdfium_engine.cc
+++ b/pdf/pdfium/pdfium_engine.cc
@@ -1219,17 +1219,18 @@
 
     if (disposition == WindowOpenDisposition::CURRENT_TAB) {
       pp::Rect page_rect(GetPageScreenRect(target.page));
+      int x = position_.x() + page_rect.x();
+      x += target.x_in_pixels.value_or(0) * current_zoom_;
       int y = position_.y() + page_rect.y();
-      if (target.y_in_pixels)
-        y += target.y_in_pixels.value() * current_zoom_;
+      y += target.y_in_pixels.value_or(0) * current_zoom_;
 
+      client_->ScrollToX(x);
       client_->ScrollToY(y, /*compensate_for_toolbar=*/true);
     } else {
       std::string parameters = base::StringPrintf("#page=%d", target.page + 1);
-      if (target.y_in_pixels) {
-        parameters += base::StringPrintf(
-            "&zoom=100,0,%d", static_cast<int>(target.y_in_pixels.value()));
-      }
+      parameters += base::StringPrintf(
+          "&zoom=100,%d,%d", static_cast<int>(target.x_in_pixels.value_or(0)),
+          static_cast<int>(target.y_in_pixels.value_or(0)));
 
       client_->NavigateTo(parameters, disposition);
     }
@@ -2082,8 +2083,10 @@
 
       base::Optional<gfx::PointF> xy =
           pages_[page_index]->GetPageXYTarget(dest);
-      if (xy)
+      if (xy) {
+        dict.Set(pp::Var("x"), pp::Var(static_cast<int>(xy.value().x())));
         dict.Set(pp::Var("y"), pp::Var(static_cast<int>(xy.value().y())));
+      }
     }
   } else {
     // Extract URI for bookmarks linking to an external page.
diff --git a/pdf/pdfium/pdfium_page.cc b/pdf/pdfium/pdfium_page.cc
index e27d582..9de9337 100644
--- a/pdf/pdfium/pdfium_page.cc
+++ b/pdf/pdfium/pdfium_page.cc
@@ -625,8 +625,11 @@
   target->page = page_index;
 
   base::Optional<gfx::PointF> xy = GetPageXYTarget(destination);
-  if (xy)
-    target->y_in_pixels = TransformPageToScreenXY(xy.value()).y();
+  if (xy) {
+    gfx::PointF point = TransformPageToScreenXY(xy.value());
+    target->x_in_pixels = point.x();
+    target->y_in_pixels = point.y();
+  }
 
   return DOCLINK_AREA;
 }
diff --git a/pdf/pdfium/pdfium_page.h b/pdf/pdfium/pdfium_page.h
index e9b69be..035ffc57 100644
--- a/pdf/pdfium/pdfium_page.h
+++ b/pdf/pdfium/pdfium_page.h
@@ -91,7 +91,8 @@
 
     // Valid for DOCLINK_AREA only.
     int page;
-    // Valid for DOCLINK_AREA only. From the top of the page.
+    // Valid for DOCLINK_AREA only. From the top-left of the page.
+    base::Optional<float> x_in_pixels;
     base::Optional<float> y_in_pixels;
   };
 
diff --git a/pdf/pdfium/pdfium_test_base.cc b/pdf/pdfium/pdfium_test_base.cc
index 724a1f8..97076ad 100644
--- a/pdf/pdfium/pdfium_test_base.cc
+++ b/pdf/pdfium/pdfium_test_base.cc
@@ -64,8 +64,10 @@
   pp::URLLoader dummy_loader;
   auto engine =
       std::make_unique<PDFiumEngine>(client, /*enable_javascript=*/false);
+  client->set_engine(engine.get());
   if (!engine->New("https://chromium.org/dummy.pdf", "") ||
       !engine->HandleDocumentLoad(dummy_loader)) {
+    client->set_engine(nullptr);
     return nullptr;
   }
   return engine;
diff --git a/pdf/test/test_client.h b/pdf/test/test_client.h
index a269995..f61ade0 100644
--- a/pdf/test/test_client.h
+++ b/pdf/test/test_client.h
@@ -15,8 +15,15 @@
 class TestClient : public PDFEngine::Client {
  public:
   TestClient();
+
+  TestClient(const TestClient& other) = delete;
+  TestClient& operator=(const TestClient& other) = delete;
+
   ~TestClient() override;
 
+  PDFEngine* engine() const { return engine_; }
+  void set_engine(PDFEngine* engine) { engine_ = engine; }
+
   // PDFEngine::Client:
   bool Confirm(const std::string& message) override;
   std::string Prompt(const std::string& question,
@@ -32,7 +39,9 @@
   float GetToolbarHeightInScreenCoords() override;
 
  private:
-  DISALLOW_COPY_AND_ASSIGN(TestClient);
+  // Not owned. Expected to dangle briefly, as the engine usually is destroyed
+  // before the client.
+  PDFEngine* engine_ = nullptr;
 };
 
 }  // namespace chrome_pdf
diff --git a/services/image_annotation/image_annotation_metrics.cc b/services/image_annotation/image_annotation_metrics.cc
index 205480a..5bb87f0 100644
--- a/services/image_annotation/image_annotation_metrics.cc
+++ b/services/image_annotation/image_annotation_metrics.cc
@@ -15,18 +15,19 @@
 
 namespace image_annotation {
 
-using namespace metrics_internal;
+using metrics_internal::kAnnotationConfidence;
+using metrics_internal::kAnnotationEmpty;
 
 void ReportCacheHit(const bool cache_hit) {
-  UMA_HISTOGRAM_BOOLEAN(kCacheHit, cache_hit);
+  UMA_HISTOGRAM_BOOLEAN(metrics_internal::kCacheHit, cache_hit);
 }
 
 void ReportJsonParseSuccess(const bool success) {
-  UMA_HISTOGRAM_BOOLEAN(kJsonParseSuccess, success);
+  UMA_HISTOGRAM_BOOLEAN(metrics_internal::kJsonParseSuccess, success);
 }
 
 void ReportPixelFetchSuccess(const bool success) {
-  UMA_HISTOGRAM_BOOLEAN(kPixelFetchSuccess, success);
+  UMA_HISTOGRAM_BOOLEAN(metrics_internal::kPixelFetchSuccess, success);
 }
 
 void ReportOcrAnnotation(const double confidence, const bool empty) {
@@ -58,74 +59,78 @@
   base::UmaHistogramBoolean(
       base::StringPrintf(kAnnotationEmpty, type_name.c_str()), empty);
 
-  UMA_HISTOGRAM_ENUMERATION(kDescType, type);
+  UMA_HISTOGRAM_ENUMERATION(metrics_internal::kDescType, type);
 }
 
 void ReportDescFailure(const DescFailureReason reason) {
-  UMA_HISTOGRAM_ENUMERATION(kDescFailure, reason);
+  UMA_HISTOGRAM_ENUMERATION(metrics_internal::kDescFailure, reason);
 }
 
 void ReportServerNetError(const int code) {
-  base::UmaHistogramSparse(kServerNetError, code);
+  base::UmaHistogramSparse(metrics_internal::kServerNetError, code);
 }
 
 void ReportServerResponseCode(const int code) {
-  base::UmaHistogramSparse(kServerHttpResponseCode, code);
+  base::UmaHistogramSparse(metrics_internal::kServerHttpResponseCode, code);
 }
 
 void ReportServerLatency(const base::TimeDelta latency) {
   // Use a custom time histogram with ~10 buckets per order of magnitude between
   // 1ms and 30sec.
-  UMA_HISTOGRAM_CUSTOM_TIMES(kServerLatency, latency,
+  UMA_HISTOGRAM_CUSTOM_TIMES(metrics_internal::kServerLatency, latency,
                              base::TimeDelta::FromMilliseconds(1),
                              base::TimeDelta::FromSeconds(30), 50);
 }
 
 void ReportImageRequestIncludesDesc(const bool includes_desc) {
-  UMA_HISTOGRAM_BOOLEAN(kImageRequestIncludesDesc, includes_desc);
+  UMA_HISTOGRAM_BOOLEAN(metrics_internal::kImageRequestIncludesDesc,
+                        includes_desc);
 }
 
 void ReportServerRequestSizeKB(const size_t size_kb) {
   // Use a custom memory histogram with ~10 buckets per order of magnitude
   // between 1KB and 30MB.
-  UMA_HISTOGRAM_CUSTOM_COUNTS(kServerRequestSize, size_kb, 1, 30000, 50);
+  UMA_HISTOGRAM_CUSTOM_COUNTS(metrics_internal::kServerRequestSize, size_kb, 1,
+                              30000, 50);
 }
 
 void ReportServerResponseSizeBytes(const size_t size_bytes) {
   // Use a custom memory histogram with ~10 buckets per order of magnitude
   // between 1byte and 1MB (at which point we stop downloading).
-  UMA_HISTOGRAM_CUSTOM_COUNTS(kServerResponseSize, size_bytes, 1, 1000000, 70);
+  UMA_HISTOGRAM_CUSTOM_COUNTS(metrics_internal::kServerResponseSize, size_bytes,
+                              1, 1000000, 70);
 }
 
 void ReportOcrStatus(const int status) {
-  base::UmaHistogramSparse(base::StringPrintf(kAnnotationStatus, "Ocr"),
-                           status);
+  base::UmaHistogramSparse(
+      base::StringPrintf(metrics_internal::kAnnotationStatus, "Ocr"), status);
 }
 
 void ReportDescStatus(const int status) {
-  base::UmaHistogramSparse(base::StringPrintf(kAnnotationStatus, "Desc"),
-                           status);
+  base::UmaHistogramSparse(
+      base::StringPrintf(metrics_internal::kAnnotationStatus, "Desc"), status);
 }
 
 void ReportEngineKnown(const bool known) {
-  UMA_HISTOGRAM_BOOLEAN(kEngineKnown, known);
+  UMA_HISTOGRAM_BOOLEAN(metrics_internal::kEngineKnown, known);
 }
 
 void ReportSourcePixelCount(const size_t pixel_count) {
   // Use a custom memory histogram with ~10 buckets per order of magnitude
   // 1x1 and 8K image resolution.
-  UMA_HISTOGRAM_CUSTOM_COUNTS(kSourcePixelCount, pixel_count, 1, 7680 * 4320,
-                              80);
+  UMA_HISTOGRAM_CUSTOM_COUNTS(metrics_internal::kSourcePixelCount, pixel_count,
+                              1, 7680 * 4320, 80);
 }
 
 void ReportEncodedJpegSize(const size_t size_bytes) {
   // Use a custom memory histogram with ~10 buckets per order of magnitude
   // between 1byte and 1MB.
-  UMA_HISTOGRAM_CUSTOM_COUNTS(kEncodedJpegSize, size_bytes, 1, 1000000, 70);
+  UMA_HISTOGRAM_CUSTOM_COUNTS(metrics_internal::kEncodedJpegSize, size_bytes, 1,
+                              1000000, 70);
 }
 
 void ReportClientResult(const ClientResult result) {
-  UMA_HISTOGRAM_ENUMERATION(kClientResult, result);
+  UMA_HISTOGRAM_ENUMERATION(metrics_internal::kClientResult, result);
 }
 
 }  // namespace image_annotation
diff --git a/services/network/loader_util.cc b/services/network/loader_util.cc
index d762367..3b1cb19 100644
--- a/services/network/loader_util.cc
+++ b/services/network/loader_util.cc
@@ -124,15 +124,6 @@
   return info;
 }
 
-std::string ComputeReferrer(const GURL& referrer) {
-  if (!referrer.is_valid() || base::CommandLine::ForCurrentProcess()->HasSwitch(
-                                  switches::kNoReferrers)) {
-    return std::string();
-  }
-
-  return referrer.spec();
-}
-
 void LogConcerningRequestHeaders(const net::HttpRequestHeaders& request_headers,
                                  bool added_during_redirect) {
   net::HttpRequestHeaders::Iterator it(request_headers);
diff --git a/services/network/public/cpp/network_switches.cc b/services/network/public/cpp/network_switches.cc
index 3b3480a..019faf68 100644
--- a/services/network/public/cpp/network_switches.cc
+++ b/services/network/public/cpp/network_switches.cc
@@ -56,9 +56,6 @@
 // for the format.
 const char kSSLKeyLogFile[] = "ssl-key-log-file";
 
-// Don't send HTTP-Referer headers.
-const char kNoReferrers[] = "no-referrers";
-
 // Allows overriding the list of restricted ports by passing a comma-separated
 // list of port numbers.
 const char kExplicitlyAllowedPorts[] = "explicitly-allowed-ports";
diff --git a/services/network/public/cpp/network_switches.h b/services/network/public/cpp/network_switches.h
index 5093899..6fa0b4d 100644
--- a/services/network/public/cpp/network_switches.h
+++ b/services/network/public/cpp/network_switches.h
@@ -20,7 +20,6 @@
 COMPONENT_EXPORT(NETWORK_CPP) extern const char kLogNetLog[];
 COMPONENT_EXPORT(NETWORK_CPP) extern const char kNetLogCaptureMode[];
 COMPONENT_EXPORT(NETWORK_CPP) extern const char kSSLKeyLogFile[];
-COMPONENT_EXPORT(NETWORK_CPP) extern const char kNoReferrers[];
 COMPONENT_EXPORT(NETWORK_CPP) extern const char kExplicitlyAllowedPorts[];
 COMPONENT_EXPORT(NETWORK_CPP)
 extern const char kUnsafelyTreatInsecureOriginAsSecure[];
diff --git a/services/network/url_loader.cc b/services/network/url_loader.cc
index a2195a0..dbef4f1 100644
--- a/services/network/url_loader.cc
+++ b/services/network/url_loader.cc
@@ -398,7 +398,7 @@
   url_request_->set_method(request.method);
   url_request_->set_site_for_cookies(request.site_for_cookies);
   url_request_->set_attach_same_site_cookies(request.attach_same_site_cookies);
-  url_request_->SetReferrer(ComputeReferrer(request.referrer));
+  url_request_->SetReferrer(request.referrer.GetAsReferrer().spec());
   url_request_->set_referrer_policy(request.referrer_policy);
   url_request_->set_upgrade_if_insecure(request.upgrade_if_insecure);
 
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 b456e4d..1f80780 100644
--- a/services/tracing/public/cpp/perfetto/trace_event_data_source.cc
+++ b/services/tracing/public/cpp/perfetto/trace_event_data_source.cc
@@ -8,6 +8,7 @@
 #include <map>
 #include <memory>
 #include <utility>
+#include <vector>
 
 #include "base/base64.h"
 #include "base/bind.h"
@@ -22,9 +23,11 @@
 #include "base/pickle.h"
 #include "base/sequence_checker.h"
 #include "base/strings/string_number_conversions.h"
+#include "base/synchronization/lock.h"
 #include "base/task/common/scoped_defer_task_posting.h"
 #include "base/threading/sequenced_task_runner_handle.h"
 #include "base/time/time.h"
+#include "base/trace_event/trace_config.h"
 #include "base/trace_event/trace_event.h"
 #include "base/trace_event/trace_log.h"
 #include "build/build_config.h"
@@ -52,6 +55,7 @@
 using TraceLog = base::trace_event::TraceLog;
 using TraceEvent = base::trace_event::TraceEvent;
 using TraceConfig = base::trace_event::TraceConfig;
+using TraceRecordMode = base::trace_event::TraceRecordMode;
 using perfetto::protos::pbzero::ChromeMetadataPacket;
 
 namespace tracing {
@@ -76,8 +80,8 @@
 
 }  // namespace
 
-using ChromeEventBundleHandle =
-    protozero::MessageHandle<perfetto::protos::pbzero::ChromeEventBundle>;
+using perfetto::protos::pbzero::ChromeEventBundle;
+using ChromeEventBundleHandle = protozero::MessageHandle<ChromeEventBundle>;
 
 // static
 TraceEventMetadataSource* TraceEventMetadataSource::GetInstance() {
@@ -102,12 +106,15 @@
     JsonMetadataGeneratorFunction generator) {
   DCHECK(origin_task_runner_->RunsTasksInCurrentSequence());
   json_generator_functions_.push_back(generator);
+  // An EventBundle is created when nullptr is passed.
+  GenerateJsonMetadataFromGenerator(generator, nullptr);
 }
 
 void TraceEventMetadataSource::AddGeneratorFunction(
     MetadataGeneratorFunction generator) {
   DCHECK(origin_task_runner_->RunsTasksInCurrentSequence());
   generator_functions_.push_back(generator);
+  GenerateMetadataFromGenerator(generator);
 }
 
 std::unique_ptr<base::DictionaryValue>
@@ -116,14 +123,12 @@
     return nullptr;
   }
 
-  base::trace_event::TraceConfig parsed_chrome_config(chrome_config_);
-
   auto metadata_dict = std::make_unique<base::DictionaryValue>();
   // If argument filtering is enabled, we need to check if the trace config is
   // whitelisted before emitting it.
   // TODO(eseckler): Figure out a way to solve this without calling directly
   // into IsMetadataWhitelisted().
-  if (!parsed_chrome_config.IsArgumentFilterEnabled() ||
+  if (!parsed_chrome_config_->IsArgumentFilterEnabled() ||
       IsMetadataWhitelisted("trace-config")) {
     metadata_dict->SetString("trace-config", chrome_config_);
   } else {
@@ -134,81 +139,177 @@
   return metadata_dict;
 }
 
-void TraceEventMetadataSource::GenerateMetadata(
-    std::unique_ptr<perfetto::TraceWriter> trace_writer) {
+void TraceEventMetadataSource::GenerateMetadataFromGenerator(
+    const TraceEventMetadataSource::MetadataGeneratorFunction& generator) {
   DCHECK(origin_task_runner_->RunsTasksInCurrentSequence());
-  auto trace_packet = trace_writer->NewTracePacket();
-  auto* chrome_metadata = trace_packet->set_chrome_metadata();
-  for (auto& generator : generator_functions_) {
-    generator.Run(chrome_metadata, privacy_filtering_enabled_);
+  perfetto::TraceWriter::TracePacketHandle trace_packet;
+  {
+    base::AutoLock lock(lock_);
+    if (!emit_metadata_at_start_ || !trace_writer_) {
+      return;
+    }
+    trace_packet = trace_writer_->NewTracePacket();
   }
-  trace_packet = perfetto::TraceWriter::TracePacketHandle();
+  trace_packet->set_timestamp(
+      TRACE_TIME_TICKS_NOW().since_origin().InNanoseconds());
+  auto* chrome_metadata = trace_packet->set_chrome_metadata();
+  generator.Run(chrome_metadata, privacy_filtering_enabled_);
+}
 
-  // We already have the |trace_writer| and |trace_packet|, so regardless of if
-  // we need to return due to privacy we need to null out the |producer_| to
-  // inform the system that we are done tracing with this |producer_|
-  producer_ = nullptr;
-  if (privacy_filtering_enabled_) {
+void TraceEventMetadataSource::GenerateJsonMetadataFromGenerator(
+    const TraceEventMetadataSource::JsonMetadataGeneratorFunction& generator,
+    ChromeEventBundle* event_bundle) {
+  DCHECK(origin_task_runner_->RunsTasksInCurrentSequence());
+  perfetto::TraceWriter::TracePacketHandle trace_packet;
+  if (!event_bundle) {
+    {
+      base::AutoLock lock(lock_);
+      if (!emit_metadata_at_start_ || !trace_writer_) {
+        return;
+      }
+      trace_packet = trace_writer_->NewTracePacket();
+    }
+    trace_packet->set_timestamp(
+        TRACE_TIME_TICKS_NOW().since_origin().InNanoseconds());
+    event_bundle = trace_packet->set_chrome_events();
+  }
+
+  std::unique_ptr<base::DictionaryValue> metadata_dict = generator.Run();
+  if (!metadata_dict) {
     return;
   }
 
-  auto legacy_trace_packet = trace_writer->NewTracePacket();
-  ChromeEventBundleHandle event_bundle(
-      legacy_trace_packet->set_chrome_events());
+  for (const auto& it : metadata_dict->DictItems()) {
+    auto* new_metadata = event_bundle->add_metadata();
+    new_metadata->set_name(it.first.c_str());
 
-  for (auto& generator : json_generator_functions_) {
-    std::unique_ptr<base::DictionaryValue> metadata_dict = generator.Run();
-    if (!metadata_dict) {
-      continue;
+    if (it.second.is_int()) {
+      new_metadata->set_int_value(it.second.GetInt());
+    } else if (it.second.is_bool()) {
+      new_metadata->set_bool_value(it.second.GetBool());
+    } else if (it.second.is_string()) {
+      new_metadata->set_string_value(it.second.GetString().c_str());
+    } else {
+      std::string json_value;
+      base::JSONWriter::Write(it.second, &json_value);
+      new_metadata->set_json_value(json_value.c_str());
     }
+  }
+}
 
-    for (const auto& it : metadata_dict->DictItems()) {
-      auto* new_metadata = event_bundle->add_metadata();
-      new_metadata->set_name(it.first.c_str());
+void TraceEventMetadataSource::GenerateMetadata(
+    std::unique_ptr<
+        std::vector<TraceEventMetadataSource::JsonMetadataGeneratorFunction>>
+        json_generators,
+    std::unique_ptr<
+        std::vector<TraceEventMetadataSource::MetadataGeneratorFunction>>
+        proto_generators) {
+  DCHECK(origin_task_runner_->RunsTasksInCurrentSequence());
+  perfetto::TraceWriter::TracePacketHandle trace_packet;
+  bool privacy_filtering_enabled;
+  {
+    base::AutoLock lock(lock_);
+    trace_packet = trace_writer_->NewTracePacket();
+    privacy_filtering_enabled = privacy_filtering_enabled_;
+  }
 
-      if (it.second.is_int()) {
-        new_metadata->set_int_value(it.second.GetInt());
-      } else if (it.second.is_bool()) {
-        new_metadata->set_bool_value(it.second.GetBool());
-      } else if (it.second.is_string()) {
-        new_metadata->set_string_value(it.second.GetString().c_str());
-      } else {
-        std::string json_value;
-        base::JSONWriter::Write(it.second, &json_value);
-        new_metadata->set_json_value(json_value.c_str());
-      }
-    }
+  trace_packet->set_timestamp(
+      TRACE_TIME_TICKS_NOW().since_origin().InNanoseconds());
+  auto* chrome_metadata = trace_packet->set_chrome_metadata();
+  for (auto& generator : *proto_generators) {
+    generator.Run(chrome_metadata, privacy_filtering_enabled_);
+  }
+
+  if (privacy_filtering_enabled) {
+    return;
+  }
+
+  trace_packet->set_timestamp(
+      TRACE_TIME_TICKS_NOW().since_origin().InNanoseconds());
+  ChromeEventBundle* event_bundle = trace_packet->set_chrome_events();
+
+  for (auto& generator : *json_generators) {
+    GenerateJsonMetadataFromGenerator(generator, event_bundle);
   }
 }
 
 void TraceEventMetadataSource::StartTracing(
     PerfettoProducer* producer,
     const perfetto::DataSourceConfig& data_source_config) {
-  // TODO(eseckler): Once we support streaming of trace data, it would make
-  // sense to emit the metadata on startup, so the UI can display it right away.
-  privacy_filtering_enabled_ =
-      data_source_config.chrome_config().privacy_filtering_enabled();
-  chrome_config_ = data_source_config.chrome_config().trace_config();
-  trace_writer_ =
-      producer->CreateTraceWriter(data_source_config.target_buffer());
+  auto json_generators =
+      std::make_unique<std::vector<JsonMetadataGeneratorFunction>>();
+  auto proto_generators =
+      std::make_unique<std::vector<MetadataGeneratorFunction>>();
+  {
+    base::AutoLock lock(lock_);
+    privacy_filtering_enabled_ =
+        data_source_config.chrome_config().privacy_filtering_enabled();
+    chrome_config_ = data_source_config.chrome_config().trace_config();
+    parsed_chrome_config_ = std::make_unique<TraceConfig>(chrome_config_);
+    trace_writer_ =
+        producer->CreateTraceWriter(data_source_config.target_buffer());
+    switch (parsed_chrome_config_->GetTraceRecordMode()) {
+      case TraceRecordMode::RECORD_UNTIL_FULL:
+      case TraceRecordMode::RECORD_AS_MUCH_AS_POSSIBLE: {
+        emit_metadata_at_start_ = true;
+        *json_generators = json_generator_functions_;
+        *proto_generators = generator_functions_;
+        break;
+      }
+      case TraceRecordMode::RECORD_CONTINUOUSLY:
+      case TraceRecordMode::ECHO_TO_CONSOLE:
+        emit_metadata_at_start_ = false;
+        return;
+    }
+  }
+  // |emit_metadata_at_start_| is true if we are in discard packets mode, write
+  // metadata at the beginning of the trace to make it less likely to be
+  // dropped.
+  origin_task_runner_->PostTask(
+      FROM_HERE,
+      base::BindOnce(&TraceEventMetadataSource::GenerateMetadata,
+                     base::Unretained(this), std::move(json_generators),
+                     std::move(proto_generators)));
 }
 
 void TraceEventMetadataSource::StopTracing(
     base::OnceClosure stop_complete_callback) {
-  if (trace_writer_) {
-    // Write metadata at the end of tracing to make it less likely that it is
-    // overwritten by other trace data in perfetto's ring buffer.
-    origin_task_runner_->PostTaskAndReply(
-        FROM_HERE,
-        base::BindOnce(&TraceEventMetadataSource::GenerateMetadata,
-                       base::Unretained(this), std::move(trace_writer_)),
-        std::move(stop_complete_callback));
-  } else {
-    producer_ = nullptr;
-    trace_writer_.reset();
-    chrome_config_ = std::string();
-    std::move(stop_complete_callback).Run();
+  base::OnceClosure maybe_generate_task = base::DoNothing();
+  {
+    base::AutoLock lock(lock_);
+    if (!emit_metadata_at_start_ && trace_writer_) {
+      // Write metadata at the end of tracing if not emitted at start (in ring
+      // buffer mode), to make it less likely that it is overwritten by other
+      // trace data in perfetto's ring buffer.
+      auto json_generators =
+          std::make_unique<std::vector<JsonMetadataGeneratorFunction>>();
+      *json_generators = json_generator_functions_;
+      auto proto_generators =
+          std::make_unique<std::vector<MetadataGeneratorFunction>>();
+      *proto_generators = generator_functions_;
+      maybe_generate_task = base::BindOnce(
+          &TraceEventMetadataSource::GenerateMetadata, base::Unretained(this),
+          std::move(json_generators), std::move(proto_generators));
+    }
   }
+  // Even when not generating metadata, make sure the metadata generate task
+  // posted at the start is finished, by posting task on origin task runner.
+  origin_task_runner_->PostTaskAndReply(
+      FROM_HERE, std::move(maybe_generate_task),
+      base::BindOnce(
+          [](TraceEventMetadataSource* ds,
+             base::OnceClosure stop_complete_callback) {
+            {
+              base::AutoLock lock(ds->lock_);
+              ds->producer_ = nullptr;
+              ds->trace_writer_.reset();
+              ds->chrome_config_ = std::string();
+              ds->parsed_chrome_config_.reset();
+              ds->emit_metadata_at_start_ = false;
+            }
+            std::move(stop_complete_callback).Run();
+          },
+          base::Unretained(this), std::move(stop_complete_callback)));
 }
 
 void TraceEventMetadataSource::Flush(
diff --git a/services/tracing/public/cpp/perfetto/trace_event_data_source.h b/services/tracing/public/cpp/perfetto/trace_event_data_source.h
index e082ce23..428b1c3 100644
--- a/services/tracing/public/cpp/perfetto/trace_event_data_source.h
+++ b/services/tracing/public/cpp/perfetto/trace_event_data_source.h
@@ -20,6 +20,7 @@
 #include "base/trace_event/trace_config.h"
 #include "services/tracing/public/cpp/perfetto/perfetto_traced_process.h"
 #include "third_party/perfetto/protos/perfetto/trace/chrome/chrome_metadata.pbzero.h"
+#include "third_party/perfetto/protos/perfetto/trace/chrome/chrome_trace_event.pbzero.h"
 
 namespace perfetto {
 class StartupTraceWriter;
@@ -60,7 +61,9 @@
       perfetto::protos::pbzero::ChromeMetadataPacket*,
       bool /* privacy_filtering_enabled */)>;
 
-  // Any callbacks passed here will be called when tracing starts.
+  // Any callbacks passed here will be called when tracing. Note that if tracing
+  // is enabled while calling this method, the callback may be invoked
+  // directly.
   void AddGeneratorFunction(JsonMetadataGeneratorFunction generator);
   // Same as above, but for filling in proto format.
   void AddGeneratorFunction(MetadataGeneratorFunction generator);
@@ -81,15 +84,29 @@
   TraceEventMetadataSource();
   ~TraceEventMetadataSource() override;
 
-  void GenerateMetadata(std::unique_ptr<perfetto::TraceWriter> trace_writer);
+  void GenerateMetadata(
+      std::unique_ptr<std::vector<JsonMetadataGeneratorFunction>>
+          json_generators,
+      std::unique_ptr<std::vector<MetadataGeneratorFunction>> proto_generators);
+  void GenerateMetadataFromGenerator(
+      const MetadataGeneratorFunction& generator);
+  void GenerateJsonMetadataFromGenerator(
+      const JsonMetadataGeneratorFunction& generator,
+      perfetto::protos::pbzero::ChromeEventBundle* event_bundle);
   std::unique_ptr<base::DictionaryValue> GenerateTraceConfigMetadataDict();
 
+  // All members are protected by |lock_|.
+  base::Lock lock_;
   std::vector<JsonMetadataGeneratorFunction> json_generator_functions_;
   std::vector<MetadataGeneratorFunction> generator_functions_;
-  scoped_refptr<base::SequencedTaskRunner> origin_task_runner_;
+
+  const scoped_refptr<base::SequencedTaskRunner> origin_task_runner_;
+
   std::unique_ptr<perfetto::TraceWriter> trace_writer_;
   bool privacy_filtering_enabled_ = false;
   std::string chrome_config_;
+  std::unique_ptr<base::trace_event::TraceConfig> parsed_chrome_config_;
+  bool emit_metadata_at_start_ = false;
 
   DISALLOW_COPY_AND_ASSIGN(TraceEventMetadataSource);
 };
diff --git a/services/tracing/public/cpp/perfetto/trace_event_data_source_unittest.cc b/services/tracing/public/cpp/perfetto/trace_event_data_source_unittest.cc
index 2fe1f1f..4e773b40 100644
--- a/services/tracing/public/cpp/perfetto/trace_event_data_source_unittest.cc
+++ b/services/tracing/public/cpp/perfetto/trace_event_data_source_unittest.cc
@@ -486,21 +486,22 @@
   NOTREACHED();
 }
 
-TEST_F(TraceEventDataSourceTest, MetadataSourceBasicTypes) {
+std::unique_ptr<base::DictionaryValue> AddJsonMetadataGenerator() {
+  auto metadata = std::make_unique<base::DictionaryValue>();
+  metadata->SetInteger("foo_int", 42);
+  metadata->SetString("foo_str", "bar");
+  metadata->SetBoolean("foo_bool", true);
+
+  auto child_dict = std::make_unique<base::DictionaryValue>();
+  child_dict->SetString("child_str", "child_val");
+  metadata->Set("child_dict", std::move(child_dict));
+  return metadata;
+}
+
+TEST_F(TraceEventDataSourceTest, MetadataGeneratorBeforeTracing) {
   auto* metadata_source = TraceEventMetadataSource::GetInstance();
-  metadata_source->AddGeneratorFunction(base::BindRepeating([]() {
-    auto metadata = std::make_unique<base::DictionaryValue>();
-    metadata->SetInteger("foo_int", 42);
-    metadata->SetString("foo_str", "bar");
-    metadata->SetBoolean("foo_bool", true);
-
-    auto child_dict = std::make_unique<base::DictionaryValue>();
-    child_dict->SetString("child_str", "child_val");
-    metadata->Set("child_dict", std::move(child_dict));
-    return metadata;
-  }));
-
-  CreateTraceEventDataSource();
+  metadata_source->AddGeneratorFunction(
+      base::BindRepeating(&AddJsonMetadataGenerator));
 
   metadata_source->StartTracing(producer_client(),
                                 perfetto::DataSourceConfig());
@@ -520,6 +521,61 @@
   MetadataHasNamedValue(metadata, "child_dict", *child_dict);
 }
 
+TEST_F(TraceEventDataSourceTest, MetadataGeneratorWhileTracing) {
+  auto* metadata_source = TraceEventMetadataSource::GetInstance();
+
+  metadata_source->StartTracing(producer_client(),
+                                perfetto::DataSourceConfig());
+  metadata_source->AddGeneratorFunction(
+      base::BindRepeating(&AddJsonMetadataGenerator));
+
+  base::RunLoop wait_for_stop;
+  metadata_source->StopTracing(wait_for_stop.QuitClosure());
+  wait_for_stop.Run();
+
+  auto metadata = producer_client()->GetChromeMetadata();
+  EXPECT_EQ(4, metadata.size());
+  MetadataHasNamedValue(metadata, "foo_int", 42);
+  MetadataHasNamedValue(metadata, "foo_str", "bar");
+  MetadataHasNamedValue(metadata, "foo_bool", true);
+
+  auto child_dict = std::make_unique<base::DictionaryValue>();
+  child_dict->SetString("child_str", "child_val");
+  MetadataHasNamedValue(metadata, "child_dict", *child_dict);
+}
+
+TEST_F(TraceEventDataSourceTest, MultipleMetadataGenerators) {
+  auto* metadata_source = TraceEventMetadataSource::GetInstance();
+  metadata_source->AddGeneratorFunction(base::BindRepeating([]() {
+    auto metadata = std::make_unique<base::DictionaryValue>();
+    metadata->SetInteger("before_int", 42);
+    return metadata;
+  }));
+
+  metadata_source->StartTracing(producer_client(),
+                                perfetto::DataSourceConfig());
+  metadata_source->AddGeneratorFunction(
+      base::BindRepeating(&AddJsonMetadataGenerator));
+
+  base::RunLoop wait_for_stop;
+  metadata_source->StopTracing(wait_for_stop.QuitClosure());
+  wait_for_stop.Run();
+
+  auto metadata = producer_client()->GetChromeMetadata();
+  EXPECT_EQ(4, metadata.size());
+  MetadataHasNamedValue(metadata, "foo_int", 42);
+  MetadataHasNamedValue(metadata, "foo_str", "bar");
+  MetadataHasNamedValue(metadata, "foo_bool", true);
+
+  auto child_dict = std::make_unique<base::DictionaryValue>();
+  child_dict->SetString("child_str", "child_val");
+  MetadataHasNamedValue(metadata, "child_dict", *child_dict);
+
+  metadata = producer_client()->GetChromeMetadata(1);
+  EXPECT_EQ(1, metadata.size());
+  MetadataHasNamedValue(metadata, "before_int", 42);
+}
+
 TEST_F(TraceEventDataSourceTest, BasicTraceEvent) {
   CreateTraceEventDataSource();
 
diff --git a/storage/browser/blob/blob_reader.h b/storage/browser/blob/blob_reader.h
index aa89fcb..fb8f1f3 100644
--- a/storage/browser/blob/blob_reader.h
+++ b/storage/browser/blob/blob_reader.h
@@ -48,7 +48,7 @@
 // Use a BlobDataHandle to create an instance.
 //
 // For more information on how to read Blobs in your specific situation, see:
-// https://chromium.googlesource.com/chromium/src/+/HEAD/storage/browser/blob/README.md#accessing-reading
+// https://chromium.googlesource.com/chromium/src/+/HEAD/storage/browser/blob/README.md#how-to-use-blobs-browser_side-accessing-reading
 class COMPONENT_EXPORT(STORAGE_BROWSER) BlobReader {
  public:
   class COMPONENT_EXPORT(STORAGE_BROWSER) FileStreamReaderProvider {
diff --git a/testing/buildbot/filters/bfcache.android_browsertests.filter b/testing/buildbot/filters/bfcache.android_browsertests.filter
index 28affa3..1873169 100644
--- a/testing/buildbot/filters/bfcache.android_browsertests.filter
+++ b/testing/buildbot/filters/bfcache.android_browsertests.filter
@@ -8,3 +8,5 @@
 -PaymentHandlerEnableDelegationsTest.*
 -PaymentHandlerExploitTest.*
 -PaymentRequestCanMakePaymentQueryCCTest.*
+-PaymentRequestCanMakePaymentQueryPMITest.*
+-PaymentRequestCanMakePaymentQueryTest.*
diff --git a/testing/variations/fieldtrial_testing_config.json b/testing/variations/fieldtrial_testing_config.json
index 4d806ff0..fbcfbf1 100644
--- a/testing/variations/fieldtrial_testing_config.json
+++ b/testing/variations/fieldtrial_testing_config.json
@@ -3094,21 +3094,6 @@
             ]
         }
     ],
-    "InterestFeedContentSuggestions": [
-        {
-            "platforms": [
-                "android"
-            ],
-            "experiments": [
-                {
-                    "name": "Enabled",
-                    "enable_features": [
-                        "InterestFeedContentSuggestions"
-                    ]
-                }
-            ]
-        }
-    ],
     "InvalidationsGCMUpstream": [
         {
             "platforms": [
@@ -6403,6 +6388,21 @@
             ]
         }
     ],
+    "UseSkiaRenderer": [
+        {
+            "platforms": [
+                "linux"
+            ],
+            "experiments": [
+                {
+                    "name": "UseSkiaRenderer",
+                    "enable_features": [
+                        "UseSkiaRenderer"
+                    ]
+                }
+            ]
+        }
+    ],
     "UserActivityEventLogging": [
         {
             "platforms": [
diff --git a/third_party/blink/OWNERS b/third_party/blink/OWNERS
index 19c3e85..770dfb0 100644
--- a/third_party/blink/OWNERS
+++ b/third_party/blink/OWNERS
@@ -10,7 +10,6 @@
 yoavweiss@chromium.org
 
 # For *.gn* changes only.
-dpranke@chromium.org
 thakis@chromium.org
 
 per-file PRESUBMIT*.py=dcheng@chromium.org
diff --git a/third_party/blink/common/BUILD.gn b/third_party/blink/common/BUILD.gn
index 338bdb53..cf273d4 100644
--- a/third_party/blink/common/BUILD.gn
+++ b/third_party/blink/common/BUILD.gn
@@ -75,6 +75,7 @@
     "origin_policy/origin_policy_parser.h",
     "origin_trials/trial_token.cc",
     "origin_trials/trial_token_validator.cc",
+    "page/page_zoom.cc",
     "peerconnection/webrtc_ip_handling_policy.cc",
     "privacy_preferences.cc",
     "scheduler/web_scheduler_tracked_feature.cc",
diff --git a/third_party/blink/common/page/page_zoom.cc b/third_party/blink/common/page/page_zoom.cc
new file mode 100644
index 0000000..a372e0a
--- /dev/null
+++ b/third_party/blink/common/page/page_zoom.cc
@@ -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.
+
+#include "third_party/blink/public/common/page/page_zoom.h"
+
+#include <cmath>
+
+namespace blink {
+
+// The minimum and maximum amount of page zoom that is possible, independent
+// of other factors such as device scale and page scale (pinch). Historically,
+// these values came from WebKitLegacy/mac/WebView/WebView.mm where they are
+// named MinimumZoomMultiplier and MaximumZoomMultiplier. But chromium has
+// changed to use different limits.
+const double kMinimumPageZoomFactor = 0.25;
+const double kMaximumPageZoomFactor = 5.0;
+
+// Change the zoom factor by 20% for each zoom level increase from the user.
+// Historically, this value came from WebKit in
+// WebKitLegacy/mac/WebView/WebView.mm (named as ZoomMultiplierRatio there).
+static const double kTextSizeMultiplierRatio = 1.2;
+
+double PageZoomLevelToZoomFactor(double zoom_level) {
+  return std::pow(kTextSizeMultiplierRatio, zoom_level);
+}
+
+double PageZoomFactorToZoomLevel(double factor) {
+  return std::log(factor) / std::log(kTextSizeMultiplierRatio);
+}
+
+bool PageZoomValuesEqual(double value_a, double value_b) {
+  // Epsilon value for comparing two floating-point zoom values. We don't use
+  // std::numeric_limits<> because it is too precise for zoom values. Zoom
+  // values lose precision due to factor/level conversions. A value of 0.001
+  // is precise enough for zoom value comparisons.
+  const double kPageZoomEpsilon = 0.001;
+  return (std::fabs(value_a - value_b) <= kPageZoomEpsilon);
+}
+
+}  // namespace blink
diff --git a/third_party/blink/public/common/BUILD.gn b/third_party/blink/public/common/BUILD.gn
index 5d6e79c..6747496 100644
--- a/third_party/blink/public/common/BUILD.gn
+++ b/third_party/blink/public/common/BUILD.gn
@@ -50,6 +50,7 @@
     "client_hints/client_hints.h",
     "common_export.h",
     "css/forced_colors.h",
+    "css/navigation_controls.h",
     "css/preferred_color_scheme.h",
     "device_memory/approximated_device_memory.h",
     "dom_storage/session_storage_namespace_id.h",
@@ -109,6 +110,7 @@
     "origin_trials/trial_token.h",
     "origin_trials/trial_token_validator.h",
     "page/launching_process_state.h",
+    "page/page_zoom.h",
     "peerconnection/webrtc_ip_handling_policy.h",
     "prerender/prerender_rel_type.h",
     "privacy_preferences.h",
diff --git a/third_party/blink/public/common/css/navigation_controls.h b/third_party/blink/public/common/css/navigation_controls.h
new file mode 100644
index 0000000..a1c789e
--- /dev/null
+++ b/third_party/blink/public/common/css/navigation_controls.h
@@ -0,0 +1,18 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_PUBLIC_COMMON_CSS_NAVIGATION_CONTROLS_H_
+#define THIRD_PARTY_BLINK_PUBLIC_COMMON_CSS_NAVIGATION_CONTROLS_H_
+
+namespace blink {
+
+// Use for passing navigation controls from the OS to the renderer.
+enum class NavigationControls {
+  kNone,
+  kBackButton,
+};
+
+}  // namespace blink
+
+#endif  // THIRD_PARTY_BLINK_PUBLIC_COMMON_CSS_NAVIGATION_CONTROLS_H_
diff --git a/third_party/blink/public/common/page/page_zoom.h b/third_party/blink/public/common/page/page_zoom.h
new file mode 100644
index 0000000..cc6b49e
--- /dev/null
+++ b/third_party/blink/public/common/page/page_zoom.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 THIRD_PARTY_BLINK_PUBLIC_COMMON_PAGE_PAGE_ZOOM_H_
+#define THIRD_PARTY_BLINK_PUBLIC_COMMON_PAGE_PAGE_ZOOM_H_
+
+#include "third_party/blink/public/common/common_export.h"
+
+namespace blink {
+
+// The minimum and maximum page zoom factors that are allowed.
+BLINK_COMMON_EXPORT extern const double kMinimumPageZoomFactor;
+BLINK_COMMON_EXPORT extern const double kMaximumPageZoomFactor;
+
+// Convert between page zoom factors and levels.
+BLINK_COMMON_EXPORT double PageZoomLevelToZoomFactor(double zoom_level);
+BLINK_COMMON_EXPORT double PageZoomFactorToZoomLevel(double factor);
+
+// Use this to compare page zoom factors and levels. It accounts for precision
+// loss due to conversions back and forth.
+BLINK_COMMON_EXPORT bool PageZoomValuesEqual(double value_a, double value_b);
+
+}  // namespace blink
+
+#endif  // THIRD_PARTY_BLINK_PUBLIC_COMMON_PAGE_PAGE_ZOOM_H_
diff --git a/third_party/blink/public/strings/blink_strings.grd b/third_party/blink/public/strings/blink_strings.grd
index d3ecd98..f80f7f79 100644
--- a/third_party/blink/public/strings/blink_strings.grd
+++ b/third_party/blink/public/strings/blink_strings.grd
@@ -246,6 +246,21 @@
       </message>
 
       <!-- Localized names for accessibility roles -->
+      <message name="IDS_AX_ROLE_ANNOTATION_ATTRIBUTION" desc="Accessibility role description for attribution, meaning authoring information tied to specific content">
+        authoring info
+      </message>
+      <message name="IDS_AX_ROLE_ANNOTATION_COMMENTARY" desc="Accessibility role description for commentary">
+        comments
+      </message>
+      <message name="IDS_AX_ROLE_ANNOTATION_PRESENCE" desc="Accessibility role description for presence, meaning information about another collaborator who is currently reviewing or editing this content">
+        live presence
+      </message>
+      <message name="IDS_AX_ROLE_ANNOTATION_REVISION" desc="Accessibility role description for revision, meaining historical change info tied to this content">
+        revision
+      </message>
+      <message name="IDS_AX_ROLE_ANNOTATION_SUGGESTION" desc="Accessibility role description for suggestion, meaning a suggested change to some content">
+        suggestion
+      </message>
       <message name="IDS_AX_ROLE_ARTICLE" desc="Accessibility role description for article">
         article
       </message>
@@ -266,7 +281,7 @@
       <message name="IDS_AX_ROLE_CONTENT_DELETION" desc="Accessibility role description for content deletion, meaning content that is has been or is suggested to be removed from a document, such as in a revision review">
         deletion
       </message>
-      <message name="IDS_AX_ROLE_CONTENT_INSERTION" desc="Accessibility role description for content insertion, meaning content that has been marked or is suggested to be inserte nto a document, such as in a revision review">
+      <message name="IDS_AX_ROLE_CONTENT_INSERTION" desc="Accessibility role description for content insertion, meaning content that has been marked or is suggested to be inserted into a document, such as in a revision review">
         insertion
       </message>
       <message name="IDS_AX_ROLE_CHECK_BOX" desc="Accessibility role description for a check box">
diff --git a/third_party/blink/public/web/web_ax_object.h b/third_party/blink/public/web/web_ax_object.h
index 7d946ae..2c70318 100644
--- a/third_party/blink/public/web/web_ax_object.h
+++ b/third_party/blink/public/web/web_ax_object.h
@@ -158,7 +158,7 @@
   BLINK_EXPORT void ColorValue(int& r, int& g, int& b) const;
   BLINK_EXPORT unsigned ColorValue() const;
   BLINK_EXPORT WebAXObject AriaActiveDescendant() const;
-  BLINK_EXPORT WebString AriaAutoComplete() const;
+  BLINK_EXPORT WebString AutoComplete() const;
   BLINK_EXPORT ax::mojom::AriaCurrentState AriaCurrentState() const;
   BLINK_EXPORT ax::mojom::HasPopup HasPopup() const;
   BLINK_EXPORT bool IsEditableRoot() const;
diff --git a/third_party/blink/public/web/web_settings.h b/third_party/blink/public/web/web_settings.h
index a5d170e..f50cff6a 100644
--- a/third_party/blink/public/web/web_settings.h
+++ b/third_party/blink/public/web/web_settings.h
@@ -34,6 +34,7 @@
 #include <unicode/uscript.h>
 
 #include "third_party/blink/public/common/css/forced_colors.h"
+#include "third_party/blink/public/common/css/navigation_controls.h"
 #include "third_party/blink/public/common/css/preferred_color_scheme.h"
 #include "third_party/blink/public/platform/pointer_properties.h"
 #include "third_party/blink/public/platform/web_common.h"
@@ -298,6 +299,7 @@
   virtual void SetForceDarkModeEnabled(bool) = 0;
   virtual void SetPreferredColorScheme(PreferredColorScheme) = 0;
   virtual void SetForcedColors(ForcedColors) = 0;
+  virtual void SetNavigationControls(NavigationControls) = 0;
 
  protected:
   ~WebSettings() = default;
diff --git a/third_party/blink/public/web/web_view.h b/third_party/blink/public/web/web_view.h
index 697771f..4f6761c 100644
--- a/third_party/blink/public/web/web_view.h
+++ b/third_party/blink/public/web/web_view.h
@@ -201,11 +201,6 @@
   // change.
   virtual double SetZoomLevel(double) = 0;
 
-  // Helper functions to convert between zoom level and zoom factor.  zoom
-  // factor is zoom percent / 100, so 300% = 3.0.
-  BLINK_EXPORT static double ZoomLevelToZoomFactor(double zoom_level);
-  BLINK_EXPORT static double ZoomFactorToZoomLevel(double factor);
-
   // Returns the current text zoom factor, where 1.0 is the normal size, > 1.0
   // is scaled up and < 1.0 is scaled down.
   virtual float TextZoomFactor() = 0;
diff --git a/third_party/blink/renderer/core/css/css.dict b/third_party/blink/renderer/core/css/css.dict
index ec5c79a..c804566 100644
--- a/third_party/blink/renderer/core/css/css.dict
+++ b/third_party/blink/renderer/core/css/css.dict
@@ -915,6 +915,7 @@
 "active"
 "linktext"
 "visitedtext"
+"back-button"
 
 # at-rules
 "@charset"
diff --git a/third_party/blink/renderer/core/css/css_value_keywords.json5 b/third_party/blink/renderer/core/css/css_value_keywords.json5
index 205af75..1010ca92 100644
--- a/third_party/blink/renderer/core/css/css_value_keywords.json5
+++ b/third_party/blink/renderer/core/css/css_value_keywords.json5
@@ -1205,5 +1205,9 @@
     // (forced-colors:) media feature
     // none
     "active",
+
+    // (navigation-controls) media feature
+    // none
+    "back-button",
   ],
 }
diff --git a/third_party/blink/renderer/core/css/media_feature_names.json5 b/third_party/blink/renderer/core/css/media_feature_names.json5
index ad3352a2..b78aa492 100644
--- a/third_party/blink/renderer/core/css/media_feature_names.json5
+++ b/third_party/blink/renderer/core/css/media_feature_names.json5
@@ -48,6 +48,7 @@
     "min-monochrome",
     "min-width",
     "min-resolution",
+    "navigation-controls",
     "pointer",
     "prefers-color-scheme",
     "prefers-reduced-motion",
diff --git a/third_party/blink/renderer/core/css/media_query_evaluator.cc b/third_party/blink/renderer/core/css/media_query_evaluator.cc
index 9c90b9d..6a9cf0ae 100644
--- a/third_party/blink/renderer/core/css/media_query_evaluator.cc
+++ b/third_party/blink/renderer/core/css/media_query_evaluator.cc
@@ -30,6 +30,7 @@
 #include "third_party/blink/renderer/core/css/media_query_evaluator.h"
 
 #include "third_party/blink/public/common/css/forced_colors.h"
+#include "third_party/blink/public/common/css/navigation_controls.h"
 #include "third_party/blink/public/common/css/preferred_color_scheme.h"
 #include "third_party/blink/public/common/manifest/web_display_mode.h"
 #include "third_party/blink/public/platform/pointer_properties.h"
@@ -867,6 +868,25 @@
           value.id == CSSValueID::kActive);
 }
 
+static bool NavigationControlsMediaFeatureEval(
+    const MediaQueryExpValue& value,
+    MediaFeaturePrefix,
+    const MediaValues& media_values) {
+  NavigationControls navigation_controls = media_values.GetNavigationControls();
+
+  if (!value.IsValid())
+    return navigation_controls != NavigationControls::kNone;
+
+  if (!value.is_id)
+    return false;
+
+  // Check the navigation controls against value.id.
+  return (navigation_controls == NavigationControls::kNone &&
+          value.id == CSSValueID::kNone) ||
+         (navigation_controls == NavigationControls::kBackButton &&
+          value.id == CSSValueID::kBackButton);
+}
+
 void MediaQueryEvaluator::Init() {
   // Create the table.
   g_function_map = new FunctionMap;
diff --git a/third_party/blink/renderer/core/css/media_query_evaluator_test.cc b/third_party/blink/renderer/core/css/media_query_evaluator_test.cc
index 1a00fa9..a45ab5d 100644
--- a/third_party/blink/renderer/core/css/media_query_evaluator_test.cc
+++ b/third_party/blink/renderer/core/css/media_query_evaluator_test.cc
@@ -186,6 +186,18 @@
     {nullptr, 0}  // Do not remove the terminator line.
 };
 
+MediaQueryEvaluatorTestCase g_navigationcontrols_back_button_cases[] = {
+    {"(navigation-controls: back-button)", 1},
+    {"(navigation-controls: none)", 0},
+    {nullptr, 0}  // Do not remove the terminator line.
+};
+
+MediaQueryEvaluatorTestCase g_navigationcontrols_none_cases[] = {
+    {"(navigation-controls: back-button)", 0},
+    {"(navigation-controls: none)", 1},
+    {nullptr, 0}  // Do not remove the terminator line.
+};
+
 void TestMQEvaluator(MediaQueryEvaluatorTestCase* test_cases,
                      const MediaQueryEvaluator& media_query_evaluator,
                      CSSParserMode mode) {
diff --git a/third_party/blink/renderer/core/css/media_query_exp.cc b/third_party/blink/renderer/core/css/media_query_exp.cc
index 07350e9..07cea4a6 100644
--- a/third_party/blink/renderer/core/css/media_query_exp.cc
+++ b/third_party/blink/renderer/core/css/media_query_exp.cc
@@ -94,6 +94,12 @@
     }
   }
 
+  if (RuntimeEnabledFeatures::MediaQueryNavigationControlsEnabled()) {
+    if (media_feature == kNavigationControlsMediaFeature) {
+      return ident == CSSValueID::kNone || ident == CSSValueID::kBackButton;
+    }
+  }
+
   return false;
 }
 
@@ -205,7 +211,8 @@
          media_feature == kImmersiveMediaFeature ||
          media_feature == kPrefersColorSchemeMediaFeature ||
          media_feature == kPrefersReducedMotionMediaFeature ||
-         media_feature == kForcedColorsMediaFeature;
+         media_feature == kForcedColorsMediaFeature ||
+         media_feature == kNavigationControlsMediaFeature;
 }
 
 bool MediaQueryExp::IsViewportDependent() const {
diff --git a/third_party/blink/renderer/core/css/media_values.cc b/third_party/blink/renderer/core/css/media_values.cc
index 63f27de..5c2184f 100644
--- a/third_party/blink/renderer/core/css/media_values.cc
+++ b/third_party/blink/renderer/core/css/media_values.cc
@@ -223,6 +223,12 @@
   return frame->GetDocument()->GetStyleEngine().GetForcedColors();
 }
 
+NavigationControls MediaValues::CalculateNavigationControls(LocalFrame* frame) {
+  DCHECK(frame);
+  DCHECK(frame->GetSettings());
+  return frame->GetSettings()->GetNavigationControls();
+}
+
 bool MediaValues::ComputeLengthImpl(double value,
                                     CSSPrimitiveValue::UnitType type,
                                     unsigned default_font_size,
diff --git a/third_party/blink/renderer/core/css/media_values.h b/third_party/blink/renderer/core/css/media_values.h
index ef3a352..fc16809 100644
--- a/third_party/blink/renderer/core/css/media_values.h
+++ b/third_party/blink/renderer/core/css/media_values.h
@@ -21,6 +21,7 @@
 enum class ColorSpaceGamut;
 enum class PreferredColorScheme;
 enum class ForcedColors;
+enum class NavigationControls;
 
 PreferredColorScheme CSSValueIDToPreferredColorScheme(CSSValueID id);
 
@@ -84,6 +85,7 @@
   virtual PreferredColorScheme GetPreferredColorScheme() const = 0;
   virtual bool PrefersReducedMotion() const = 0;
   virtual ForcedColors GetForcedColors() const = 0;
+  virtual NavigationControls GetNavigationControls() const = 0;
 
  protected:
   static double CalculateViewportWidth(LocalFrame*);
@@ -108,6 +110,7 @@
   static PreferredColorScheme CalculatePreferredColorScheme(LocalFrame*);
   static bool CalculatePrefersReducedMotion(LocalFrame*);
   static ForcedColors CalculateForcedColors(LocalFrame*);
+  static NavigationControls CalculateNavigationControls(LocalFrame*);
 };
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/css/media_values_cached.cc b/third_party/blink/renderer/core/css/media_values_cached.cc
index 7da0eaf..1230d7a 100644
--- a/third_party/blink/renderer/core/css/media_values_cached.cc
+++ b/third_party/blink/renderer/core/css/media_values_cached.cc
@@ -5,6 +5,7 @@
 #include "third_party/blink/renderer/core/css/media_values_cached.h"
 
 #include "third_party/blink/public/common/css/forced_colors.h"
+#include "third_party/blink/public/common/css/navigation_controls.h"
 #include "third_party/blink/public/common/css/preferred_color_scheme.h"
 #include "third_party/blink/renderer/core/css/css_primitive_value.h"
 #include "third_party/blink/renderer/core/dom/document.h"
@@ -35,7 +36,8 @@
       color_gamut(ColorSpaceGamut::kUnknown),
       preferred_color_scheme(PreferredColorScheme::kNoPreference),
       prefers_reduced_motion(false),
-      forced_colors(ForcedColors::kNone) {}
+      forced_colors(ForcedColors::kNone),
+      navigation_controls(NavigationControls::kNone) {}
 
 MediaValuesCached::MediaValuesCachedData::MediaValuesCachedData(
     Document& document)
@@ -77,6 +79,7 @@
     preferred_color_scheme = MediaValues::CalculatePreferredColorScheme(frame);
     prefers_reduced_motion = MediaValues::CalculatePrefersReducedMotion(frame);
     forced_colors = MediaValues::CalculateForcedColors(frame);
+    navigation_controls = MediaValues::CalculateNavigationControls(frame);
   }
 }
 
@@ -203,4 +206,8 @@
   return data_.forced_colors;
 }
 
+NavigationControls MediaValuesCached::GetNavigationControls() const {
+  return data_.navigation_controls;
+}
+
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/css/media_values_cached.h b/third_party/blink/renderer/core/css/media_values_cached.h
index 3d123a7..97b5baf 100644
--- a/third_party/blink/renderer/core/css/media_values_cached.h
+++ b/third_party/blink/renderer/core/css/media_values_cached.h
@@ -39,6 +39,7 @@
     PreferredColorScheme preferred_color_scheme;
     bool prefers_reduced_motion;
     ForcedColors forced_colors;
+    NavigationControls navigation_controls;
 
     MediaValuesCachedData();
     explicit MediaValuesCachedData(Document&);
@@ -67,6 +68,7 @@
       data.preferred_color_scheme = preferred_color_scheme;
       data.prefers_reduced_motion = prefers_reduced_motion;
       data.forced_colors = forced_colors;
+      data.navigation_controls = navigation_controls;
       return data;
     }
   };
@@ -106,6 +108,7 @@
   PreferredColorScheme GetPreferredColorScheme() const override;
   bool PrefersReducedMotion() const override;
   ForcedColors GetForcedColors() const override;
+  NavigationControls GetNavigationControls() const override;
 
   void OverrideViewportDimensions(double width, double height) override;
 
diff --git a/third_party/blink/renderer/core/css/media_values_dynamic.cc b/third_party/blink/renderer/core/css/media_values_dynamic.cc
index 59488e7..658de89 100644
--- a/third_party/blink/renderer/core/css/media_values_dynamic.cc
+++ b/third_party/blink/renderer/core/css/media_values_dynamic.cc
@@ -5,6 +5,7 @@
 #include "third_party/blink/renderer/core/css/media_values_dynamic.h"
 
 #include "third_party/blink/public/common/css/forced_colors.h"
+#include "third_party/blink/public/common/css/navigation_controls.h"
 #include "third_party/blink/public/common/css/preferred_color_scheme.h"
 #include "third_party/blink/renderer/core/css/css_primitive_value.h"
 #include "third_party/blink/renderer/core/css/css_resolution_units.h"
@@ -155,6 +156,10 @@
   return CalculateForcedColors(frame_);
 }
 
+NavigationControls MediaValuesDynamic::GetNavigationControls() const {
+  return CalculateNavigationControls(frame_);
+}
+
 Document* MediaValuesDynamic::GetDocument() const {
   return frame_->GetDocument();
 }
diff --git a/third_party/blink/renderer/core/css/media_values_dynamic.h b/third_party/blink/renderer/core/css/media_values_dynamic.h
index 95c454e..636d2fc 100644
--- a/third_party/blink/renderer/core/css/media_values_dynamic.h
+++ b/third_party/blink/renderer/core/css/media_values_dynamic.h
@@ -51,6 +51,7 @@
   PreferredColorScheme GetPreferredColorScheme() const override;
   bool PrefersReducedMotion() const override;
   ForcedColors GetForcedColors() const override;
+  NavigationControls GetNavigationControls() const override;
   Document* GetDocument() const override;
   bool HasValues() const override;
   void OverrideViewportDimensions(double width, double height) override;
diff --git a/third_party/blink/renderer/core/css/style_engine_test.cc b/third_party/blink/renderer/core/css/style_engine_test.cc
index 5af643f6..7a9d910 100644
--- a/third_party/blink/renderer/core/css/style_engine_test.cc
+++ b/third_party/blink/renderer/core/css/style_engine_test.cc
@@ -8,6 +8,7 @@
 
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/blink/public/common/css/forced_colors.h"
+#include "third_party/blink/public/common/css/navigation_controls.h"
 #include "third_party/blink/public/common/css/preferred_color_scheme.h"
 #include "third_party/blink/public/platform/web_float_rect.h"
 #include "third_party/blink/renderer/bindings/core/v8/v8_binding_for_core.h"
@@ -1723,6 +1724,33 @@
                 GetCSSPropertyColor()));
 }
 
+TEST_F(StyleEngineTest, MediaQueriesChangeNavigationControls) {
+  ScopedMediaQueryNavigationControlsForTest scoped_feature(true);
+  GetDocument().body()->SetInnerHTMLFromString(R"HTML(
+    <style>
+      @media (navigation-controls: none) {
+        body { color: red }
+      }
+      @media (navigation-controls: back-button) {
+        body { color: green }
+      }
+    </style>
+    <body></body>
+  )HTML");
+
+  UpdateAllLifecyclePhases();
+  EXPECT_EQ(MakeRGB(255, 0, 0),
+            GetDocument().body()->GetComputedStyle()->VisitedDependentColor(
+                GetCSSPropertyColor()));
+
+  GetDocument().GetSettings()->SetNavigationControls(
+      NavigationControls::kBackButton);
+  UpdateAllLifecyclePhases();
+  EXPECT_EQ(MakeRGB(0, 128, 0),
+            GetDocument().body()->GetComputedStyle()->VisitedDependentColor(
+                GetCSSPropertyColor()));
+}
+
 TEST_F(StyleEngineTest, ShadowRootStyleRecalcCrash) {
   GetDocument().body()->SetInnerHTMLFromString("<div id=host></div>");
   auto* host = To<HTMLElement>(GetDocument().getElementById("host"));
diff --git a/third_party/blink/renderer/core/dom/node.cc b/third_party/blink/renderer/core/dom/node.cc
index d7bb3f9..f98fa7c5 100644
--- a/third_party/blink/renderer/core/dom/node.cc
+++ b/third_party/blink/renderer/core/dom/node.cc
@@ -615,6 +615,7 @@
   if (!GetDocument().GetPage()) {
     // We should always have a Page if we're scrolling. See
     // crbug.com/689074 for details.
+    NOTREACHED();
     return;
   }
 
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 4ac7aa7f..c0ef6e7 100644
--- a/third_party/blink/renderer/core/exported/web_settings_impl.cc
+++ b/third_party/blink/renderer/core/exported/web_settings_impl.cc
@@ -787,6 +787,11 @@
   settings_->SetForcedColors(forced_colors);
 }
 
+void WebSettingsImpl::SetNavigationControls(
+    NavigationControls navigation_controls) {
+  settings_->SetNavigationControls(navigation_controls);
+}
+
 STATIC_ASSERT_ENUM(WebSettings::ImageAnimationPolicy::kAllowed,
                    kImageAnimationPolicyAllowed);
 STATIC_ASSERT_ENUM(WebSettings::ImageAnimationPolicy::kAnimateOnce,
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 e42de18..6eb9c08 100644
--- a/third_party/blink/renderer/core/exported/web_settings_impl.h
+++ b/third_party/blink/renderer/core/exported/web_settings_impl.h
@@ -224,6 +224,7 @@
   void SetForceDarkModeEnabled(bool) override;
   void SetPreferredColorScheme(PreferredColorScheme) override;
   void SetForcedColors(ForcedColors) override;
+  void SetNavigationControls(NavigationControls) override;
 
   bool RenderVSyncNotificationEnabled() const {
     return render_v_sync_notification_enabled_;
diff --git a/third_party/blink/renderer/core/exported/web_view_impl.cc b/third_party/blink/renderer/core/exported/web_view_impl.cc
index a88aab76..618f45d 100644
--- a/third_party/blink/renderer/core/exported/web_view_impl.cc
+++ b/third_party/blink/renderer/core/exported/web_view_impl.cc
@@ -39,6 +39,7 @@
 #include "build/build_config.h"
 #include "cc/layers/picture_layer.h"
 #include "third_party/blink/public/common/features.h"
+#include "third_party/blink/public/common/page/page_zoom.h"
 #include "third_party/blink/public/platform/web_float_point.h"
 #include "third_party/blink/public/platform/web_image.h"
 #include "third_party/blink/public/platform/web_input_event.h"
@@ -197,17 +198,11 @@
 static const float leftBoxRatio = 0.3f;
 static const int caretPadding = 10;
 
-// Mirrored from content/common/page_zoom.cc.
-const double kMinimumPageZoomFactor = 0.25;
-const double kMaximumPageZoomFactor = 5.0;
-
 namespace blink {
 
-// Change the text zoom level by kTextSizeMultiplierRatio each time the user
-// zooms text in or out (ie., change by 20%).  The min and max values limit
-// text zoom to half and 3x the original text size.  These three values match
-// those in Apple's port in WebKit/WebKit/WebView/WebView.mm
-const double WebView::kTextSizeMultiplierRatio = 1.2;
+// Historically, these values came from Webkit in
+// WebKitLegacy/mac/WebView/WebView.mm (named MinimumZoomMultiplier and
+// MaximumZoomMultiplier there).
 const double WebView::kMinTextSizeMultiplier = 0.5;
 const double WebView::kMaxTextSizeMultiplier = 3.0;
 
@@ -280,8 +275,8 @@
                          WebViewImpl* opener)
     : as_view_(client),
       chrome_client_(MakeGarbageCollected<ChromeClientImpl>(this)),
-      minimum_zoom_level_(ZoomFactorToZoomLevel(kMinimumPageZoomFactor)),
-      maximum_zoom_level_(ZoomFactorToZoomLevel(kMaximumPageZoomFactor)),
+      minimum_zoom_level_(PageZoomFactorToZoomLevel(kMinimumPageZoomFactor)),
+      maximum_zoom_level_(PageZoomFactorToZoomLevel(kMaximumPageZoomFactor)),
       does_composite_(does_composite),
       fullscreen_controller_(std::make_unique<FullscreenController>(this)) {
   if (!AsView().client) {
@@ -2389,7 +2384,7 @@
   float zoom_factor =
       zoom_factor_override_
           ? zoom_factor_override_
-          : static_cast<float>(ZoomLevelToZoomFactor(zoom_level_));
+          : static_cast<float>(PageZoomLevelToZoomFactor(zoom_level_));
   if (zoom_factor_for_device_scale_factor_) {
     if (compositor_device_scale_factor_override_) {
       // Adjust the page's DSF so that DevicePixelRatio becomes
@@ -2422,15 +2417,6 @@
   return text_zoom_factor;
 }
 
-double WebView::ZoomLevelToZoomFactor(double zoom_level) {
-  return pow(kTextSizeMultiplierRatio, zoom_level);
-}
-
-double WebView::ZoomFactorToZoomLevel(double factor) {
-  // Since factor = 1.2^level, level = log(factor) / log(1.2)
-  return log(factor) / log(kTextSizeMultiplierRatio);
-}
-
 float WebViewImpl::PageScaleFactor() const {
   if (!GetPage())
     return 1;
diff --git a/third_party/blink/renderer/core/exported/web_view_test.cc b/third_party/blink/renderer/core/exported/web_view_test.cc
index c9a944b..5b26311 100644
--- a/third_party/blink/renderer/core/exported/web_view_test.cc
+++ b/third_party/blink/renderer/core/exported/web_view_test.cc
@@ -48,6 +48,7 @@
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/blink/public/common/frame/frame_owner_element_type.h"
 #include "third_party/blink/public/common/manifest/web_display_mode.h"
+#include "third_party/blink/public/common/page/page_zoom.h"
 #include "third_party/blink/public/mojom/frame/document_interface_broker.mojom-blink.h"
 #include "third_party/blink/public/platform/web_coalesced_input_event.h"
 #include "third_party/blink/public/platform/web_cursor_info.h"
@@ -4250,19 +4251,19 @@
   EXPECT_EQ(100, size.width);
   EXPECT_EQ(100, size.height);
 
-  web_view->SetZoomLevel(WebView::ZoomFactorToZoomLevel(2.0));
+  web_view->SetZoomLevel(PageZoomFactorToZoomLevel(2.0));
   size = web_view->ContentsPreferredMinimumSize();
   EXPECT_EQ(200, size.width);
   EXPECT_EQ(200, size.height);
 
   // Verify that both width and height are rounded (in this case up)
-  web_view->SetZoomLevel(WebView::ZoomFactorToZoomLevel(0.9995));
+  web_view->SetZoomLevel(PageZoomFactorToZoomLevel(0.9995));
   size = web_view->ContentsPreferredMinimumSize();
   EXPECT_EQ(100, size.width);
   EXPECT_EQ(100, size.height);
 
   // Verify that both width and height are rounded (in this case down)
-  web_view->SetZoomLevel(WebView::ZoomFactorToZoomLevel(1.0005));
+  web_view->SetZoomLevel(PageZoomFactorToZoomLevel(1.0005));
   size = web_view->ContentsPreferredMinimumSize();
   EXPECT_EQ(100, size.width);
   EXPECT_EQ(100, size.height);
@@ -4272,7 +4273,7 @@
       ToKURL(url), test::CoreTestDataPath("specify_size.html"));
   web_view = web_view_helper_.InitializeAndLoad(url);
 
-  web_view->SetZoomLevel(WebView::ZoomFactorToZoomLevel(1));
+  web_view->SetZoomLevel(PageZoomFactorToZoomLevel(1));
   size = web_view->ContentsPreferredMinimumSize();
   EXPECT_EQ(2, size.width);
   EXPECT_EQ(2, size.height);
@@ -5032,7 +5033,7 @@
 TEST_F(WebViewTest, WidthMediaQueryWithPageZoomAfterPrinting) {
   WebViewImpl* web_view = web_view_helper_.Initialize();
   web_view->MainFrameWidget()->Resize(WebSize(800, 600));
-  web_view->SetZoomLevel(WebView::ZoomFactorToZoomLevel(2.0));
+  web_view->SetZoomLevel(PageZoomFactorToZoomLevel(2.0));
 
   WebURL base_url = url_test_helpers::ToKURL("http://example.com/");
   frame_test_helpers::LoadHTMLString(web_view->MainFrameImpl(),
@@ -5067,7 +5068,7 @@
 TEST_F(WebViewTest, ViewportUnitsPrintingWithPageZoom) {
   WebViewImpl* web_view = web_view_helper_.Initialize();
   web_view->MainFrameWidget()->Resize(WebSize(800, 600));
-  web_view->SetZoomLevel(WebView::ZoomFactorToZoomLevel(2.0));
+  web_view->SetZoomLevel(PageZoomFactorToZoomLevel(2.0));
 
   WebURL base_url = url_test_helpers::ToKURL("http://example.com/");
   frame_test_helpers::LoadHTMLString(web_view->MainFrameImpl(),
diff --git a/third_party/blink/renderer/core/frame/settings.h b/third_party/blink/renderer/core/frame/settings.h
index 5c40911..99cca84d 100644
--- a/third_party/blink/renderer/core/frame/settings.h
+++ b/third_party/blink/renderer/core/frame/settings.h
@@ -32,6 +32,7 @@
 
 #include "base/macros.h"
 #include "third_party/blink/public/common/css/forced_colors.h"
+#include "third_party/blink/public/common/css/navigation_controls.h"
 #include "third_party/blink/public/common/css/preferred_color_scheme.h"
 #include "third_party/blink/public/common/manifest/web_display_mode.h"
 #include "third_party/blink/public/platform/pointer_properties.h"
diff --git a/third_party/blink/renderer/core/frame/settings.json5 b/third_party/blink/renderer/core/frame/settings.json5
index a3eacd0..36c7346c 100644
--- a/third_party/blink/renderer/core/frame/settings.json5
+++ b/third_party/blink/renderer/core/frame/settings.json5
@@ -1082,5 +1082,13 @@
       invalidate: "ColorScheme",
       type: "ForcedColors",
     },
+    // Navigation controls from the application passed to the renderer for
+    // evaluating the navigation-controls media query.
+    {
+      name: "navigationControls",
+      initial: "NavigationControls::kNone",
+      invalidate: "MediaQuery",
+      type: "NavigationControls",
+    },
   ],
 }
diff --git a/third_party/blink/renderer/core/html/canvas/canvas_rendering_context.cc b/third_party/blink/renderer/core/html/canvas/canvas_rendering_context.cc
index 62f2f2c..89ae1d2 100644
--- a/third_party/blink/renderer/core/html/canvas/canvas_rendering_context.cc
+++ b/third_party/blink/renderer/core/html/canvas/canvas_rendering_context.cc
@@ -124,9 +124,6 @@
     const base::PendingTask& /* pending_task */) {
   StopListeningForDidProcessTask();
 
-  // Call FinalizeFrame() on self first before informing host so that we can
-  // present swap chain before exporting it.
-  FinalizeFrame();
   // The end of a script task that drew content to the canvas is the point
   // at which the current frame may be considered complete.
   if (Host())
diff --git a/third_party/blink/renderer/core/html/canvas/canvas_rendering_context.h b/third_party/blink/renderer/core/html/canvas/canvas_rendering_context.h
index 35c2af8..b065c84 100644
--- a/third_party/blink/renderer/core/html/canvas/canvas_rendering_context.h
+++ b/third_party/blink/renderer/core/html/canvas/canvas_rendering_context.h
@@ -133,7 +133,6 @@
   // the contents of the canvas (called didDraw). It marks the completion
   // of a presentable frame.
   virtual void FinalizeFrame() {}
-
   void NeedsFinalizeFrame();
 
   // Thread::TaskObserver implementation
diff --git a/third_party/blink/renderer/core/html/canvas/canvas_rendering_context_host.cc b/third_party/blink/renderer/core/html/canvas/canvas_rendering_context_host.cc
index d7ae9b1..65ad86e 100644
--- a/third_party/blink/renderer/core/html/canvas/canvas_rendering_context_host.cc
+++ b/third_party/blink/renderer/core/html/canvas/canvas_rendering_context_host.cc
@@ -208,7 +208,7 @@
 ScriptPromise CanvasRenderingContextHost::convertToBlob(
     ScriptState* script_state,
     const ImageEncodeOptions* options,
-    ExceptionState& exception_state) const {
+    ExceptionState& exception_state) {
   WTF::String object_name = "Canvas";
   if (this->IsOffscreenCanvas())
     object_name = "OffscreenCanvas";
@@ -240,6 +240,8 @@
     return ScriptPromise();
   }
 
+  // It's possible that there are recorded commands that have not been resolved
+  RenderingContext()->FinalizeFrame();
   base::TimeTicks start_time = base::TimeTicks::Now();
   scoped_refptr<StaticBitmapImage> image_bitmap =
       RenderingContext()->GetImage(kPreferNoAcceleration);
diff --git a/third_party/blink/renderer/core/html/canvas/canvas_rendering_context_host.h b/third_party/blink/renderer/core/html/canvas/canvas_rendering_context_host.h
index d90f197..74282e90 100644
--- a/third_party/blink/renderer/core/html/canvas/canvas_rendering_context_host.h
+++ b/third_party/blink/renderer/core/html/canvas/canvas_rendering_context_host.h
@@ -95,9 +95,11 @@
   bool Is2d() const;
   CanvasColorParams ColorParams() const;
 
+  // For deferred canvases this will have the side effect of drawing recorded
+  // commands in order to finalize the frame
   ScriptPromise convertToBlob(ScriptState*,
                               const ImageEncodeOptions*,
-                              ExceptionState&) const;
+                              ExceptionState&);
 
   // blink::CanvasImageSource
   bool IsOffscreenCanvas() const override;
diff --git a/third_party/blink/renderer/core/html/canvas/html_canvas_element.cc b/third_party/blink/renderer/core/html/canvas/html_canvas_element.cc
index 0bc82095..77c908e 100644
--- a/third_party/blink/renderer/core/html/canvas/html_canvas_element.cc
+++ b/third_party/blink/renderer/core/html/canvas/html_canvas_element.cc
@@ -403,10 +403,10 @@
   if (resource_provider)
     resource_provider->ReleaseLockedImages();
 
-  if (canvas2d_bridge_) {
-    if (!LowLatencyEnabled())
-      canvas2d_bridge_->FinalizeFrame();
-  }
+  // Low-latency 2d canvases will produce their frames after the resource gets
+  // single buffered
+  if (context_ && !(Is2d() && LowLatencyEnabled()))
+    context_->FinalizeFrame();
 
   if (LowLatencyEnabled() && !dirty_rect_.IsEmpty()) {
     if (GetOrCreateCanvasResourceProvider(kPreferAcceleration)) {
@@ -420,6 +420,8 @@
           context_->ProvideBackBufferToResourceProvider();
       }
 
+      // TODO(aaronhk) This should just be a generic call to finalize frame
+      // but the pixel 2 bot hanging prevents it (crbug.com/1002946)
       if (canvas2d_bridge_) {
         canvas2d_bridge_->FlushRecording();
       } else {
diff --git a/third_party/blink/renderer/core/html/html_image_element.cc b/third_party/blink/renderer/core/html/html_image_element.cc
index d2df98f7..22c06f3 100644
--- a/third_party/blink/renderer/core/html/html_image_element.cc
+++ b/third_party/blink/renderer/core/html/html_image_element.cc
@@ -458,6 +458,7 @@
 
   if (!GetLayoutObject()) {
     // check the attribute first for an explicit pixel value
+    // TODO(cbiesinger): The attribute could be a float or percentage value...
     unsigned width = 0;
     if (ParseHTMLNonNegativeInteger(getAttribute(kWidthAttr), width))
       return width;
@@ -479,6 +480,7 @@
 
   if (!GetLayoutObject()) {
     // check the attribute first for an explicit pixel value
+    // TODO(cbiesinger): The attribute could be a float or percentage value...
     unsigned height = 0;
     if (ParseHTMLNonNegativeInteger(getAttribute(kHeightAttr), height))
       return height;
diff --git a/third_party/blink/renderer/core/html/media/html_media_source.h b/third_party/blink/renderer/core/html/media/html_media_source.h
index a759c7b8..4e89100 100644
--- a/third_party/blink/renderer/core/html/media/html_media_source.h
+++ b/third_party/blink/renderer/core/html/media/html_media_source.h
@@ -74,11 +74,8 @@
   // The JavaScript exposed version of this is Buffered.
   virtual WebTimeRanges BufferedInternal() const = 0;
 
-  // The JavaScript exposed version of this is Seekable.
   virtual WebTimeRanges SeekableInternal() const = 0;
-
   virtual TimeRanges* Buffered() const = 0;
-  virtual TimeRanges* Seekable() const = 0;
   virtual void OnTrackChanged(TrackBase*) = 0;
 
   // URLRegistrable
diff --git a/third_party/blink/renderer/core/layout/layout_image.cc b/third_party/blink/renderer/core/layout/layout_image.cc
index 2794fb0..b281b01 100644
--- a/third_party/blink/renderer/core/layout/layout_image.cc
+++ b/third_party/blink/renderer/core/layout/layout_image.cc
@@ -142,7 +142,7 @@
 }
 
 void LayoutImage::UpdateIntrinsicSizeIfNeeded(const LayoutSize& new_size) {
-  if (image_resource_->ErrorOccurred() || !image_resource_->HasImage())
+  if (image_resource_->ErrorOccurred())
     return;
   SetIntrinsicSize(new_size);
 }
diff --git a/third_party/blink/renderer/core/layout/layout_replaced.cc b/third_party/blink/renderer/core/layout/layout_replaced.cc
index e2d48ad..6f43874 100644
--- a/third_party/blink/renderer/core/layout/layout_replaced.cc
+++ b/third_party/blink/renderer/core/layout/layout_replaced.cc
@@ -24,6 +24,7 @@
 #include "third_party/blink/renderer/core/layout/layout_replaced.h"
 
 #include "third_party/blink/renderer/core/editing/position_with_affinity.h"
+#include "third_party/blink/renderer/core/html/html_dimension.h"
 #include "third_party/blink/renderer/core/layout/api/line_layout_block_flow.h"
 #include "third_party/blink/renderer/core/layout/geometry/logical_offset.h"
 #include "third_party/blink/renderer/core/layout/geometry/logical_size.h"
@@ -39,6 +40,7 @@
 #include "third_party/blink/renderer/core/paint/paint_layer.h"
 #include "third_party/blink/renderer/core/paint/replaced_painter.h"
 #include "third_party/blink/renderer/platform/geometry/length_functions.h"
+#include "third_party/blink/renderer/platform/runtime_enabled_features.h"
 
 namespace blink {
 
@@ -671,10 +673,30 @@
                                          IntrinsicLogicalHeight().ToFloat());
 
   // Figure out if we need to compute an intrinsic ratio.
-  if (intrinsic_sizing_info.size.IsEmpty() || !LayoutObjectHasAspectRatio(this))
+  if (!LayoutObjectHasAspectRatio(this))
     return;
 
-  intrinsic_sizing_info.aspect_ratio = intrinsic_sizing_info.size;
+  if (!intrinsic_sizing_info.size.IsEmpty())
+    intrinsic_sizing_info.aspect_ratio = intrinsic_sizing_info.size;
+
+  auto* elem = DynamicTo<Element>(GetNode());
+  if (RuntimeEnabledFeatures::AspectRatioFromWidthAndHeightEnabled() && elem &&
+      IsHTMLImageElement(elem) &&
+      intrinsic_sizing_info.aspect_ratio.IsEmpty() &&
+      elem->FastHasAttribute(html_names::kWidthAttr) &&
+      elem->FastHasAttribute(html_names::kHeightAttr)) {
+    const AtomicString& width_str =
+        elem->FastGetAttribute(html_names::kWidthAttr);
+    const AtomicString& height_str =
+        elem->FastGetAttribute(html_names::kHeightAttr);
+    HTMLDimension width_dim, height_dim;
+    if (ParseDimensionValue(width_str, width_dim) &&
+        ParseDimensionValue(height_str, height_dim) && width_dim.IsAbsolute() &&
+        height_dim.IsAbsolute()) {
+      intrinsic_sizing_info.aspect_ratio.SetWidth(width_dim.Value());
+      intrinsic_sizing_info.aspect_ratio.SetHeight(height_dim.Value());
+    }
+  }
 }
 
 static inline LayoutUnit ResolveWidthForRatio(LayoutUnit height,
diff --git a/third_party/blink/renderer/core/layout/ng/exclusions/ng_exclusion_space.cc b/third_party/blink/renderer/core/layout/ng/exclusions/ng_exclusion_space.cc
index 25f9134..f7d88c1 100644
--- a/third_party/blink/renderer/core/layout/ng/exclusions/ng_exclusion_space.cc
+++ b/third_party/blink/renderer/core/layout/ng/exclusions/ng_exclusion_space.cc
@@ -109,33 +109,6 @@
          opportunity.rect.BlockEndOffset() > offset.block_offset;
 }
 
-// Returns true if the area defined by the given offset and inline_size
-// intersects with the shelfs area.
-//
-// No checks for the block direction are needed as the given area (defined by
-// offset and inline_size) extends to a block-end of infinity, and a shelf also
-// has a block-end of infinity.
-//
-// If the shelf is at -Infinity or +Infinity at either end, the given area
-// always intersects.
-bool Intersects(const NGExclusionSpaceInternal::NGShelf& shelf,
-                const NGBfcOffset& offset,
-                const LayoutUnit inline_size,
-                bool is_inline_level) {
-  if (shelf.line_right >= offset.line_offset &&
-      shelf.line_left <= offset.line_offset + inline_size)
-    return true;
-
-  // Negative available space creates a zero-width opportunity at the inline-end
-  // of the shelf. Consider such shelf intersects.
-  if (UNLIKELY(is_inline_level &&
-               (shelf.line_left > offset.line_offset ||
-                shelf.line_right < offset.line_offset + inline_size)))
-    return true;
-
-  return false;
-}
-
 // Creates a new layout opportunity. The given layout opportunity *must*
 // intersect with the given area (defined by offset and inline_size).
 NGLayoutOpportunity CreateLayoutOpportunity(const NGLayoutOpportunity& other,
@@ -163,10 +136,7 @@
 NGLayoutOpportunity CreateLayoutOpportunity(
     const NGExclusionSpaceInternal::NGShelf& shelf,
     const NGBfcOffset& offset,
-    const LayoutUnit inline_size,
-    bool is_inline_level) {
-  DCHECK(Intersects(shelf, offset, inline_size, is_inline_level));
-
+    const LayoutUnit inline_size) {
   NGBfcOffset start_offset(std::max(shelf.line_left, offset.line_offset),
                            std::max(shelf.block_offset, offset.block_offset));
 
@@ -547,22 +517,23 @@
 NGExclusionSpaceInternal::DerivedGeometry::FindLayoutOpportunity(
     const NGBfcOffset& offset,
     const LayoutUnit available_inline_size,
-    const LogicalSize& minimum_size) const {
+    const LayoutUnit minimum_inline_size) const {
   // TODO(ikilpatrick): Determine what to do for a -ve available_inline_size.
 
   NGLayoutOpportunity return_opportunity;
   IterateAllLayoutOpportunities(
-      offset, available_inline_size, false /* is_inline_level */,
-      [&return_opportunity, &minimum_size,
-       &available_inline_size](const NGLayoutOpportunity opportunity) -> bool {
+      offset, available_inline_size,
+      [&return_opportunity, &offset, &available_inline_size,
+       &minimum_inline_size](const NGLayoutOpportunity opportunity) -> bool {
         // Determine if this opportunity will fit the given size.
         //
-        // NOTE: There are cases where the available_inline_size may be smaller
-        // than the minimum_size.inline_size. In such cases if the opportunity
-        // is the same as the available_inline_size, it pretends that it "fits".
-        if ((opportunity.rect.InlineSize() >= minimum_size.inline_size ||
-             opportunity.rect.InlineSize() == available_inline_size) &&
-            opportunity.rect.BlockSize() >= minimum_size.block_size) {
+        // NOTE: There are cases where the |available_inline_size| may be
+        // smaller than the |minimum_inline_size|. In such cases if the
+        // opportunity is the same as the |available_inline_size|, it pretends
+        // that it "fits".
+        if (opportunity.rect.InlineSize() >= minimum_inline_size ||
+            (opportunity.rect.InlineSize() == available_inline_size &&
+             opportunity.rect.LineStartOffset() == offset.line_offset)) {
           return_opportunity = std::move(opportunity);
           return true;
         }
@@ -581,7 +552,7 @@
 
   // This method is only used for determining the position of line-boxes.
   IterateAllLayoutOpportunities(
-      offset, available_inline_size, true /* is_inline_level */,
+      offset, available_inline_size,
       [&opportunities](const NGLayoutOpportunity opportunity) -> bool {
         opportunities.push_back(std::move(opportunity));
         return false;
@@ -594,7 +565,6 @@
 void NGExclusionSpaceInternal::DerivedGeometry::IterateAllLayoutOpportunities(
     const NGBfcOffset& offset,
     const LayoutUnit available_inline_size,
-    bool is_inline_level,
     const LambdaFunc& lambda) const {
   auto* shelves_it = shelves_.begin();
   auto* areas_it = areas_.begin();
@@ -608,11 +578,6 @@
     DCHECK_NE(shelves_it, shelves_end);
     const NGShelf& shelf = *shelves_it;
 
-    if (!Intersects(shelf, offset, available_inline_size, is_inline_level)) {
-      ++shelves_it;
-      continue;
-    }
-
     if (areas_it != areas_end) {
       const NGClosedArea& area = *areas_it;
 
@@ -653,8 +618,7 @@
         HasSolidEdges(shelf.line_right_edges, offset.block_offset,
                       LayoutUnit::Max());
     if (has_solid_edges) {
-      if (lambda(CreateLayoutOpportunity(shelf, offset, available_inline_size,
-                                         is_inline_level)))
+      if (lambda(CreateLayoutOpportunity(shelf, offset, available_inline_size)))
         return;
     }
     ++shelves_it;
diff --git a/third_party/blink/renderer/core/layout/ng/exclusions/ng_exclusion_space.h b/third_party/blink/renderer/core/layout/ng/exclusions/ng_exclusion_space.h
index 5adea8b..3f40568 100644
--- a/third_party/blink/renderer/core/layout/ng/exclusions/ng_exclusion_space.h
+++ b/third_party/blink/renderer/core/layout/ng/exclusions/ng_exclusion_space.h
@@ -40,7 +40,7 @@
   NGLayoutOpportunity FindLayoutOpportunity(
       const NGBfcOffset& offset,
       const LayoutUnit available_inline_size,
-      const LogicalSize& minimum_size) const {
+      const LayoutUnit minimum_inline_size) const {
     // If the area clears all floats, we can just return the layout opportunity
     // which matches the available space.
     if (offset.block_offset >=
@@ -52,7 +52,7 @@
     }
 
     return GetDerivedGeometry().FindLayoutOpportunity(
-        offset, available_inline_size, minimum_size);
+        offset, available_inline_size, minimum_inline_size);
   }
 
   LayoutOpportunityVector AllLayoutOpportunities(
@@ -351,7 +351,7 @@
     NGLayoutOpportunity FindLayoutOpportunity(
         const NGBfcOffset& offset,
         const LayoutUnit available_inline_size,
-        const LogicalSize& minimum_size) const;
+        const LayoutUnit minimum_inline_size) const;
 
     LayoutOpportunityVector AllLayoutOpportunities(
         const NGBfcOffset& offset,
@@ -360,7 +360,6 @@
     template <typename LambdaFunc>
     void IterateAllLayoutOpportunities(const NGBfcOffset& offset,
                                        const LayoutUnit available_inline_size,
-                                       bool is_inline_level,
                                        const LambdaFunc&) const;
 
     // See |NGShelf| for a broad description of what shelves are. We always
@@ -424,12 +423,12 @@
 
   // Returns a layout opportunity, within the BFC.
   // The area to search for layout opportunities is defined by the given offset,
-  // and available_inline_size. The layout opportunity must be greater than the
-  // given minimum_size.
+  // and |available_inline_size|. The layout opportunity must be greater than
+  // the given |minimum_inline_size|.
   NGLayoutOpportunity FindLayoutOpportunity(
       const NGBfcOffset& offset,
       const LayoutUnit available_inline_size,
-      const LogicalSize& minimum_size) const {
+      const LayoutUnit minimum_inline_size = LayoutUnit()) const {
     if (!exclusion_space_) {
       NGBfcOffset end_offset(
           offset.line_offset + available_inline_size.ClampNegativeToZero(),
@@ -437,7 +436,7 @@
       return NGLayoutOpportunity(NGBfcRect(offset, end_offset), nullptr);
     }
     return exclusion_space_->FindLayoutOpportunity(
-        offset, available_inline_size, minimum_size);
+        offset, available_inline_size, minimum_inline_size);
   }
 
   // If possible prefer FindLayoutOpportunity over this function.
diff --git a/third_party/blink/renderer/core/layout/ng/inline/ng_line_breaker.cc b/third_party/blink/renderer/core/layout/ng/inline/ng_line_breaker.cc
index 44cc69b..ad471bb 100644
--- a/third_party/blink/renderer/core/layout/ng/inline/ng_line_breaker.cc
+++ b/third_party/blink/renderer/core/layout/ng/inline/ng_line_breaker.cc
@@ -1422,7 +1422,7 @@
 
     NGLayoutOpportunity opportunity = exclusion_space_->FindLayoutOpportunity(
         {constraint_space_.BfcOffset().line_offset, bfc_block_offset},
-        constraint_space_.AvailableSize().inline_size, LogicalSize());
+        constraint_space_.AvailableSize().inline_size);
 
     DCHECK_EQ(bfc_block_offset, opportunity.rect.BlockStartOffset());
 
diff --git a/third_party/blink/renderer/core/layout/ng/list/ng_unpositioned_list_marker.cc b/third_party/blink/renderer/core/layout/ng/list/ng_unpositioned_list_marker.cc
index 8a4ff04..374086a 100644
--- a/third_party/blink/renderer/core/layout/ng/list/ng_unpositioned_list_marker.cc
+++ b/third_party/blink/renderer/core/layout/ng/list/ng_unpositioned_list_marker.cc
@@ -170,8 +170,8 @@
                               border_scrollbar_padding.inline_start -
                               border_scrollbar_padding.inline_end;
   NGLayoutOpportunity opportunity =
-      space.ExclusionSpace().FindLayoutOpportunity(
-          origin_offset, available_size, LogicalSize());
+      space.ExclusionSpace().FindLayoutOpportunity(origin_offset,
+                                                   available_size);
   DCHECK(marker_layout_object_);
   const TextDirection direction = marker_layout_object_->StyleRef().Direction();
   if (direction == TextDirection::kLtr) {
diff --git a/third_party/blink/renderer/core/layout/ng/ng_block_layout_algorithm_utils.cc b/third_party/blink/renderer/core/layout/ng/ng_block_layout_algorithm_utils.cc
index 3841774..9ea3b29c 100644
--- a/third_party/blink/renderer/core/layout/ng/ng_block_layout_algorithm_utils.cc
+++ b/third_party/blink/renderer/core/layout/ng/ng_block_layout_algorithm_utils.cc
@@ -18,8 +18,7 @@
 
   // Find a layout opportunity, where we would have placed a zero-sized line.
   NGLayoutOpportunity opportunity = exclusion_space.FindLayoutOpportunity(
-      origin_bfc_offset, child_available_inline_size,
-      /* minimum_size */ LogicalSize());
+      origin_bfc_offset, child_available_inline_size);
 
   LayoutUnit child_line_offset = IsLtr(direction)
                                      ? opportunity.rect.LineStartOffset()
diff --git a/third_party/blink/renderer/core/layout/ng/ng_floats_utils.cc b/third_party/blink/renderer/core/layout/ng/ng_floats_utils.cc
index d2397e3..98b661f 100644
--- a/third_party/blink/renderer/core/layout/ng/ng_floats_utils.cc
+++ b/third_party/blink/renderer/core/layout/ng/ng_floats_utils.cc
@@ -52,10 +52,9 @@
 
   AdjustToClearance(clearance_offset, &adjusted_origin_point);
 
-  LogicalSize float_size(inline_size + fragment_margins.InlineSum(),
-                         LayoutUnit());
   return exclusion_space.FindLayoutOpportunity(
-      adjusted_origin_point, float_available_size.inline_size, float_size);
+      adjusted_origin_point, float_available_size.inline_size,
+      inline_size + fragment_margins.InlineSum() /* minimum_inline_size */);
 }
 
 // Creates a constraint space for an unpositioned float. origin_block_offset
diff --git a/third_party/blink/renderer/core/offscreencanvas/offscreen_canvas.cc b/third_party/blink/renderer/core/offscreencanvas/offscreen_canvas.cc
index 6e8bbcd..d562712 100644
--- a/third_party/blink/renderer/core/offscreencanvas/offscreen_canvas.cc
+++ b/third_party/blink/renderer/core/offscreencanvas/offscreen_canvas.cc
@@ -19,7 +19,7 @@
 #include "third_party/blink/renderer/core/html/canvas/canvas_rendering_context_factory.h"
 #include "third_party/blink/renderer/core/html/canvas/image_data.h"
 #include "third_party/blink/renderer/core/imagebitmap/image_bitmap.h"
-#include "third_party/blink/renderer/core/workers/worker_global_scope.h"
+#include "third_party/blink/renderer/core/workers/dedicated_worker_global_scope.h"
 #include "third_party/blink/renderer/platform/graphics/canvas_resource_dispatcher.h"
 #include "third_party/blink/renderer/platform/graphics/canvas_resource_provider.h"
 #include "third_party/blink/renderer/platform/graphics/gpu/shared_gpu_context.h"
@@ -77,9 +77,9 @@
   }
 
   if (HasPlaceholderCanvas() && GetTopExecutionContext() &&
-      GetTopExecutionContext()->IsWorkerGlobalScope()) {
+      GetTopExecutionContext()->IsDedicatedWorkerGlobalScope()) {
     WorkerAnimationFrameProvider* animation_frame_provider =
-        To<WorkerGlobalScope>(GetTopExecutionContext())
+        To<DedicatedWorkerGlobalScope>(GetTopExecutionContext())
             ->GetAnimationFrameProvider();
     if (animation_frame_provider)
       animation_frame_provider->DeregisterOffscreenCanvas(this);
@@ -89,9 +89,9 @@
 void OffscreenCanvas::SetPlaceholderCanvasId(DOMNodeId canvas_id) {
   placeholder_canvas_id_ = canvas_id;
   if (GetTopExecutionContext() &&
-      GetTopExecutionContext()->IsWorkerGlobalScope()) {
+      GetTopExecutionContext()->IsDedicatedWorkerGlobalScope()) {
     WorkerAnimationFrameProvider* animation_frame_provider =
-        To<WorkerGlobalScope>(GetTopExecutionContext())
+        To<DedicatedWorkerGlobalScope>(GetTopExecutionContext())
             ->GetAnimationFrameProvider();
     if (animation_frame_provider)
       animation_frame_provider->RegisterOffscreenCanvas(this);
diff --git a/third_party/blink/renderer/core/page/scrolling/root_scroller_test.cc b/third_party/blink/renderer/core/page/scrolling/root_scroller_test.cc
index 120ccb2d..cbcef3f 100644
--- a/third_party/blink/renderer/core/page/scrolling/root_scroller_test.cc
+++ b/third_party/blink/renderer/core/page/scrolling/root_scroller_test.cc
@@ -3,6 +3,7 @@
 // found in the LICENSE file.
 
 #include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/public/common/page/page_zoom.h"
 #include "third_party/blink/public/platform/web_coalesced_input_event.h"
 #include "third_party/blink/public/platform/web_url_loader_mock_factory.h"
 #include "third_party/blink/public/web/web_console_message.h"
@@ -2354,7 +2355,7 @@
   EXPECT_EQ(container,
             GetDocument().GetRootScrollerController().EffectiveRootScroller());
   EXPECT_EQ(ToLayoutBox(container->GetLayoutObject())->Size().Height(), 600);
-  WebView().SetZoomLevel(WebView::ZoomFactorToZoomLevel(2.0));
+  WebView().SetZoomLevel(PageZoomFactorToZoomLevel(2.0));
   WebView().GetPage()->GetBrowserControls().SetShownRatio(0);
   WebView().ResizeWithBrowserControls(IntSize(800, 650), 50, 0, false);
   Compositor().BeginFrame();
diff --git a/third_party/blink/renderer/core/svg/animation/smil_time.h b/third_party/blink/renderer/core/svg/animation/smil_time.h
index ab5b45c..556e4d5 100644
--- a/third_party/blink/renderer/core/svg/animation/smil_time.h
+++ b/third_party/blink/renderer/core/svg/animation/smil_time.h
@@ -152,16 +152,11 @@
   DISALLOW_NEW();
 
  public:
-  SMILTimeWithOrigin() : origin_(SMILTimeOrigin::kAttribute) {}
-
   SMILTimeWithOrigin(const SMILTime& time, SMILTimeOrigin origin)
       : time_(time), origin_(origin) {}
 
   const SMILTime& Time() const { return time_; }
-  bool OriginIsScript() const { return origin_ == SMILTimeOrigin::kScript; }
-  bool HasSameOrigin(SMILTimeWithOrigin other) const {
-    return origin_ == other.origin_;
-  }
+  SMILTimeOrigin Origin() const { return origin_; }
 
  private:
   SMILTime time_;
@@ -207,35 +202,6 @@
   return !(a == b);
 }
 
-struct SMILTimeHash {
-  STATIC_ONLY(SMILTimeHash);
-  static unsigned GetHash(const SMILTime& key) {
-    return WTF::IntHash<int64_t>::GetHash(key.time_.InMicroseconds());
-  }
-  static bool Equal(const SMILTime& a, const SMILTime& b) { return a == b; }
-  static const bool safe_to_compare_to_empty_or_deleted = true;
-};
-
 }  // namespace blink
 
-namespace WTF {
-
-template <>
-struct DefaultHash<blink::SMILTime> {
-  typedef blink::SMILTimeHash Hash;
-};
-
-template <>
-struct HashTraits<blink::SMILTime> : GenericHashTraits<blink::SMILTime> {
-  static blink::SMILTime EmptyValue() { return blink::SMILTime::Unresolved(); }
-  static void ConstructDeletedValue(blink::SMILTime& slot, bool) {
-    slot = blink::SMILTime::Earliest();
-  }
-  static bool IsDeletedValue(blink::SMILTime value) {
-    return value == blink::SMILTime::Earliest();
-  }
-};
-
-}  // namespace WTF
-
 #endif  // THIRD_PARTY_BLINK_RENDERER_CORE_SVG_ANIMATION_SMIL_TIME_H_
diff --git a/third_party/blink/renderer/core/svg/animation/svg_smil_element.cc b/third_party/blink/renderer/core/svg/animation/svg_smil_element.cc
index 3f82428..1ae15c4 100644
--- a/third_party/blink/renderer/core/svg/animation/svg_smil_element.cc
+++ b/third_party/blink/renderer/core/svg/animation/svg_smil_element.cc
@@ -268,10 +268,11 @@
   ConnectConditions();
 }
 
-static inline void ClearTimesWithDynamicOrigins(
-    Vector<SMILTimeWithOrigin>& time_list) {
+static inline void RemoveInstanceTimesWithOrigin(
+    Vector<SMILTimeWithOrigin>& time_list,
+    SMILTimeOrigin origin) {
   for (int i = time_list.size() - 1; i >= 0; --i) {
-    if (time_list[i].OriginIsScript())
+    if (time_list[i].Origin() == origin)
       time_list.EraseAt(i);
   }
 }
@@ -309,8 +310,10 @@
 
   // "If no attribute is present, the default begin value (an offset-value of 0)
   // must be evaluated."
-  if (!FastHasAttribute(svg_names::kBeginAttr))
-    begin_times_.push_back(SMILTimeWithOrigin());
+  if (!FastHasAttribute(svg_names::kBeginAttr) && begin_times_.IsEmpty()) {
+    begin_times_.push_back(
+        SMILTimeWithOrigin(SMILTime(), SMILTimeOrigin::kAttribute));
+  }
 
   if (is_waiting_for_first_interval_)
     ResolveFirstInterval();
@@ -475,22 +478,29 @@
       begin_or_end == kBegin ? begin_times_ : end_times_;
   if (begin_or_end == kEnd)
     has_end_event_conditions_ = false;
-  HashSet<SMILTime> existing;
-  for (const auto& instance_time : time_list) {
-    if (!instance_time.Time().IsUnresolved())
-      existing.insert(instance_time.Time());
-  }
+
+  // Remove any previously added offset-values.
+  // TODO(fs): Ought to remove instance times originating from sync-bases,
+  // events etc. as well if those conditions are no longer in the attribute.
+  RemoveInstanceTimesWithOrigin(time_list, SMILTimeOrigin::kAttribute);
+
   Vector<String> split_string;
   parse_string.Split(';', split_string);
   for (const auto& item : split_string) {
     SMILTime value = ParseClockValue(item);
     if (value.IsUnresolved()) {
       ParseCondition(item, begin_or_end);
-    } else if (!existing.Contains(value)) {
+    } else {
       time_list.push_back(
           SMILTimeWithOrigin(value, SMILTimeOrigin::kAttribute));
     }
   }
+  // "If no attribute is present, the default begin value (an offset-value of 0)
+  // must be evaluated."
+  if (begin_or_end == kBegin && parse_string.IsNull()) {
+    begin_times_.push_back(
+        SMILTimeWithOrigin(SMILTime(), SMILTimeOrigin::kAttribute));
+  }
   std::sort(time_list.begin(), time_list.end());
 }
 
@@ -705,7 +715,7 @@
       break;
     // If they share both time and origin, we don't need to add it,
     // we just need to react.
-    if (position->HasSameOrigin(time))
+    if (position->Origin() == time.Origin())
       return;
   }
   list.insert(position - list.begin(), time);
@@ -717,15 +727,14 @@
   SMILTime current_presentation_time =
       time_container_ ? time_container_->CurrentDocumentTime() : SMILTime();
   DCHECK(!current_presentation_time.IsUnresolved());
-  SMILTimeWithOrigin time_with_origin(time, origin);
   // Ignore new instance times for 'end' if the element is not active
   // and the origin is script.
   if (begin_or_end == kEnd && GetActiveState() == kInactive &&
-      time_with_origin.OriginIsScript())
+      origin == SMILTimeOrigin::kScript)
     return;
   Vector<SMILTimeWithOrigin>& list =
       begin_or_end == kBegin ? begin_times_ : end_times_;
-  InsertSortedAndUnique(list, time_with_origin);
+  InsertSortedAndUnique(list, SMILTimeWithOrigin(time, origin));
   if (begin_or_end == kBegin)
     BeginListChanged(current_presentation_time);
   else
@@ -1237,8 +1246,8 @@
 }
 
 void SVGSMILElement::EndedActiveInterval() {
-  ClearTimesWithDynamicOrigins(begin_times_);
-  ClearTimesWithDynamicOrigins(end_times_);
+  RemoveInstanceTimesWithOrigin(begin_times_, SMILTimeOrigin::kScript);
+  RemoveInstanceTimesWithOrigin(end_times_, SMILTimeOrigin::kScript);
 }
 
 void SVGSMILElement::ScheduleRepeatEvents() {
diff --git a/third_party/blink/renderer/core/svg/svg_animation_element.cc b/third_party/blink/renderer/core/svg/svg_animation_element.cc
index 17210de..504f43b 100644
--- a/third_party/blink/renderer/core/svg/svg_animation_element.cc
+++ b/third_party/blink/renderer/core/svg/svg_animation_element.cc
@@ -71,6 +71,10 @@
   return false;
 }
 
+static bool IsInZeroToOneRange(float value) {
+  return value >= 0 && value <= 1;
+}
+
 static bool ParseKeyTimes(const String& string,
                           Vector<float>& result,
                           bool verify_order) {
@@ -81,7 +85,7 @@
     String time_string = parse_list[n].StripWhiteSpace();
     bool ok;
     float time = time_string.ToFloat(&ok);
-    if (!ok || time < 0 || time > 1)
+    if (!ok || !IsInZeroToOneRange(time))
       goto fail;
     if (verify_order) {
       if (!n) {
@@ -108,20 +112,20 @@
   SkipOptionalSVGSpaces(ptr, end);
 
   while (ptr < end) {
-    float pos_a = 0;
-    if (!ParseNumber(ptr, end, pos_a))
+    float cp1x = 0;
+    if (!ParseNumber(ptr, end, cp1x))
       return false;
 
-    float pos_b = 0;
-    if (!ParseNumber(ptr, end, pos_b))
+    float cp1y = 0;
+    if (!ParseNumber(ptr, end, cp1y))
       return false;
 
-    float pos_c = 0;
-    if (!ParseNumber(ptr, end, pos_c))
+    float cp2x = 0;
+    if (!ParseNumber(ptr, end, cp2x))
       return false;
 
-    float pos_d = 0;
-    if (!ParseNumber(ptr, end, pos_d, kDisallowWhitespace))
+    float cp2y = 0;
+    if (!ParseNumber(ptr, end, cp2y, kDisallowWhitespace))
       return false;
 
     SkipOptionalSVGSpaces(ptr, end);
@@ -130,7 +134,11 @@
       ptr++;
     SkipOptionalSVGSpaces(ptr, end);
 
-    result.push_back(gfx::CubicBezier(pos_a, pos_b, pos_c, pos_d));
+    // Require that the x values are within the [0, 1] range.
+    if (!IsInZeroToOneRange(cp1x) || !IsInZeroToOneRange(cp2x))
+      return false;
+
+    result.push_back(gfx::CubicBezier(cp1x, cp1y, cp2x, cp2y));
   }
 
   return ptr == end;
diff --git a/third_party/blink/renderer/core/workers/dedicated_worker_global_scope.cc b/third_party/blink/renderer/core/workers/dedicated_worker_global_scope.cc
index fedd0a71..cae95c4 100644
--- a/third_party/blink/renderer/core/workers/dedicated_worker_global_scope.cc
+++ b/third_party/blink/renderer/core/workers/dedicated_worker_global_scope.cc
@@ -64,6 +64,8 @@
     base::TimeTicks time_origin) {
   std::unique_ptr<Vector<String>> outside_origin_trial_tokens =
       std::move(creation_params->origin_trial_tokens);
+  BeginFrameProviderParams begin_frame_provider_params =
+      creation_params->begin_frame_provider_params;
 
   // Off-the-main-thread worker script fetch:
   // Initialize() is called after script fetch.
@@ -71,7 +73,7 @@
       OffMainThreadWorkerScriptFetchOption::kEnabled) {
     return MakeGarbageCollected<DedicatedWorkerGlobalScope>(
         std::move(creation_params), thread, time_origin,
-        std::move(outside_origin_trial_tokens));
+        std::move(outside_origin_trial_tokens), begin_frame_provider_params);
   }
 
   // Legacy on-the-main-thread worker script fetch (to be removed):
@@ -82,7 +84,7 @@
       *creation_params->response_address_space;
   auto* global_scope = MakeGarbageCollected<DedicatedWorkerGlobalScope>(
       std::move(creation_params), thread, time_origin,
-      std::move(outside_origin_trial_tokens));
+      std::move(outside_origin_trial_tokens), begin_frame_provider_params);
   // Pass dummy CSP headers here as it is superseded by outside's CSP headers in
   // Initialize().
   // Pass dummy origin trial tokens here as it is already set to outside's
@@ -100,8 +102,13 @@
     std::unique_ptr<GlobalScopeCreationParams> creation_params,
     DedicatedWorkerThread* thread,
     base::TimeTicks time_origin,
-    std::unique_ptr<Vector<String>> outside_origin_trial_tokens)
-    : WorkerGlobalScope(std::move(creation_params), thread, time_origin) {
+    std::unique_ptr<Vector<String>> outside_origin_trial_tokens,
+    const BeginFrameProviderParams& begin_frame_provider_params)
+    : WorkerGlobalScope(std::move(creation_params), thread, time_origin),
+      animation_frame_provider_(
+          MakeGarbageCollected<WorkerAnimationFrameProvider>(
+              this,
+              begin_frame_provider_params)) {
   // Dedicated workers don't need to pause after script fetch.
   ReadyToRunWorkerScript();
   // Inherit the outside's origin trial tokens.
@@ -304,12 +311,36 @@
       classic_script_loader->ReleaseCachedMetadata(), stack_id);
 }
 
+int DedicatedWorkerGlobalScope::requestAnimationFrame(
+    V8FrameRequestCallback* callback,
+    ExceptionState& exception_state) {
+  auto* frame_callback =
+      MakeGarbageCollected<FrameRequestCallbackCollection::V8FrameCallback>(
+          callback);
+  frame_callback->SetUseLegacyTimeBase(false);
+
+  int ret = animation_frame_provider_->RegisterCallback(frame_callback);
+
+  if (ret == WorkerAnimationFrameProvider::kInvalidCallbackId) {
+    exception_state.ThrowDOMException(
+        DOMExceptionCode::kNotSupportedError,
+        "requestAnimationFrame not supported in this Worker.");
+  }
+
+  return ret;
+}
+
+void DedicatedWorkerGlobalScope::cancelAnimationFrame(int id) {
+  animation_frame_provider_->CancelCallback(id);
+}
+
 DedicatedWorkerObjectProxy& DedicatedWorkerGlobalScope::WorkerObjectProxy()
     const {
   return static_cast<DedicatedWorkerThread*>(GetThread())->WorkerObjectProxy();
 }
 
 void DedicatedWorkerGlobalScope::Trace(blink::Visitor* visitor) {
+  visitor->Trace(animation_frame_provider_);
   WorkerGlobalScope::Trace(visitor);
 }
 
diff --git a/third_party/blink/renderer/core/workers/dedicated_worker_global_scope.h b/third_party/blink/renderer/core/workers/dedicated_worker_global_scope.h
index 9aa91ebc..c75483c 100644
--- a/third_party/blink/renderer/core/workers/dedicated_worker_global_scope.h
+++ b/third_party/blink/renderer/core/workers/dedicated_worker_global_scope.h
@@ -32,6 +32,7 @@
 #define THIRD_PARTY_BLINK_RENDERER_CORE_WORKERS_DEDICATED_WORKER_GLOBAL_SCOPE_H_
 
 #include <memory>
+#include "third_party/blink/renderer/core/animation_frame/worker_animation_frame_provider.h"
 #include "third_party/blink/renderer/core/core_export.h"
 #include "third_party/blink/renderer/core/messaging/message_port.h"
 #include "third_party/blink/renderer/core/workers/worker_global_scope.h"
@@ -64,7 +65,8 @@
       std::unique_ptr<GlobalScopeCreationParams>,
       DedicatedWorkerThread*,
       base::TimeTicks time_origin,
-      std::unique_ptr<Vector<String>> outside_origin_trial_tokens);
+      std::unique_ptr<Vector<String>> outside_origin_trial_tokens,
+      const BeginFrameProviderParams& begin_frame_provider_params);
 
   ~DedicatedWorkerGlobalScope() override;
 
@@ -75,6 +77,13 @@
   // (via WorkerOrWorkletGlobalScope -> EventTargetWithInlineData).
   const AtomicString& InterfaceName() const override;
 
+  // RequestAnimationFrame
+  int requestAnimationFrame(V8FrameRequestCallback* callback, ExceptionState&);
+  void cancelAnimationFrame(int id);
+  WorkerAnimationFrameProvider* GetAnimationFrameProvider() {
+    return animation_frame_provider_;
+  }
+
   // Implements WorkerGlobalScope.
   void Initialize(const KURL& response_url,
                   network::mojom::ReferrerPolicy response_referrer_policy,
@@ -116,6 +125,8 @@
                              const v8_inspector::V8StackTraceId& stack_id);
 
   DedicatedWorkerObjectProxy& WorkerObjectProxy() const;
+
+  Member<WorkerAnimationFrameProvider> animation_frame_provider_;
 };
 
 template <>
diff --git a/third_party/blink/renderer/core/workers/dedicated_worker_global_scope.idl b/third_party/blink/renderer/core/workers/dedicated_worker_global_scope.idl
index 4adb13fb..034a5e4 100644
--- a/third_party/blink/renderer/core/workers/dedicated_worker_global_scope.idl
+++ b/third_party/blink/renderer/core/workers/dedicated_worker_global_scope.idl
@@ -41,6 +41,11 @@
 
     void close();
 
+    // AnimationFrameProvider mixin
+    // https://html.spec.whatwg.org/C/#animation-frames
+    [RaisesException] long requestAnimationFrame(FrameRequestCallback callback);
+    void cancelAnimationFrame(long handle);
+
     attribute EventHandler onmessage;
     attribute EventHandler onmessageerror;
 };
diff --git a/third_party/blink/renderer/core/workers/worker_global_scope.cc b/third_party/blink/renderer/core/workers/worker_global_scope.cc
index 161a0bd..6be6ed6 100644
--- a/third_party/blink/renderer/core/workers/worker_global_scope.cc
+++ b/third_party/blink/renderer/core/workers/worker_global_scope.cc
@@ -489,10 +489,6 @@
       timers_(GetTaskRunner(TaskType::kJavascriptTimer)),
       time_origin_(time_origin),
       font_selector_(MakeGarbageCollected<OffscreenFontSelector>(this)),
-      animation_frame_provider_(
-          MakeGarbageCollected<WorkerAnimationFrameProvider>(
-              this,
-              creation_params->begin_frame_provider_params)),
       script_eval_state_(ScriptEvalState::kPauseAfterFetch) {
   InstanceCounters::IncrementCounter(
       InstanceCounters::kWorkerGlobalScopeCounter);
@@ -561,28 +557,6 @@
                 WrapPersistent(callback), nullptr));
 }
 
-int WorkerGlobalScope::requestAnimationFrame(V8FrameRequestCallback* callback,
-                                             ExceptionState& exception_state) {
-  auto* frame_callback =
-      MakeGarbageCollected<FrameRequestCallbackCollection::V8FrameCallback>(
-          callback);
-  frame_callback->SetUseLegacyTimeBase(false);
-
-  int ret = animation_frame_provider_->RegisterCallback(frame_callback);
-
-  if (ret == WorkerAnimationFrameProvider::kInvalidCallbackId) {
-    exception_state.ThrowDOMException(
-        DOMExceptionCode::kNotSupportedError,
-        "requestAnimationFrame not supported in this Worker.");
-  }
-
-  return ret;
-}
-
-void WorkerGlobalScope::cancelAnimationFrame(int id) {
-  animation_frame_provider_->CancelCallback(id);
-}
-
 void WorkerGlobalScope::SetWorkerSettings(
     std::unique_ptr<WorkerSettings> worker_settings) {
   worker_settings_ = std::move(worker_settings);
@@ -605,7 +579,6 @@
   visitor->Trace(timers_);
   visitor->Trace(pending_error_events_);
   visitor->Trace(font_selector_);
-  visitor->Trace(animation_frame_provider_);
   visitor->Trace(trusted_types_);
   visitor->Trace(worker_script_);
   WorkerOrWorkletGlobalScope::Trace(visitor);
diff --git a/third_party/blink/renderer/core/workers/worker_global_scope.h b/third_party/blink/renderer/core/workers/worker_global_scope.h
index 6637679..1a59228 100644
--- a/third_party/blink/renderer/core/workers/worker_global_scope.h
+++ b/third_party/blink/renderer/core/workers/worker_global_scope.h
@@ -35,7 +35,6 @@
 #include "third_party/blink/public/common/browser_interface_broker_proxy.h"
 #include "third_party/blink/public/mojom/script/script_type.mojom-blink.h"
 #include "third_party/blink/renderer/bindings/core/v8/active_script_wrappable.h"
-#include "third_party/blink/renderer/core/animation_frame/worker_animation_frame_provider.h"
 #include "third_party/blink/renderer/core/core_export.h"
 #include "third_party/blink/renderer/core/dom/frame_request_callback_collection.h"
 #include "third_party/blink/renderer/core/execution_context/execution_context.h"
@@ -198,13 +197,6 @@
   // https://html.spec.whatwg.org/C/#windoworworkerglobalscope-mixin
   void queueMicrotask(V8VoidFunction*);
 
-  int requestAnimationFrame(V8FrameRequestCallback* callback, ExceptionState&);
-  void cancelAnimationFrame(int id);
-
-  WorkerAnimationFrameProvider* GetAnimationFrameProvider() {
-    return animation_frame_provider_;
-  }
-
   TrustedTypePolicyFactory* GetTrustedTypes() const override;
   TrustedTypePolicyFactory* trustedTypesWorkers() const {
     return GetTrustedTypes();
@@ -266,7 +258,6 @@
   int last_pending_error_event_id_ = 0;
 
   Member<OffscreenFontSelector> font_selector_;
-  Member<WorkerAnimationFrameProvider> animation_frame_provider_;
 
   service_manager::InterfaceProvider interface_provider_;
 
diff --git a/third_party/blink/renderer/core/workers/worker_global_scope.idl b/third_party/blink/renderer/core/workers/worker_global_scope.idl
index 23fb81b..d88dadaf 100644
--- a/third_party/blink/renderer/core/workers/worker_global_scope.idl
+++ b/third_party/blink/renderer/core/workers/worker_global_scope.idl
@@ -68,11 +68,6 @@
     [Replaceable] readonly attribute DOMString origin;
     void queueMicrotask(VoidFunction callback);
 
-    // AnimationFrameProvider mixin
-    // https://html.spec.whatwg.org/C/#animation-frames
-    [RaisesException] long requestAnimationFrame(FrameRequestCallback callback);
-    void cancelAnimationFrame(long handle);
-
     // FontFaceSource
     // https://drafts.csswg.org/css-font-loading-3/#font-face-source
     // TODO(fserb): temporarly until we can enable the interface below.
diff --git a/third_party/blink/renderer/devtools/front_end/event_listeners/EventListenersView.js b/third_party/blink/renderer/devtools/front_end/event_listeners/EventListenersView.js
index 7f52d7fa2..a4c1e2b 100644
--- a/third_party/blink/renderer/devtools/front_end/event_listeners/EventListenersView.js
+++ b/third_party/blink/renderer/devtools/front_end/event_listeners/EventListenersView.js
@@ -290,8 +290,9 @@
     const subtitle = this.listItemElement.createChild('span', 'event-listener-tree-subtitle');
     subtitle.appendChild(linkifier.linkifyRawLocation(this._eventListener.location(), this._eventListener.sourceURL()));
 
-    title.appendChild(
-        ObjectUI.ObjectPropertiesSection.createValueElement(object, false /* wasThrown */, false /* showPreview */));
+    this._valueTitle =
+        ObjectUI.ObjectPropertiesSection.createValueElement(object, false /* wasThrown */, false /* showPreview */);
+    title.appendChild(this._valueTitle);
 
     if (this._eventListener.canRemove()) {
       const deleteButton = title.createChild('span', 'event-listener-button');
@@ -348,4 +349,16 @@
   eventListener() {
     return this._eventListener;
   }
+
+  /**
+   * @override
+   */
+  onenter() {
+    if (this._valueTitle) {
+      this._valueTitle.click();
+      return true;
+    }
+
+    return false;
+  }
 };
diff --git a/third_party/blink/renderer/devtools/front_end/network/NetworkConfigView.js b/third_party/blink/renderer/devtools/front_end/network/NetworkConfigView.js
index 07c25a8..2fe4ecd 100644
--- a/third_party/blink/renderer/devtools/front_end/network/NetworkConfigView.js
+++ b/third_party/blink/renderer/devtools/front_end/network/NetworkConfigView.js
@@ -151,236 +151,239 @@
 /** @type {!Array.<{title: string, values: !Array.<{title: string, value: string}>}>} */
 Network.NetworkConfigView._userAgentGroups = [
   {
-    title: 'Android',
+    title: ls`Android`,
     values: [
       {
-        title: 'Android (4.0.2) Browser \u2014 Galaxy Nexus',
+        title: ls`Android (4.0.2) Browser \u2014 Galaxy Nexus`,
         value:
             'Mozilla/5.0 (Linux; U; Android 4.0.2; en-us; Galaxy Nexus Build/ICL53F) AppleWebKit/534.30 (KHTML, like Gecko) Version/4.0 Mobile Safari/534.30'
       },
       {
-        title: 'Android (2.3) Browser \u2014 Nexus S',
+        title: ls`Android (2.3) Browser \u2014 Nexus S`,
         value:
             'Mozilla/5.0 (Linux; U; Android 2.3.6; en-us; Nexus S Build/GRK39F) AppleWebKit/533.1 (KHTML, like Gecko) Version/4.0 Mobile Safari/533.1'
       }
     ]
   },
   {
-    title: 'BlackBerry',
+    title: ls`BlackBerry`,
     values: [
       {
-        title: 'BlackBerry \u2014 BB10',
+        title: ls`BlackBerry \u2014 BB10`,
         value:
             'Mozilla/5.0 (BB10; Touch) AppleWebKit/537.1+ (KHTML, like Gecko) Version/10.0.0.1337 Mobile Safari/537.1+'
       },
       {
-        title: 'BlackBerry \u2014 PlayBook 2.1',
+        title: ls`BlackBerry \u2014 PlayBook 2.1`,
         value:
             'Mozilla/5.0 (PlayBook; U; RIM Tablet OS 2.1.0; en-US) AppleWebKit/536.2+ (KHTML, like Gecko) Version/7.2.1.0 Safari/536.2+'
       },
       {
-        title: 'BlackBerry \u2014 9900',
+        title: ls`BlackBerry \u2014 9900`,
         value:
             'Mozilla/5.0 (BlackBerry; U; BlackBerry 9900; en-US) AppleWebKit/534.11+ (KHTML, like Gecko) Version/7.0.0.187 Mobile Safari/534.11+'
       }
     ]
   },
   {
-    title: 'Chrome',
+    title: ls`Chrome`,
     values: [
       {
-        title: 'Chrome \u2014 Android Mobile',
+        title: ls`Chrome \u2014 Android Mobile`,
         value:
             'Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/%s Mobile Safari/537.36'
       },
       {
-        title: 'Chrome \u2014 Android Tablet',
+        title: ls`Chrome \u2014 Android Tablet`,
         value:
             'Mozilla/5.0 (Linux; Android 4.3; Nexus 7 Build/JSS15Q) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/%s Safari/537.36'
       },
       {
-        title: 'Chrome \u2014 iPhone',
+        title: ls`Chrome \u2014 iPhone`,
         value:
             'Mozilla/5.0 (iPhone; CPU iPhone OS 9_1 like Mac OS X) AppleWebKit/601.1 (KHTML, like Gecko) CriOS/%s Mobile/13B143 Safari/601.1.46'
       },
       {
-        title: 'Chrome \u2014 iPad',
+        title: ls`Chrome \u2014 iPad`,
         value:
             'Mozilla/5.0 (iPad; CPU OS 9_1 like Mac OS X) AppleWebKit/601.1 (KHTML, like Gecko) CriOS/%s Mobile/13B143 Safari/601.1.46'
       },
       {
-        title: 'Chrome \u2014 Chrome OS',
+        title: ls`Chrome \u2014 Chrome OS`,
         value: 'Mozilla/5.0 (X11; CrOS x86_64 10066.0.0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/%s Safari/537.36'
       },
       {
-        title: 'Chrome \u2014 Mac',
+        title: ls`Chrome \u2014 Mac`,
         value:
             'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/%s Safari/537.36'
       },
       {
-        title: 'Chrome \u2014 Windows',
+        title: ls`Chrome \u2014 Windows`,
         value: 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/%s Safari/537.36'
       }
     ]
   },
   {
-    title: 'Firefox',
+    title: ls`Firefox`,
     values: [
       {
-        title: 'Firefox \u2014 Android Mobile',
+        title: ls`Firefox \u2014 Android Mobile`,
         value: 'Mozilla/5.0 (Android 4.4; Mobile; rv:46.0) Gecko/46.0 Firefox/46.0'
       },
       {
-        title: 'Firefox \u2014 Android Tablet',
+        title: ls`Firefox \u2014 Android Tablet`,
         value: 'Mozilla/5.0 (Android 4.4; Tablet; rv:46.0) Gecko/46.0 Firefox/46.0'
       },
       {
-        title: 'Firefox \u2014 iPhone',
+        title: ls`Firefox \u2014 iPhone`,
         value:
             'Mozilla/5.0 (iPhone; CPU iPhone OS 8_3 like Mac OS X) AppleWebKit/600.1.4 (KHTML, like Gecko) FxiOS/1.0 Mobile/12F69 Safari/600.1.4'
       },
       {
-        title: 'Firefox \u2014 iPad',
+        title: ls`Firefox \u2014 iPad`,
         value:
             'Mozilla/5.0 (iPad; CPU iPhone OS 8_3 like Mac OS X) AppleWebKit/600.1.4 (KHTML, like Gecko) FxiOS/1.0 Mobile/12F69 Safari/600.1.4'
       },
       {
-        title: 'Firefox \u2014 Mac',
+        title: ls`Firefox \u2014 Mac`,
         value: 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10.11; rv:46.0) Gecko/20100101 Firefox/46.0'
       },
       {
-        title: 'Firefox \u2014 Windows',
+        title: ls`Firefox \u2014 Windows`,
         value: 'Mozilla/5.0 (Windows NT 10.0; WOW64; rv:46.0) Gecko/20100101 Firefox/46.0'
       }
     ]
   },
   {
-    title: 'Googlebot',
+    title: ls`Googlebot`,
     values: [
-      {title: 'Googlebot', value: 'Mozilla/5.0 (compatible; Googlebot/2.1; +http://www.google.com/bot.html)'}, {
-        title: 'Googlebot Smartphone',
+      {title: ls`Googlebot`, value: 'Mozilla/5.0 (compatible; Googlebot/2.1; +http://www.google.com/bot.html)'}, {
+        title: ls`Googlebot Smartphone`,
         value:
             'Mozilla/5.0 (Linux; Android 6.0.1; Nexus 5X Build/MMB29P) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2272.96 Mobile Safari/537.36 (compatible; Googlebot/2.1; +http://www.google.com/bot.html)'
       }
     ]
   },
   {
-    title: 'Internet Explorer',
+    title: ls`Internet Explorer`,
     values: [
-      {title: 'Internet Explorer 11', value: 'Mozilla/5.0 (Windows NT 10.0; WOW64; Trident/7.0; rv:11.0) like Gecko'},
-      {title: 'Internet Explorer 10', value: 'Mozilla/5.0 (compatible; MSIE 10.0; Windows NT 6.1; WOW64; Trident/6.0)'},
-      {title: 'Internet Explorer 9', value: 'Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Trident/5.0)'},
-      {title: 'Internet Explorer 8', value: 'Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.0; Trident/4.0)'},
-      {title: 'Internet Explorer 7', value: 'Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.0)'}
+      {title: ls`Internet Explorer 11`, value: 'Mozilla/5.0 (Windows NT 10.0; WOW64; Trident/7.0; rv:11.0) like Gecko'},
+      {
+        title: ls`Internet Explorer 10`,
+        value: 'Mozilla/5.0 (compatible; MSIE 10.0; Windows NT 6.1; WOW64; Trident/6.0)'
+      },
+      {title: ls`Internet Explorer 9`, value: 'Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Trident/5.0)'},
+      {title: ls`Internet Explorer 8`, value: 'Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.0; Trident/4.0)'},
+      {title: ls`Internet Explorer 7`, value: 'Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.0)'}
     ]
   },
   {
-    title: 'Microsoft Edge',
+    title: ls`Microsoft Edge`,
     values: [
       {
-        title: 'Microsoft Edge (Chromium) \u2014 Windows',
+        title: ls`Microsoft Edge (Chromium) \u2014 Windows`,
         value:
             'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/%s Safari/537.36 Edg/%s'
       },
       {
-        title: 'Microsoft Edge (Chromium) \u2014 Mac',
+        title: ls`Microsoft Edge (Chromium) \u2014 Mac`,
         value:
             'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/%s Safari/537.36 Edg/%s'
       },
       {
-        title: 'Microsoft Edge \u2014 iPhone',
+        title: ls`Microsoft Edge \u2014 iPhone`,
         value:
             'Mozilla/5.0 (iPhone; CPU iPhone OS 12_3_1 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/12.1.1 EdgiOS/44.5.0.10 Mobile/15E148 Safari/604.1'
       },
       {
-        title: 'Microsoft Edge \u2014 iPad',
+        title: ls`Microsoft Edge \u2014 iPad`,
         value:
             'Mozilla/5.0 (iPad; CPU OS 12_3_1 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/12.0 EdgiOS/44.5.2 Mobile/15E148 Safari/605.1.15'
       },
       {
-        title: 'Microsoft Edge \u2014 Android Mobile',
+        title: ls`Microsoft Edge \u2014 Android Mobile`,
         value:
             'Mozilla/5.0 (Linux; Android 8.1.0; Pixel Build/OPM4.171019.021.D1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/65.0.3325.109 Mobile Safari/537.36 EdgA/42.0.0.2057'
       },
       {
-        title: 'Microsoft Edge \u2014 Android Tablet',
+        title: ls`Microsoft Edge \u2014 Android Tablet`,
         value:
             'Mozilla/5.0 (Linux; Android 6.0.1; Nexus 7 Build/MOB30X) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/65.0.3325.109 Safari/537.36 EdgA/42.0.0.2057'
       },
       {
-        title: 'Microsoft Edge (EdgeHTML) \u2014 Windows',
+        title: ls`Microsoft Edge (EdgeHTML) \u2014 Windows`,
         value:
             'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.102 Safari/537.36 Edge/18.18362'
       },
       {
-        title: 'Microsoft Edge (EdgeHTML) \u2014 XBox',
+        title: ls`Microsoft Edge (EdgeHTML) \u2014 XBox`,
         value:
             'Mozilla/5.0 (Windows NT 10.0; Win64; x64; Xbox; Xbox One) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.102 Safari/537.36 Edge/18.18362'
       }
     ]
   },
   {
-    title: 'Opera',
+    title: ls`Opera`,
     values: [
       {
-        title: 'Opera \u2014 Mac',
+        title: ls`Opera \u2014 Mac`,
         value:
             'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/50.0.2661.87 Safari/537.36 OPR/37.0.2178.31'
       },
       {
-        title: 'Opera \u2014 Windows',
+        title: ls`Opera \u2014 Windows`,
         value:
             'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/50.0.2661.87 Safari/537.36 OPR/37.0.2178.31'
       },
       {
-        title: 'Opera (Presto) \u2014 Mac',
+        title: ls`Opera (Presto) \u2014 Mac`,
         value: 'Opera/9.80 (Macintosh; Intel Mac OS X 10.9.1) Presto/2.12.388 Version/12.16'
       },
-      {title: 'Opera (Presto) \u2014 Windows', value: 'Opera/9.80 (Windows NT 6.1) Presto/2.12.388 Version/12.16'}, {
-        title: 'Opera Mobile \u2014 Android Mobile',
+      {title: ls`Opera (Presto) \u2014 Windows`, value: 'Opera/9.80 (Windows NT 6.1) Presto/2.12.388 Version/12.16'}, {
+        title: ls`Opera Mobile \u2014 Android Mobile`,
         value: 'Opera/12.02 (Android 4.1; Linux; Opera Mobi/ADR-1111101157; U; en-US) Presto/2.9.201 Version/12.02'
       },
       {
-        title: 'Opera Mini \u2014 iOS',
+        title: ls`Opera Mini \u2014 iOS`,
         value: 'Opera/9.80 (iPhone; Opera Mini/8.0.0/34.2336; U; en) Presto/2.8.119 Version/11.10'
       }
     ]
   },
   {
-    title: 'Safari',
+    title: ls`Safari`,
     values: [
       {
-        title: 'Safari \u2014 iPad iOS 9',
+        title: ls`Safari \u2014 iPad iOS 9`,
         value:
             'Mozilla/5.0 (iPad; CPU OS 9_1 like Mac OS X) AppleWebKit/601.1.46 (KHTML, like Gecko) Version/9.0 Mobile/13B137 Safari/601.1'
       },
       {
-        title: 'Safari \u2014 iPhone iOS 9',
+        title: ls`Safari \u2014 iPhone iOS 9`,
         value:
             'Mozilla/5.0 (iPhone; CPU iPhone OS 9_1 like Mac OS X) AppleWebKit/601.1.46 (KHTML, like Gecko) Version/9.0 Mobile/13B137 Safari/601.1'
       },
       {
-        title: 'Safari \u2014 Mac',
+        title: ls`Safari \u2014 Mac`,
         value:
             'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_9_3) AppleWebKit/537.75.14 (KHTML, like Gecko) Version/7.0.3 Safari/7046A194A'
       }
     ]
   },
   {
-    title: 'UC Browser',
+    title: ls`UC Browser`,
     values: [
       {
-        title: 'UC Browser \u2014 Android Mobile',
+        title: ls`UC Browser \u2014 Android Mobile`,
         value:
             'Mozilla/5.0 (Linux; U; Android 8.1.0; en-US; Nexus 6P Build/OPM7.181205.001) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/57.0.2987.108 UCBrowser/12.11.1.1197 Mobile Safari/537.36'
       },
       {
-        title: 'UC Browser \u2014 iOS',
+        title: ls`UC Browser \u2014 iOS`,
         value:
             'Mozilla/5.0 (iPhone; CPU iPhone OS 12_1 like Mac OS X; zh-CN) AppleWebKit/537.51.1 (KHTML, like Gecko) Mobile/16B92 UCBrowser/12.1.7.1109 Mobile AliApp(TUnionSDK/0.1.20.3)'
       },
       {
-        title: 'UC Browser \u2014 Windows Phone',
+        title: ls`UC Browser \u2014 Windows Phone`,
         value:
             'Mozilla/5.0 (compatible; MSIE 10.0; Windows Phone 8.0; Trident/6.0; IEMobile/10.0; ARM; Touch; NOKIA; Lumia 920) UCBrowser/10.1.0.563 Mobile'
       }
diff --git a/third_party/blink/renderer/devtools/front_end/network/network_strings.grdp b/third_party/blink/renderer/devtools/front_end/network/network_strings.grdp
index e320ea32..ef3f966 100644
--- a/third_party/blink/renderer/devtools/front_end/network/network_strings.grdp
+++ b/third_party/blink/renderer/devtools/front_end/network/network_strings.grdp
@@ -1,5 +1,8 @@
 <?xml version="1.0" encoding="utf-8"?>
 <grit-part>
+  <message name="IDS_DEVTOOLS_008aa5b6435af8504757085a92b2eb28" desc="An option in the user agent dropdown menu in the Network conditions tool">
+    <ph name="LOCKED_1">Microsoft Edge (EdgeHTML) — XBox</ph>
+  </message>
   <message name="IDS_DEVTOOLS_0194336f90a2b1de65553e3bec01becd" desc="Text in Network Log View of the Network panel">
     Copy as fetch
   </message>
@@ -18,6 +21,9 @@
   <message name="IDS_DEVTOOLS_059f48d76d79fe0727740d75dc81fa94" desc="Text in Network Log View of the Network panel">
     <ph name="SELECTEDTRANSFERSIZE">$1s<ex>25</ex></ph> B / <ph name="TRANSFERSIZE">$2s<ex>25</ex></ph> B transferred
   </message>
+  <message name="IDS_DEVTOOLS_05ffb3b626077843c5f71d13185391d8" desc="An option in the user agent dropdown menu in the Network conditions tool">
+    <ph name="LOCKED_1">Safari — iPad iOS 9</ph>
+  </message>
   <message name="IDS_DEVTOOLS_06337a0bc6e91b2adb10567bee4d6f14" desc="Text in Network Log View of the Network panel">
     Copy all as HAR
   </message>
@@ -36,6 +42,9 @@
   <message name="IDS_DEVTOOLS_0bcc4ec10c2e4cee4b9bbabd48501304" desc="Text in Resource Web Socket Frame View of the Network panel">
     Select message to browse its content.
   </message>
+  <message name="IDS_DEVTOOLS_0c1ac4502b106b89cbf1d3850f44ae12" desc="An option in the user agent dropdown menu in the Network conditions tool">
+    <ph name="LOCKED_1">Android (4.0.2) Browser — Galaxy Nexus</ph>
+  </message>
   <message name="IDS_DEVTOOLS_0db377921f4ce762c62526131097968f" desc="Text in Request Headers View of the Network panel">
     General
   </message>
@@ -54,6 +63,12 @@
   <message name="IDS_DEVTOOLS_12881f9e98bb240671bc3e1787d64a03" desc="Title of a setting under the Network category that can be invoked through the Command Menu">
     Color code by resource type
   </message>
+  <message name="IDS_DEVTOOLS_16c32be3b42643aee708e7ccdd52c9f4" desc="An option in the user agent dropdown menu in the Network conditions tool">
+    <ph name="LOCKED_1">Microsoft Edge — Android Mobile</ph>
+  </message>
+  <message name="IDS_DEVTOOLS_17439cab6b197fcaff231614384c9dcc" desc="An option in the user agent dropdown menu in the Network conditions tool">
+    <ph name="LOCKED_1">Opera (Presto) — Windows</ph>
+  </message>
   <message name="IDS_DEVTOOLS_1826c1f3d66791128e1f73f876147f2f" desc="A tag of Network color-code resource types that can be searched in the command menu">
     color code
   </message>
@@ -72,6 +87,9 @@
   <message name="IDS_DEVTOOLS_1b581452e4e1158b75e514c99a760431" desc="Text in Request Headers View of the Network panel">
     Request Payload
   </message>
+  <message name="IDS_DEVTOOLS_1ba63dd21ea9a100c54e9f5ed8a0d469" desc="An option in the user agent dropdown menu in the Network conditions tool">
+    <ph name="LOCKED_1">UC Browser — Windows Phone</ph>
+  </message>
   <message name="IDS_DEVTOOLS_1c047e32d57295a9f0319976c543162e" desc="Text in Network Data Grid Node of the Network panel">
     (blocked:<ph name="REASON">$1s<ex>mixed-content</ex></ph>)
   </message>
@@ -81,12 +99,24 @@
   <message name="IDS_DEVTOOLS_1cf0226d974af378b6aa6070bb2a37c3" desc="A tag of Network Conditions tool that can be searched in the command menu">
     network throttling
   </message>
+  <message name="IDS_DEVTOOLS_1d420bebb54e53a8d8a4d809576b58d5" desc="An option in the user agent dropdown menu in the Network conditions tool">
+    <ph name="LOCKED_1">Chrome — Mac</ph>
+  </message>
   <message name="IDS_DEVTOOLS_1dc4a71b18d2c3a1442267136158128e" desc="Text in Network Log View of the Network panel">
     Copy all as cURL (cmd)
   </message>
   <message name="IDS_DEVTOOLS_1eb445fa6bd078346bf63ecac35fa77c" desc="Text in Binary Resource View of the Network panel">
     Base64
   </message>
+  <message name="IDS_DEVTOOLS_20ed2e30da35e48e723855c8e14bfdf1" desc="An option in the user agent dropdown menu in the Network conditions tool">
+    <ph name="LOCKED_1">Microsoft Edge — Android Tablet</ph>
+  </message>
+  <message name="IDS_DEVTOOLS_21fd2e738b03921c90da8ac98319a04b" desc="An option in the user agent dropdown menu in the Network conditions tool">
+    <ph name="LOCKED_1">Chrome — Windows</ph>
+  </message>
+  <message name="IDS_DEVTOOLS_22f3e616a52e25632d98fd53d3f821ed" desc="An option in the user agent dropdown menu in the Network conditions tool">
+    <ph name="LOCKED_1">Safari — Mac</ph>
+  </message>
   <message name="IDS_DEVTOOLS_2537b3e6907e17001b7cdf121cd39dc0" desc="Text in Request Headers View of the Network panel">
     (from ServiceWorker)
   </message>
@@ -102,6 +132,9 @@
   <message name="IDS_DEVTOOLS_29d37bdcfeb74d25d56bcf841866ae32" desc="Cell title in Network Data Grid Node of the Network panel">
     Served from ServiceWorker, resource size: <ph name="RESOURCESIZE">$1s<ex>4 B</ex></ph>
   </message>
+  <message name="IDS_DEVTOOLS_2ad7f735dd6d0dedb0d010a612854a17" desc="An option in the user agent dropdown menu in the Network conditions tool">
+    <ph name="LOCKED_1">Internet Explorer 8</ph>
+  </message>
   <message name="IDS_DEVTOOLS_2d13df6f8b5e4c5af9f87e0dc39df69d" desc="Text in Network Data Grid Node of the Network panel">
     Pending
   </message>
@@ -135,6 +168,9 @@
   <message name="IDS_DEVTOOLS_37bb3bd9caf5dafb7c8f9eccb3b9800e" desc="Text in Request Headers View of the Network panel">
     (unable to decode value)
   </message>
+  <message name="IDS_DEVTOOLS_37eabd36dcdd3c4f4586c88a7354d10d" desc="An option in the user agent dropdown menu in the Network conditions tool">
+    <ph name="LOCKED_1">Microsoft Edge (Chromium) — Windows</ph>
+  </message>
   <message name="IDS_DEVTOOLS_383fc50a38f1666adc3f06d0bdf71467" desc="Text in Network Log View of the Network panel">
     Are you sure you want to clear browser cookies?
   </message>
@@ -159,6 +195,15 @@
   <message name="IDS_DEVTOOLS_3e0dc7bd31f5ad41b900fef5b7aba791" desc="Title of a setting under the Network category that can be invoked through the Command Menu">
     Don&apos;t group network log items by frame
   </message>
+  <message name="IDS_DEVTOOLS_3ebfafd757759c5e0081920b36450c58" desc="An option in the user agent dropdown menu in the Network conditions tool">
+    <ph name="LOCKED_1">Firefox — Android Mobile</ph>
+  </message>
+  <message name="IDS_DEVTOOLS_408f8a4ecdacbe1d9989b800a0c12936" desc="An option in the user agent dropdown menu in the Network conditions tool">
+    <ph name="LOCKED_1">Internet Explorer 11</ph>
+  </message>
+  <message name="IDS_DEVTOOLS_4145ea54b6f576d7646d6a68eb39813f" desc="An option in the user agent dropdown menu in the Network conditions tool">
+    <ph name="LOCKED_1">Opera (Presto) — Mac</ph>
+  </message>
   <message name="IDS_DEVTOOLS_41de6d6cfb8953c021bbe4ba0701c8a1" desc="Text in Network Item View of the Network panel">
     Messages
   </message>
@@ -174,6 +219,9 @@
   <message name="IDS_DEVTOOLS_44749712dbec183e983dcd78a7736c41" desc="Text in Signed Exchange Info View of the Network panel">
     Date
   </message>
+  <message name="IDS_DEVTOOLS_44bea05ea1f4af197ad4b6453e04a0eb" desc="An option in the user agent dropdown menu in the Network conditions tool">
+    <ph name="LOCKED_1">Firefox — Android Tablet</ph>
+  </message>
   <message name="IDS_DEVTOOLS_4566ce384ed4a889beb6f6432d5906d7" desc="The UI destination when right clicking an item that can be revealed">
     Network panel
   </message>
@@ -207,12 +255,18 @@
   <message name="IDS_DEVTOOLS_5023e5b1d1c02cf168a9a67dda6d2d87" desc="Text in Network Panel of the Network panel">
     Show overview
   </message>
+  <message name="IDS_DEVTOOLS_50de9bc68c93dc32d8c7c90593471760" desc="A group title in the user agent dropdown menu in the Network conditions tool">
+    <ph name="LOCKED_1">Safari</ph>
+  </message>
   <message name="IDS_DEVTOOLS_5101cdda28ea3097ac00cdbfdcedc353" desc="Title of a setting under the Network category that can be invoked through the Command Menu">
     Use default colors
   </message>
   <message name="IDS_DEVTOOLS_53fa249ce3d02c8a686b657b31d02ee7" desc="Text in Network Log View of the Network panel">
     Copy response
   </message>
+  <message name="IDS_DEVTOOLS_549fae23948f165d8dbb68c9a17b8c44" desc="An option in the user agent dropdown menu in the Network conditions tool">
+    <ph name="LOCKED_1">Chrome — Android Mobile</ph>
+  </message>
   <message name="IDS_DEVTOOLS_5639d3df5aea3d9e78027b843ee3cbb2" desc="Text in Resource Web Socket Frame View of the Network panel">
     Receive
   </message>
@@ -231,9 +285,15 @@
   <message name="IDS_DEVTOOLS_59535aed1e49266dfd0b93172f5a7242" desc="Text in Network Panel of the Network panel">
     Group by frame
   </message>
+  <message name="IDS_DEVTOOLS_59f2df528ff5c941c8d6308288a83141" desc="An option in the user agent dropdown menu in the Network conditions tool">
+    <ph name="LOCKED_1">Chrome — iPad</ph>
+  </message>
   <message name="IDS_DEVTOOLS_5cb98890f19c13e5fce52287187d9803" desc="Text in Network Log View of the Network panel">
     <ph name="SELECTEDNODENUMBER">$1s<ex>3</ex></ph> / <ph name="NODECOUNT">$2s<ex>5</ex></ph> requests
   </message>
+  <message name="IDS_DEVTOOLS_5d849c6c271a039aba239a13132c5dd5" desc="An option in the user agent dropdown menu in the Network conditions tool">
+    <ph name="LOCKED_1">Firefox — Mac</ph>
+  </message>
   <message name="IDS_DEVTOOLS_5e2f683aacb9d5f3635b3f88583dec80" desc="Text in Network Config View of the Network panel">
     Custom...
   </message>
@@ -264,6 +324,9 @@
   <message name="IDS_DEVTOOLS_6a98894cd6b4628f0b5117412aab083e" desc="Text in Network Log View Columns of the Network panel">
     Cache-Control
   </message>
+  <message name="IDS_DEVTOOLS_6b41d86d21d49c95f5b106fb2ed95762" desc="An option in the user agent dropdown menu in the Network conditions tool">
+    <ph name="LOCKED_1">Internet Explorer 10</ph>
+  </message>
   <message name="IDS_DEVTOOLS_6c2a1bf87fb84c91fc7e1120f4e3d0c7" desc="Text in Request Timing View of the Network panel">
     Content Download
   </message>
@@ -279,12 +342,18 @@
   <message name="IDS_DEVTOOLS_6e68a529a38966508d348e9f65d7ea31" desc="Title of an action in the network tool to toggle recording">
     Record network log
   </message>
+  <message name="IDS_DEVTOOLS_71f5989793ddede100da3fde07d1f9f9" desc="An option in the user agent dropdown menu in the Network conditions tool">
+    <ph name="LOCKED_1">Opera Mini — iOS</ph>
+  </message>
   <message name="IDS_DEVTOOLS_71fb2761cd2a446400a8069fe16cd45e" desc="A context menu item in the Network Log View of the Network panel">
     Block request domain
   </message>
   <message name="IDS_DEVTOOLS_722e6ea747a62b7f93bb017d3dd04cbd" desc="Op codes continuation frame of map in Resource Web Socket Frame View of the Network panel">
     Continuation Frame
   </message>
+  <message name="IDS_DEVTOOLS_f5a6d40aab4d260009df79b7dab7e6a0" desc="An option in the user agent dropdown menu in the Network conditions tool">
+    <ph name="LOCKED_1">Microsoft Edge — iPhone</ph>
+  </message>
   <message name="IDS_DEVTOOLS_72d569ab3718d10a89315f80cf05cc73" desc="Title of a setting under the Network category in Settings">
     Group network log by frame
   </message>
@@ -294,9 +363,15 @@
   <message name="IDS_DEVTOOLS_7543ba96bb16a5f17f8fb25bdd995d3f" desc="Text in Binary Resource View of the Network panel">
     Copy as Base64
   </message>
+  <message name="IDS_DEVTOOLS_763f7f1aec350cd1a46238d1d5c3c229" desc="A group title in the user agent dropdown menu in the Network conditions tool">
+    <ph name="LOCKED_1">Firefox</ph>
+  </message>
   <message name="IDS_DEVTOOLS_76669aaf74dda2a59e3c363da10c3faf" desc="Text in Request Timing View of the Network panel">
     Reading Push
   </message>
+  <message name="IDS_DEVTOOLS_77c0ed7e4536e1c251279fa0c90bca50" desc="An option in the user agent dropdown menu in the Network conditions tool">
+    <ph name="LOCKED_1">Microsoft Edge (EdgeHTML) — Windows</ph>
+  </message>
   <message name="IDS_DEVTOOLS_77f02f5047451625e1c6bb24884cdfe6" desc="Text in Binary Resource View of the Network panel">
     Copied as Hex
   </message>
@@ -309,6 +384,12 @@
   <message name="IDS_DEVTOOLS_795f3202b17cb6bc3d4b771d8c6c9eaf" desc="Reason in Network Data Grid Node of the Network panel">
     other
   </message>
+  <message name="IDS_DEVTOOLS_7a4feae759066e44a88f2e6610d5a757" desc="An option in the user agent dropdown menu in the Network conditions tool">
+    <ph name="LOCKED_1">Opera — Mac</ph>
+  </message>
+  <message name="IDS_DEVTOOLS_7a8cfc6a08f7d15e1d612ea47e68b67c" desc="An option in the user agent dropdown menu in the Network conditions tool">
+    <ph name="LOCKED_1">Internet Explorer 7</ph>
+  </message>
   <message name="IDS_DEVTOOLS_7b5bea45778433caf34b0ba556a7d93b" desc="Text in Network Data Grid Node of the Network panel">
     (failed)
   </message>
@@ -321,12 +402,21 @@
   <message name="IDS_DEVTOOLS_7e1c4c7b01e0fd0ad1e89ca7512f353e" desc="Op codes ping frame of map in Resource Web Socket Frame View of the Network panel">
     Ping Message
   </message>
+  <message name="IDS_DEVTOOLS_7e2709c4d40a9827390281d60aae22a7" desc="An option in the user agent dropdown menu in the Network conditions tool">
+    <ph name="LOCKED_1">Firefox — iPhone</ph>
+  </message>
+  <message name="IDS_DEVTOOLS_7ee22334256f1cf5538adc052ebf96bf" desc="An option in the user agent dropdown menu in the Network conditions tool">
+    <ph name="LOCKED_1">Safari — iPhone iOS 9</ph>
+  </message>
   <message name="IDS_DEVTOOLS_804910399e8d26511871e33416ccd127" desc="Cell title in Network Data Grid Node of the Network panel">
     Served from disk cache, resource size: <ph name="RESOURCESIZE">$1s<ex>10 B</ex></ph>
   </message>
   <message name="IDS_DEVTOOLS_814f9c3c7e7aa04c21f2e61f5b2dcf18" desc="Text in Network Log View of the Network panel">
     Copy response headers
   </message>
+  <message name="IDS_DEVTOOLS_81c1f883582dfa740bba882106414bd3" desc="An option in the user agent dropdown menu in the Network conditions tool">
+    <ph name="LOCKED_1">BlackBerry — PlayBook 2.1</ph>
+  </message>
   <message name="IDS_DEVTOOLS_827452688eed02a12178e96f924ac529" desc="Text in Network Log View Columns of the Network panel">
     Scheme
   </message>
@@ -411,6 +501,12 @@
   <message name="IDS_DEVTOOLS_97f0a2ba508f8299c686818366ad712a" desc="Text in Network Data Grid Node of the Network panel">
     (data)
   </message>
+  <message name="IDS_DEVTOOLS_9813946564c3a3698618ad520f6d1e69" desc="A group title in the user agent dropdown menu in the Network conditions tool">
+    <ph name="LOCKED_1">BlackBerry</ph>
+  </message>
+  <message name="IDS_DEVTOOLS_9825e566e3304756959d9ba52b5903d3" desc="An option in the user agent dropdown menu in the Network conditions tool">
+    <ph name="LOCKED_1">Chrome — Android Tablet</ph>
+  </message>
   <message name="IDS_DEVTOOLS_985187565936a7c1e02c9f96852c0f9a" desc="Reason in Network Data Grid Node of the Network panel">
     subresource-filter
   </message>
@@ -420,6 +516,9 @@
   <message name="IDS_DEVTOOLS_986491ef3610ed7ef82bd863e4ee00a2" desc="Title of the 'Request blocking' tool in the bottom drawer">
     Request blocking
   </message>
+  <message name="IDS_DEVTOOLS_986c37480b1f1c2e443504b38b6361b4" desc="A group title in the user agent dropdown menu in the Network conditions tool">
+    <ph name="LOCKED_1">Chrome</ph>
+  </message>
   <message name="IDS_DEVTOOLS_9979c41f94d4d232c06b4c87f2d0f951" desc="Text in Network Data Grid Node of the Network panel">
     Push / '''
   </message>
@@ -453,6 +552,12 @@
   <message name="IDS_DEVTOOLS_a09b75f1cd4e3d54099fa8e14ff5056d" desc="From service worker format in Network Time Calculator of the Network panel">
     <ph name="PH1">$1s<ex>20ms latency</ex></ph> (from ServiceWorker)
   </message>
+  <message name="IDS_DEVTOOLS_a12a6481ad87f558622dcf0a1a87982a" desc="An option in the user agent dropdown menu in the Network conditions tool">
+    <ph name="LOCKED_1">UC Browser — Android Mobile</ph>
+  </message>
+  <message name="IDS_DEVTOOLS_a16761bfe43337abda8aedb823fe0a10" desc="An option in the user agent dropdown menu in the Network conditions tool">
+    <ph name="LOCKED_1">BlackBerry — 9900</ph>
+  </message>
   <message name="IDS_DEVTOOLS_a217246afb12245a032617715f81923b" desc="Text in Network Manage Custom Headers View of the Network panel">
     Manage Header Columns
   </message>
@@ -465,6 +570,9 @@
   <message name="IDS_DEVTOOLS_a299d2f506523f8104dc4202de7b0c64" desc="Tooltip text that appears when hovering over the largeicon download button in the Network Panel of the Network panel">
     Export HAR...
   </message>
+  <message name="IDS_DEVTOOLS_a5c41f715c1b8a880253846c025624e9" desc="A group title in the user agent dropdown menu in the Network conditions tool">
+    <ph name="LOCKED_1">Microsoft Edge</ph>
+  </message>
   <message name="IDS_DEVTOOLS_a5d6e0224f694699dde6d1bd34d608bd" desc="Text in Network Log View of the Network panel">
     Copy as cURL
   </message>
@@ -474,6 +582,9 @@
   <message name="IDS_DEVTOOLS_a74e6894e1f36ce12a6773b9a6b9aea8" desc="Text in Network Log View of the Network panel">
     <ph name="NODECOUNT">$1s<ex>6</ex></ph> requests
   </message>
+  <message name="IDS_DEVTOOLS_a956e985535b8d57732d14dfa0e4c71a" desc="A group title in the user agent dropdown menu in the Network conditions tool">
+    <ph name="LOCKED_1">Opera</ph>
+  </message>
   <message name="IDS_DEVTOOLS_a976624c5265b8187b19aea9df9230eb" desc="Title of a setting under the Network category in Settings">
     Color-code resource types
   </message>
@@ -483,6 +594,9 @@
   <message name="IDS_DEVTOOLS_ac5eb504b5a840271e066bf6fd3000b8" desc="Text in Request Headers View of the Network panel">
     view parsed
   </message>
+  <message name="IDS_DEVTOOLS_ac90a9ac36eb64388ce0e0a9047b295c" desc="An option in the user agent dropdown menu in the Network conditions tool">
+    <ph name="LOCKED_1">Android (2.3) Browser — Nexus S</ph>
+  </message>
   <message name="IDS_DEVTOOLS_ad86890fb40822a3b12627efaca4ecd7" desc="Text of a DOM element in Network Data Grid Node of the Network panel">
     (disk cache)
   </message>
@@ -492,6 +606,9 @@
   <message name="IDS_DEVTOOLS_ae3b3df9970b49b6523e608759bc957d" desc="Text in Binary Resource View of the Network panel">
     UTF-8
   </message>
+  <message name="IDS_DEVTOOLS_ae89abb57630fcbd1af25cfc6684a5ff" desc="An option in the user agent dropdown menu in the Network conditions tool">
+    <ph name="LOCKED_1">Opera Mobile — Android Mobile</ph>
+  </message>
   <message name="IDS_DEVTOOLS_af0a229a0c5be77de07889743db3b409" desc="Text in Binary Resource View of the Network panel">
     Copied as UTF-8
   </message>
@@ -501,6 +618,15 @@
   <message name="IDS_DEVTOOLS_b021df6aac4654c454f46c77646e745f" desc="Text in Signed Exchange Info View of the Network panel">
     Label
   </message>
+  <message name="IDS_DEVTOOLS_b04f746dac7a367d85c411341b6673f6" desc="A group title in the user agent dropdown menu in the Network conditions tool">
+    <ph name="LOCKED_1">Googlebot</ph>
+  </message>
+  <message name="IDS_DEVTOOLS_b08687e344501a098d281a89e77aff6b" desc="An option in the user agent dropdown menu in the Network conditions tool">
+    <ph name="LOCKED_1">Opera — Windows</ph>
+  </message>
+  <message name="IDS_DEVTOOLS_b1b2ed29de5b9a2a8cf8ba5cf9e52624" desc="An option in the user agent dropdown menu in the Network conditions tool">
+    <ph name="LOCKED_1">Chrome — Chrome OS</ph>
+  </message>
   <message name="IDS_DEVTOOLS_b1f63146853f7f450d8622aae6a73408" desc="Text in Network Log View of the Network panel">
     Copy as cURL (bash)
   </message>
@@ -510,6 +636,9 @@
   <message name="IDS_DEVTOOLS_b72ac10807b29c77f5b7e4b80ea40414" desc="Text in Request Timing View of the Network panel">
     Explanation
   </message>
+  <message name="IDS_DEVTOOLS_b87b6ab319ae2ed428153cd7b6441a90" desc="An option in the user agent dropdown menu in the Network conditions tool">
+    <ph name="LOCKED_1">Firefox — iPad</ph>
+  </message>
   <message name="IDS_DEVTOOLS_b926fd82158cde57655d0cd1dd8dbc70" desc="Text in Request Headers View of the Network panel">
     Query String Parameters
   </message>
@@ -543,6 +672,9 @@
   <message name="IDS_DEVTOOLS_c0635a52980f98eff8adf2279c8ad8e0" desc="A tag of Network Conditions tool that can be searched in the command menu">
     <ph name="LOCKED_1">useragent</ph>
   </message>
+  <message name="IDS_DEVTOOLS_c14108bc627e61f0e445dda0ff029f58" desc="An option in the user agent dropdown menu in the Network conditions tool">
+    <ph name="LOCKED_1">Chrome — iPhone</ph>
+  </message>
   <message name="IDS_DEVTOOLS_c3c14eb17a6cf9c6120f381790ed06eb" desc="Text in Network Log View of the Network panel">
     Copy all as PowerShell
   </message>
@@ -561,9 +693,15 @@
   <message name="IDS_DEVTOOLS_c84718b71b8ad70dde23736e79e25e83" desc="Op codes pong frame of map in Resource Web Socket Frame View of the Network panel">
     Pong Message
   </message>
+  <message name="IDS_DEVTOOLS_c89585ee0469e9a3824828ae8961555b" desc="An option in the user agent dropdown menu in the Network conditions tool">
+    <ph name="LOCKED_1">Microsoft Edge (Chromium) — Mac</ph>
+  </message>
   <message name="IDS_DEVTOOLS_c91a577b72313356fad611c55f43c10f" desc="Text in Network Log View Columns of the Network panel">
     End Time
   </message>
+  <message name="IDS_DEVTOOLS_caae91004cd7ecb81b25a7c4536e289f" desc="An option in the user agent dropdown menu in the Network conditions tool">
+    <ph name="LOCKED_1">UC Browser — iOS</ph>
+  </message>
   <message name="IDS_DEVTOOLS_cb64a2679b345ae476ea3a7fb6a70080" desc="Op codes binary frame of map in Resource Web Socket Frame View of the Network panel">
     Binary Message
   </message>
@@ -588,9 +726,15 @@
   <message name="IDS_DEVTOOLS_d0e5383d7c91948cfab6cacccec8812d" desc="Text in Request Headers View of the Network panel">
     (from signed-exchange)
   </message>
+  <message name="IDS_DEVTOOLS_d21a4ca270bc9a879992b4b3d9510a60" desc="A group title in the user agent dropdown menu in the Network conditions tool">
+    <ph name="LOCKED_1">UC Browser</ph>
+  </message>
   <message name="IDS_DEVTOOLS_d3b69e993f4e9bf9c479c7e794ede387" desc="Text in Network Item View of the Network panel">
     Timing
   </message>
+  <message name="IDS_DEVTOOLS_d6c3c06d819624b743db87f8e1384f58" desc="An option in the user agent dropdown menu in the Network conditions tool">
+    <ph name="LOCKED_1">Firefox — Windows</ph>
+  </message>
   <message name="IDS_DEVTOOLS_d76e44f856d0114826e7436a630a3fa7" desc="Text of a DOM element in Network Data Grid Node of the Network panel">
     (signed-exchange)
   </message>
@@ -657,12 +801,24 @@
   <message name="IDS_DEVTOOLS_e6fb9da54f521b9c33d41121d4fcd35c" desc="Text of a DOM element in Network Data Grid Node of the Network panel">
     (memory cache)
   </message>
+  <message name="IDS_DEVTOOLS_e84e30b9390cdb64db6db2c9ab87846d" desc="A group title in the user agent dropdown menu in the Network conditions tool">
+    <ph name="LOCKED_1">Android</ph>
+  </message>
+  <message name="IDS_DEVTOOLS_e930f2148ae22be78152a04aa1a290b7" desc="An option in the user agent dropdown menu in the Network conditions tool">
+    <ph name="LOCKED_1">BlackBerry — BB10</ph>
+  </message>
   <message name="IDS_DEVTOOLS_ea52c36203c5f99c3ce2442d531b1a22" desc="Text in Request Timing View of the Network panel">
     SSL
   </message>
   <message name="IDS_DEVTOOLS_eb0bd7de3ba805621bf9e03e4d16b510" desc="Text of a DOM element in Request Timing View of the Network panel">
     Queued at <ph name="CALCULATOR_FORMATVALUE_REQUEST_ISSUETIME______">$1s<ex>120.39ms</ex></ph>
   </message>
+  <message name="IDS_DEVTOOLS_ebe4f447bd3316e4f81ab2a6d2f26810" desc="A group title in the user agent dropdown menu in the Network conditions tool">
+    <ph name="LOCKED_1">Internet Explorer</ph>
+  </message>
+  <message name="IDS_DEVTOOLS_ec0492b0eb0336c99c83c68f109b3561" desc="An option in the user agent dropdown menu in the Network conditions tool">
+    <ph name="LOCKED_1">Microsoft Edge — iPad</ph>
+  </message>
   <message name="IDS_DEVTOOLS_ec559fc895e8cc77ef0c4d6fdff5bdcb" desc="Text in Request Headers View of the Network panel">
     Form Data
   </message>
@@ -732,4 +888,10 @@
   <message name="IDS_DEVTOOLS_fe1bc3eb2f3e1a9a90b5401eb6baa5b9" desc="Text in Network Log View of the Network panel">
     Copy all as cURL
   </message>
+  <message name="IDS_DEVTOOLS_fec7b18500d718dc2ba515971366d74e" desc="An option in the user agent dropdown menu in the Network conditions tool">
+    <ph name="LOCKED_1">Internet Explorer 9</ph>
+  </message>
+  <message name="IDS_DEVTOOLS_fef333b290196b891e522e03e5c9fd06" desc="An option in the user agent dropdown menu in the Network conditions tool">
+    <ph name="LOCKED_1">Googlebot Smartphone</ph>
+  </message>
 </grit-part>
diff --git a/third_party/blink/renderer/devtools/front_end/resources/ServiceWorkersView.js b/third_party/blink/renderer/devtools/front_end/resources/ServiceWorkersView.js
index 3a96d898..351e1d39 100644
--- a/third_party/blink/renderer/devtools/front_end/resources/ServiceWorkersView.js
+++ b/third_party/blink/renderer/devtools/front_end/resources/ServiceWorkersView.js
@@ -471,9 +471,9 @@
 
     if (active) {
       this._updateSourceField(active);
+      const localizedRunningStatus = SDK.ServiceWorkerVersion.RunningStatus[active.runningStatus];
       const activeEntry = this._addVersion(
-          versionsStack, 'service-worker-active-circle',
-          Common.UIString('#%s activated and is %s', active.id, active.runningStatus));
+          versionsStack, 'service-worker-active-circle', ls`#${active.id} activated and is ${localizedRunningStatus}`);
 
       if (active.isRunning() || active.isStarting()) {
         this._createLink(activeEntry, Common.UIString('stop'), this._stopButtonClicked.bind(this, active.id));
diff --git a/third_party/blink/renderer/devtools/front_end/sdk/ServiceWorkerManager.js b/third_party/blink/renderer/devtools/front_end/sdk/ServiceWorkerManager.js
index a5914df..649be99 100644
--- a/third_party/blink/renderer/devtools/front_end/sdk/ServiceWorkerManager.js
+++ b/third_party/blink/renderer/devtools/front_end/sdk/ServiceWorkerManager.js
@@ -444,6 +444,16 @@
 };
 
 /**
+ * @type {!Object<string, string>}
+ */
+SDK.ServiceWorkerVersion.RunningStatus = {
+  [Protocol.ServiceWorker.ServiceWorkerVersionRunningStatus.Running]: ls`running`,
+  [Protocol.ServiceWorker.ServiceWorkerVersionRunningStatus.Starting]: ls`starting`,
+  [Protocol.ServiceWorker.ServiceWorkerVersionRunningStatus.Stopped]: ls`stopped`,
+  [Protocol.ServiceWorker.ServiceWorkerVersionRunningStatus.Stopping]: ls`stopping`,
+};
+
+/**
  * @enum {string}
  */
 SDK.ServiceWorkerVersion.Modes = {
diff --git a/third_party/blink/renderer/devtools/front_end/sdk/sdk_strings.grdp b/third_party/blink/renderer/devtools/front_end/sdk/sdk_strings.grdp
index 75b1e17a..7c854d8 100644
--- a/third_party/blink/renderer/devtools/front_end/sdk/sdk_strings.grdp
+++ b/third_party/blink/renderer/devtools/front_end/sdk/sdk_strings.grdp
@@ -30,6 +30,9 @@
   <message name="IDS_DEVTOOLS_1a4157cb54ce78ee862a88ecd1a66916" desc="Tooltip to explain why a cookie was blocked">
     This set-cookie used the &quot;__Secure-&quot; or &quot;__Host-&quot; prefix in its name and broke the additional rules applied to cookies with these prefixes as defined in https://tools.ietf.org/html/draft-west-cookie-prefixes-05.
   </message>
+  <message name="IDS_DEVTOOLS_1ee85f6c60017a7f0646ba8dc5824de6" desc="Service worker running status displayed in the Service Workers view in the Application panel">
+    starting
+  </message>
   <message name="IDS_DEVTOOLS_21070fd87b02611a5426d726a70a5502" desc="Text in Server Timing">
     No value found for parameter &quot;<ph name="PARAMNAME">$1s<ex>https</ex></ph>&quot;.
   </message>
@@ -72,6 +75,9 @@
   <message name="IDS_DEVTOOLS_38fe155e13d77298cfd5688c3378fa85" desc="Title of a setting under the Rendering category that can be invoked through the Command Menu">
     Emulate CSS screen media type
   </message>
+  <message name="IDS_DEVTOOLS_3b2a3c8ed19fc3647432e72885d633e7" desc="Service worker running status displayed in the Service Workers view in the Application panel">
+    stopping
+  </message>
   <message name="IDS_DEVTOOLS_3b563524fdb17b4a86590470d40bef74" desc="Text in DOMDebugger Model">
     Media
   </message>
@@ -168,6 +174,9 @@
   <message name="IDS_DEVTOOLS_7121afd196f5c52bef488d5a0f4c097b" desc="Text in the Event Listener Breakpoints Panel of the JavaScript Debugger in the Sources Panel">
     Script First Statement
   </message>
+  <message name="IDS_DEVTOOLS_75101dcdfc88455bcafc9e53e0b06689" desc="Service worker running status displayed in the Service Workers view in the Application panel">
+    running
+  </message>
   <message name="IDS_DEVTOOLS_77454122d17d16885c51c0d57f5c7bd3" desc="Tooltip to explain why a cookie was blocked">
     This cookie had the &quot;SameSite=Strict&quot; attribute and the request was made on on a different site. This includes navigation requests initiated by other sites.
   </message>
@@ -327,6 +336,9 @@
   <message name="IDS_DEVTOOLS_eff8d530f562a22a45fbf1ee83299353" desc="Title of a setting under the Rendering category that can be invoked through the Command Menu">
     Show scroll performance bottlenecks
   </message>
+  <message name="IDS_DEVTOOLS_f0a0bfe6bc7d2c58d2989034f83183e0" desc="Service worker running status displayed in the Service Workers view in the Application panel">
+    stopped
+  </message>
   <message name="IDS_DEVTOOLS_f107ac919ab14339cdb22cd00fccd215" desc="Title of a setting under the Rendering category that can be invoked through the Command Menu">
     Show paint flashing rectangles
   </message>
diff --git a/third_party/blink/renderer/devtools/front_end/text_editor/cmdevtools.css b/third_party/blink/renderer/devtools/front_end/text_editor/cmdevtools.css
index 28d1496..78fa25a 100644
--- a/third_party/blink/renderer/devtools/front_end/text_editor/cmdevtools.css
+++ b/third_party/blink/renderer/devtools/front_end/text_editor/cmdevtools.css
@@ -309,15 +309,6 @@
     background-color: rgb(171, 191, 254);
 }
 
-.cm-execution-line .CodeMirror-linenumber::after {
-    position: absolute;
-    top:-1px;
-    bottom:-1px;
-    right:-1.5em;
-    content:"";
-    border-right: 1px solid rgb(64, 115, 244);
-}
-
 :host-context(.-theme-with-dark-background) .cm-execution-line,
 .-theme-with-dark-background .cm-execution-line {
     background-color: #14522b;
diff --git a/third_party/blink/renderer/devtools/scripts/build/generate_devtools_ui_strings.js b/third_party/blink/renderer/devtools/scripts/build/generate_devtools_ui_strings.js
new file mode 100644
index 0000000..bbe13f9
--- /dev/null
+++ b/third_party/blink/renderer/devtools/scripts/build/generate_devtools_ui_strings.js
@@ -0,0 +1,124 @@
+// 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.
+
+/**
+ * This script is called by generate_devtools_ui_strings.py as part of the build process. It
+ * parses DevTools frontend .js and module.json files, collects localizable strings, checks
+ * if frontend strings are in .grd/.grdp files and reports error if present, and generates
+ * {jsonKey, IDS_KEY} mappings if there is no error.
+ *
+ * Usage:
+ *   --root_gen_dir      The root directory of the output .h and .cc files
+ *   --output_header     Absolute path of the output .h file for the id mappings
+ *   --output_cc         Absolute path of the output .cc file for the id mappings
+ */
+
+const checkLocalizedStrings = require('../localization_utils/check_localized_strings');
+const localizationUtils = require('../localization_utils/localization_utils');
+
+const fs = require('fs');
+const path = require('path');
+const {promisify} = require('util');
+const writeFileAsync = promisify(fs.writeFile);
+
+class Arguments {
+  constructor(rootGenDir, outputHeaderFilePath, outputCCFilePath) {
+    this.rootGenDir = rootGenDir;
+    this.outputHeaderFilePath = outputHeaderFilePath;
+    this.outputCCFilePath = outputCCFilePath;
+  }
+}
+
+function parseArguments(args) {
+  const rootGenDirIndex = args.indexOf('--root_gen_dir');
+  const outputHeaderIndex = args.indexOf('--output_header');
+  const outputCCIndex = args.indexOf('--output_cc');
+  return new Arguments(args[rootGenDirIndex + 1], args[outputHeaderIndex + 1], args[outputCCIndex + 1]);
+}
+
+async function main() {
+  const args = parseArguments(process.argv);
+  let frontendStrings;
+  try {
+    [frontendStrings, _] = await checkLocalizedStrings.parseLocalizableResourceMaps();
+  } catch (e) {
+    console.log(e);
+    process.exit(1);
+  }
+
+  const toAddError = checkLocalizedStrings.getAndReportResourcesToAdd();
+  const toModifyError = checkLocalizedStrings.getAndReportIDSKeysToModify();
+  const toRemoveError = checkLocalizedStrings.getAndReportResourcesToRemove();
+  let error = `${toAddError ? `${toAddError}\n` : ''}${toModifyError ? `${toModifyError}\n` : ''}${
+      toRemoveError ? `${toRemoveError}\n` : ''}`;
+  if (error !== '') {
+    error +=
+        '\nThe errors are potentially fixable with `node third_party/blink/renderer/devtools/scripts/check_localizable_resources.js --autofix`'
+    console.log(error);
+  }
+
+  // Since it's part of the build system, only fail if there are strings to be added to GRD/GRDP files
+  // or if there are wrong IDS_ keys.
+  if (toAddError || toModifyError)
+    process.exit(1);
+
+  try {
+    await generateDevToolsLocalizedStrings(args, frontendStrings);
+  } catch (e) {
+    console.log('Error generating id map files:');
+    console.log(e.stack);
+    process.exit(1);
+  }
+}
+
+// Generates {jsonKey, IDS_KEY} mappings according to frontendStrings
+async function generateDevToolsLocalizedStrings(args, frontendStrings) {
+  const promises = [];
+  const outputAbsoluteHeaderFilePath = path.join(args.rootGenDir, args.outputHeaderFilePath);
+  const outputAbsoluteCCFilePath = path.join(args.rootGenDir, args.outputCCFilePath);
+  const doNotEditStr =
+      `// This file is automatically generated by //third_party/blink/rendered/devtools/build/generate_devtools_ui_strings.js. Do not edit.`;
+  const outputHeaderFileContent = `${doNotEditStr}
+
+#ifndef CHROME_BROWSER_UI_WEBUI_DEVTOOLS_UI_STRINGS_H_
+#define CHROME_BROWSER_UI_WEBUI_DEVTOOLS_UI_STRINGS_H_
+
+#include "chrome/browser/ui/webui/localized_string.h"
+
+namespace devtools {
+
+constexpr unsigned int kLocalizedStringsSize = ${frontendStrings.size};
+extern const LocalizedString kLocalizedStrings[kLocalizedStringsSize];
+
+} // namespace devtools
+
+#endif // CHROME_BROWSER_UI_WEBUI_DEVTOOLS_UI_STRINGS_H_
+`;
+
+  promises.push(writeFileAsync(outputAbsoluteHeaderFilePath, outputHeaderFileContent));
+
+  let mappingsStr = '';
+  frontendStrings.forEach((frontendString, idsKey) => {
+    mappingsStr += `  {"${localizationUtils.sanitizeStringIntoCppFormat(frontendString.string)}", ${idsKey}},\n`;
+  });
+
+  const outputCCFileContent = `${doNotEditStr}
+
+#include "${args.outputHeaderFilePath}"
+
+#include "third_party/blink/renderer/devtools/front_end/langpacks/devtools_ui_strings.h"
+
+namespace devtools {
+
+const LocalizedString kLocalizedStrings[] = {
+${mappingsStr}};
+
+} // namespace devtools
+`;
+
+  promises.push(writeFileAsync(outputAbsoluteCCFilePath, outputCCFileContent));
+  return Promise.all(promises);
+}
+
+main();
diff --git a/third_party/blink/renderer/devtools/scripts/build/generate_devtools_ui_strings.py b/third_party/blink/renderer/devtools/scripts/build/generate_devtools_ui_strings.py
new file mode 100644
index 0000000..62a9f57
--- /dev/null
+++ b/third_party/blink/renderer/devtools/scripts/build/generate_devtools_ui_strings.py
@@ -0,0 +1,53 @@
+# 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 optparse
+import os
+import subprocess
+import sys
+
+_HERE_PATH = os.path.abspath(os.path.dirname(__file__))
+_JS_SCRIPT_PATH = os.path.join(_HERE_PATH, 'generate_devtools_ui_strings.js')
+_SRC_PATH = os.path.normpath(os.path.join(_HERE_PATH, '..', '..', '..', '..', '..', '..'))
+
+sys.path.append(os.path.join(_SRC_PATH, 'third_party', 'node'))
+# pylint: disable=wrong-import-position
+import node  # pylint: disable=import-error
+_NODE_PATH = node.GetBinaryPath()
+
+
+def main():
+    parser = optparse.OptionParser(description=__doc__)
+    parser.add_option(
+        '--root_gen_dir',
+        action='store',
+        metavar='ROOT_GEN_DIR',
+        help='The root directory where the header and cc will be generated.')
+    parser.add_option('--output_header', action='store', metavar='OUTPUT_HEADER', help='Generated output .h file for pairs of IDs')
+    parser.add_option(
+        '--output_cc',
+        action='store',
+        metavar='OUTPUT_CC',
+        help='Generated output .cc file that contains pairs of {front-end string key, IDS_ key}')
+    options, _ = parser.parse_args()
+
+    if not options.root_gen_dir:
+        parser.error('--root_gen_dir was not specified.')
+    if not options.output_header:
+        parser.error('--output_header was not specified.')
+    if not options.output_header:
+        parser.error('--output_cc was not specified.')
+
+    cmd_parts = [
+        _NODE_PATH, _JS_SCRIPT_PATH, '--root_gen_dir',
+        os.path.abspath(options.root_gen_dir), '--output_header', options.output_header, '--output_cc', options.output_cc
+    ]
+    process = subprocess.Popen(cmd_parts, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
+    out, _ = process.communicate()
+    if process.returncode != 0:
+        return out
+
+
+if __name__ == '__main__':
+    sys.exit(main())
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 6cc5f7fc3..721f1201 100644
--- a/third_party/blink/renderer/modules/accessibility/ax_node_object.cc
+++ b/third_party/blink/renderer/modules/accessibility/ax_node_object.cc
@@ -637,7 +637,7 @@
     return ax::mojom::Role::kMath;
 
   if (GetNode()->HasTagName(kRpTag) || GetNode()->HasTagName(kRtTag))
-    return ax::mojom::Role::kAnnotation;
+    return ax::mojom::Role::kRubyAnnotation;
 
   if (IsHTMLFormElement(*GetNode()))
     return ax::mojom::Role::kForm;
@@ -1369,8 +1369,7 @@
   return level;
 }
 
-// TODO: rename this just AutoComplete, it's not only ARIA.
-String AXNodeObject::AriaAutoComplete() const {
+String AXNodeObject::AutoComplete() const {
   if (IsNativeTextControl() || IsARIATextControl()) {
     const AtomicString& aria_auto_complete =
         GetAOMPropertyOrARIAAttribute(AOMStringProperty::kAutocomplete)
diff --git a/third_party/blink/renderer/modules/accessibility/ax_node_object.h b/third_party/blink/renderer/modules/accessibility/ax_node_object.h
index 814ede0..2140d86d 100644
--- a/third_party/blink/renderer/modules/accessibility/ax_node_object.h
+++ b/third_party/blink/renderer/modules/accessibility/ax_node_object.h
@@ -64,7 +64,7 @@
   void AlterSliderOrSpinButtonValue(bool increase);
   AXObject* ActiveDescendant() override;
   String AriaAccessibilityDescription() const;
-  String AriaAutoComplete() const override;
+  String AutoComplete() const override;
   void AccessibilityChildrenFromAOMProperty(AOMRelationListProperty,
                                             AXObject::AXObjectVector&) const;
 
diff --git a/third_party/blink/renderer/modules/accessibility/ax_object.cc b/third_party/blink/renderer/modules/accessibility/ax_object.cc
index f9e7f73..549315c 100644
--- a/third_party/blink/renderer/modules/accessibility/ax_object.cc
+++ b/third_party/blink/renderer/modules/accessibility/ax_object.cc
@@ -92,6 +92,11 @@
 const RoleEntry kRoles[] = {
     {"alert", ax::mojom::Role::kAlert},
     {"alertdialog", ax::mojom::Role::kAlertDialog},
+    {"annotation-attribution", ax::mojom::Role::kAnnotationAttribution},
+    {"annotation-commentary", ax::mojom::Role::kAnnotationCommentary},
+    {"annotation-presence", ax::mojom::Role::kAnnotationPresence},
+    {"annotation-revision", ax::mojom::Role::kAnnotationRevision},
+    {"annotation-suggestion", ax::mojom::Role::kAnnotationSuggestion},
     {"application", ax::mojom::Role::kApplication},
     {"article", ax::mojom::Role::kArticle},
     {"banner", ax::mojom::Role::kBanner},
@@ -231,7 +236,11 @@
     {ax::mojom::Role::kAlertDialog, "AlertDialog"},
     {ax::mojom::Role::kAlert, "Alert"},
     {ax::mojom::Role::kAnchor, "Anchor"},
-    {ax::mojom::Role::kAnnotation, "Annotation"},
+    {ax::mojom::Role::kAnnotationAttribution, "kAnnotationAttribution"},
+    {ax::mojom::Role::kAnnotationCommentary, "AnnotationCommentary"},
+    {ax::mojom::Role::kAnnotationPresence, "AnnotationPresence"},
+    {ax::mojom::Role::kAnnotationRevision, "AnnotationRevision"},
+    {ax::mojom::Role::kAnnotationSuggestion, "AnnotationSuggestion"},
     {ax::mojom::Role::kApplication, "Application"},
     {ax::mojom::Role::kArticle, "Article"},
     {ax::mojom::Role::kAudio, "Audio"},
@@ -381,6 +390,7 @@
     {ax::mojom::Role::kRowHeader, "RowHeader"},
     {ax::mojom::Role::kRow, "Row"},
     {ax::mojom::Role::kRuby, "Ruby"},
+    {ax::mojom::Role::kRubyAnnotation, "RubyAnnotation"},
     {ax::mojom::Role::kSvgRoot, "SVGRoot"},
     {ax::mojom::Role::kScrollBar, "ScrollBar"},
     {ax::mojom::Role::kScrollView, "ScrollView"},
@@ -3378,6 +3388,11 @@
     // containers for many subobjects. Superset of nameFrom:author ARIA roles.
     case ax::mojom::Role::kAlert:
     case ax::mojom::Role::kAlertDialog:
+    case ax::mojom::Role::kAnnotationAttribution:
+    case ax::mojom::Role::kAnnotationCommentary:
+    case ax::mojom::Role::kAnnotationPresence:
+    case ax::mojom::Role::kAnnotationRevision:
+    case ax::mojom::Role::kAnnotationSuggestion:
     case ax::mojom::Role::kApplication:
     case ax::mojom::Role::kAudio:
     case ax::mojom::Role::kArticle:
@@ -3496,7 +3511,6 @@
     // Some objects can contribute their contents to ancestor names, but
     // only have their own name if they are focusable
     case ax::mojom::Role::kAbbr:
-    case ax::mojom::Role::kAnnotation:
     case ax::mojom::Role::kCanvas:
     case ax::mojom::Role::kCaption:
     case ax::mojom::Role::kContentDeletion:
@@ -3532,6 +3546,7 @@
     // if the row might receive focus
     case ax::mojom::Role::kRow:
     case ax::mojom::Role::kRuby:
+    case ax::mojom::Role::kRubyAnnotation:
       result = recursive || (CanReceiveAccessibilityFocus() && !IsEditable());
       break;
 
diff --git a/third_party/blink/renderer/modules/accessibility/ax_object.h b/third_party/blink/renderer/modules/accessibility/ax_object.h
index 9786766..37759734 100644
--- a/third_party/blink/renderer/modules/accessibility/ax_object.h
+++ b/third_party/blink/renderer/modules/accessibility/ax_object.h
@@ -711,7 +711,7 @@
   ax::mojom::Role DetermineAriaRoleAttribute() const;
   virtual ax::mojom::Role AriaRoleAttribute() const;
   virtual AXObject* ActiveDescendant() { return nullptr; }
-  virtual String AriaAutoComplete() const { return String(); }
+  virtual String AutoComplete() const { return String(); }
   virtual void AriaOwnsElements(AXObjectVector& owns) const {}
   virtual void AriaDescribedbyElements(AXObjectVector&) const {}
   virtual AXObject* ErrorMessage() const { return nullptr; }
diff --git a/third_party/blink/renderer/modules/accessibility/inspector_accessibility_agent.cc b/third_party/blink/renderer/modules/accessibility/inspector_accessibility_agent.cc
index b825bd2..3b0aa34 100644
--- a/third_party/blink/renderer/modules/accessibility/inspector_accessibility_agent.cc
+++ b/third_party/blink/renderer/modules/accessibility/inspector_accessibility_agent.cc
@@ -212,7 +212,7 @@
 void FillWidgetProperties(AXObject& ax_object,
                           protocol::Array<AXProperty>& properties) {
   ax::mojom::Role role = ax_object.RoleValue();
-  String autocomplete = ax_object.AriaAutoComplete();
+  String autocomplete = ax_object.AutoComplete();
   if (!autocomplete.IsEmpty())
     properties.emplace_back(
         CreateProperty(AXPropertyNameEnum::Autocomplete,
diff --git a/third_party/blink/renderer/modules/canvas/canvas2d/base_rendering_context_2d.cc b/third_party/blink/renderer/modules/canvas/canvas2d/base_rendering_context_2d.cc
index 9d49f83..5110563a 100644
--- a/third_party/blink/renderer/modules/canvas/canvas2d/base_rendering_context_2d.cc
+++ b/third_party/blink/renderer/modules/canvas/canvas2d/base_rendering_context_2d.cc
@@ -1597,6 +1597,9 @@
   WTF::ArrayBufferContents contents;
 
   const CanvasColorParams& color_params = ColorParams();
+  // Deferred offscreen canvases might have recorded commands, make sure
+  // that those get drawn here
+  FinalizeFrame();
   scoped_refptr<StaticBitmapImage> snapshot = GetImage(kPreferNoAcceleration);
 
   if (!StaticBitmapImage::ConvertToArrayBufferContents(
@@ -1605,8 +1608,6 @@
     return nullptr;
   }
 
-  NeedsFinalizeFrame();
-
   // Convert pixels to proper storage format if needed
   if (PixelFormat() != kRGBA8CanvasPixelFormat) {
     ImageDataStorageFormat storage_format =
diff --git a/third_party/blink/renderer/modules/canvas/canvas2d/base_rendering_context_2d.h b/third_party/blink/renderer/modules/canvas/canvas2d/base_rendering_context_2d.h
index 112f4aa..64bc549 100644
--- a/third_party/blink/renderer/modules/canvas/canvas2d/base_rendering_context_2d.h
+++ b/third_party/blink/renderer/modules/canvas/canvas2d/base_rendering_context_2d.h
@@ -193,6 +193,8 @@
                              ImageDataColorSettings*,
                              ExceptionState&) const;
 
+  // For deferred canvases this will have the side effect of drawing recorded
+  // commands in order to finalize the frame
   ImageData* getImageData(int sx, int sy, int sw, int sh, ExceptionState&);
   void putImageData(ImageData*, int dx, int dy, ExceptionState&);
   void putImageData(ImageData*,
@@ -361,6 +363,7 @@
 
   mutable UsageCounters usage_counters_;
 
+  virtual void FinalizeFrame() {}
   virtual void NeedsFinalizeFrame() {}
 
   float GetFontBaseline(const SimpleFontData&) const;
diff --git a/third_party/blink/renderer/modules/canvas/canvas2d/canvas_rendering_context_2d.cc b/third_party/blink/renderer/modules/canvas/canvas2d/canvas_rendering_context_2d.cc
index 5df98e9f..17cf448 100644
--- a/third_party/blink/renderer/modules/canvas/canvas2d/canvas_rendering_context_2d.cc
+++ b/third_party/blink/renderer/modules/canvas/canvas2d/canvas_rendering_context_2d.cc
@@ -671,6 +671,12 @@
   return canvas()->GetCanvas2DLayerBridge()->NewImageSnapshot(hint);
 }
 
+void CanvasRenderingContext2D::FinalizeFrame() {
+  if (canvas() && canvas()->GetCanvas2DLayerBridge())
+    canvas()->GetCanvas2DLayerBridge()->FinalizeFrame();
+  usage_counters_.num_frames_since_reset++;
+}
+
 bool CanvasRenderingContext2D::ParseColorOrCurrentColor(
     Color& color,
     const String& color_string) const {
diff --git a/third_party/blink/renderer/modules/canvas/canvas2d/canvas_rendering_context_2d.h b/third_party/blink/renderer/modules/canvas/canvas2d/canvas_rendering_context_2d.h
index 4f26dd2..13d07b2 100644
--- a/third_party/blink/renderer/modules/canvas/canvas2d/canvas_rendering_context_2d.h
+++ b/third_party/blink/renderer/modules/canvas/canvas2d/canvas_rendering_context_2d.h
@@ -190,7 +190,7 @@
 
   void ValidateStateStack() const final;
 
-  void FinalizeFrame() override { usage_counters_.num_frames_since_reset++; }
+  void FinalizeFrame() override;
 
   bool IsPaintable() const final { return canvas()->GetCanvas2DLayerBridge(); }
 
@@ -204,7 +204,6 @@
   void NeedsFinalizeFrame() override {
     CanvasRenderingContext::NeedsFinalizeFrame();
   }
-
   CanvasColorParams ColorParams() const override;
   bool WritePixels(const SkImageInfo& orig_info,
                    const void* pixels,
diff --git a/third_party/blink/renderer/modules/canvas/offscreencanvas2d/offscreen_canvas_rendering_context_2d.h b/third_party/blink/renderer/modules/canvas/offscreencanvas2d/offscreen_canvas_rendering_context_2d.h
index 89270c56..248c961 100644
--- a/third_party/blink/renderer/modules/canvas/offscreencanvas2d/offscreen_canvas_rendering_context_2d.h
+++ b/third_party/blink/renderer/modules/canvas/offscreencanvas2d/offscreen_canvas_rendering_context_2d.h
@@ -124,7 +124,6 @@
   void NeedsFinalizeFrame() override {
     CanvasRenderingContext::NeedsFinalizeFrame();
   }
-
   CanvasColorParams ColorParams() const override;
   bool WritePixels(const SkImageInfo& orig_info,
                    const void* pixels,
diff --git a/third_party/blink/renderer/modules/exported/web_ax_object.cc b/third_party/blink/renderer/modules/exported/web_ax_object.cc
index 4fb9d34..5ac9cd7 100644
--- a/third_party/blink/renderer/modules/exported/web_ax_object.cc
+++ b/third_party/blink/renderer/modules/exported/web_ax_object.cc
@@ -281,11 +281,11 @@
   return private_->IsAutofillAvailable();
 }
 
-WebString WebAXObject::AriaAutoComplete() const {
+WebString WebAXObject::AutoComplete() const {
   if (IsDetached())
     return WebString();
 
-  return private_->AriaAutoComplete();
+  return private_->AutoComplete();
 }
 
 ax::mojom::AriaCurrentState WebAXObject::AriaCurrentState() const {
diff --git a/third_party/blink/renderer/modules/indexeddb/idb_database.cc b/third_party/blink/renderer/modules/indexeddb/idb_database.cc
index bcb343ac..6551d98 100644
--- a/third_party/blink/renderer/modules/indexeddb/idb_database.cc
+++ b/third_party/blink/renderer/modules/indexeddb/idb_database.cc
@@ -353,6 +353,14 @@
 IDBTransaction* IDBDatabase::transaction(
     ScriptState* script_state,
     const StringOrStringSequence& store_names,
+    const String& mode,
+    ExceptionState& exception_state) {
+  return transaction(script_state, store_names, mode, nullptr, exception_state);
+}
+
+IDBTransaction* IDBDatabase::transaction(
+    ScriptState* script_state,
+    const StringOrStringSequence& store_names,
     const String& mode_string,
     const IDBTransactionOptions* options,
     ExceptionState& exception_state) {
@@ -423,7 +431,8 @@
 
   mojom::IDBTransactionDurability durability =
       mojom::IDBTransactionDurability::Default;
-  if (RuntimeEnabledFeatures::IDBRelaxedDurabilityEnabled() && options) {
+  if (options) {
+    DCHECK(RuntimeEnabledFeatures::IDBRelaxedDurabilityEnabled());
     if (options->durability() == "relaxed") {
       durability = mojom::IDBTransactionDurability::Relaxed;
     } else if (options->durability() == "strict") {
diff --git a/third_party/blink/renderer/modules/indexeddb/idb_database.h b/third_party/blink/renderer/modules/indexeddb/idb_database.h
index 38cf13d..7445262 100644
--- a/third_party/blink/renderer/modules/indexeddb/idb_database.h
+++ b/third_party/blink/renderer/modules/indexeddb/idb_database.h
@@ -105,6 +105,10 @@
   IDBTransaction* transaction(ScriptState*,
                               const StringOrStringSequence& store_names,
                               const String& mode,
+                              ExceptionState&);
+  IDBTransaction* transaction(ScriptState*,
+                              const StringOrStringSequence& store_names,
+                              const String& mode,
                               const IDBTransactionOptions* options,
                               ExceptionState&);
   void deleteObjectStore(const String& name, ExceptionState&);
diff --git a/third_party/blink/renderer/modules/indexeddb/idb_database.idl b/third_party/blink/renderer/modules/indexeddb/idb_database.idl
index 5942f0a..938b6b4 100644
--- a/third_party/blink/renderer/modules/indexeddb/idb_database.idl
+++ b/third_party/blink/renderer/modules/indexeddb/idb_database.idl
@@ -35,8 +35,12 @@
     readonly attribute DOMStringList objectStoreNames;
 
     [NewObject, CallWith=ScriptState, RaisesException] IDBTransaction transaction((DOMString or sequence<DOMString>) storeNames,
+                                                                                  optional IDBTransactionMode mode = "readonly");
+
+    [NewObject, CallWith=ScriptState, RaisesException, RuntimeEnabled=IDBRelaxedDurability] IDBTransaction transaction((DOMString or sequence<DOMString>) storeNames,
+
                                                                                   optional IDBTransactionMode mode = "readonly",
-                                                                                  optional IDBTransactionOptions options);
+                                                                                  IDBTransactionOptions options);
     void close();
 
     [MeasureAs=IndexedDBWrite, NewObject, RaisesException]
diff --git a/third_party/blink/renderer/modules/mediasource/media_source.cc b/third_party/blink/renderer/modules/mediasource/media_source.cc
index 045b6a0..9e24dab 100644
--- a/third_party/blink/renderer/modules/mediasource/media_source.cc
+++ b/third_party/blink/renderer/modules/mediasource/media_source.cc
@@ -533,10 +533,6 @@
   return ranges;
 }
 
-TimeRanges* MediaSource::Seekable() const {
-  return MakeGarbageCollected<TimeRanges>(SeekableInternal());
-}
-
 void MediaSource::OnTrackChanged(TrackBase* track) {
   DCHECK(HTMLMediaElement::MediaTracksEnabledInternally());
   SourceBuffer* source_buffer =
diff --git a/third_party/blink/renderer/modules/mediasource/media_source.h b/third_party/blink/renderer/modules/mediasource/media_source.h
index 09e5867..de8973e4 100644
--- a/third_party/blink/renderer/modules/mediasource/media_source.h
+++ b/third_party/blink/renderer/modules/mediasource/media_source.h
@@ -102,7 +102,6 @@
   WebTimeRanges BufferedInternal() const override;
   WebTimeRanges SeekableInternal() const override;
   TimeRanges* Buffered() const override;
-  TimeRanges* Seekable() const override;
   void OnTrackChanged(TrackBase*) override;
 
   // EventTarget interface
diff --git a/third_party/blink/renderer/platform/BUILD.gn b/third_party/blink/renderer/platform/BUILD.gn
index decd2fd..70cfa715 100644
--- a/third_party/blink/renderer/platform/BUILD.gn
+++ b/third_party/blink/renderer/platform/BUILD.gn
@@ -1698,6 +1698,7 @@
     "exported/mediastream/media_stream_audio_processor_options_test.cc",
     "exported/mediastream/media_stream_audio_test.cc",
     "exported/mediastream/webrtc_uma_histograms_test.cc",
+    "exported/page_zoom_test.cc",
     "exported/video_capture/web_video_capture_impl_manager_test.cc",
     "exported/web_canonical_cookie_test.cc",
     "exported/web_icon_sizes_parser_test.cc",
diff --git a/content/common/page_zoom_unittest.cc b/third_party/blink/renderer/platform/exported/page_zoom_test.cc
similarity index 65%
rename from content/common/page_zoom_unittest.cc
rename to third_party/blink/renderer/platform/exported/page_zoom_test.cc
index 67dd5d56..f65c533 100644
--- a/content/common/page_zoom_unittest.cc
+++ b/third_party/blink/renderer/platform/exported/page_zoom_test.cc
@@ -2,18 +2,17 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "content/public/common/page_zoom.h"
+#include "third_party/blink/public/common/page/page_zoom.h"
 
 #include "testing/gtest/include/gtest/gtest.h"
 
 TEST(PageZoomTest, ZoomValuesEqual) {
   // Test two identical values.
-  EXPECT_TRUE(content::ZoomValuesEqual(1.5, 1.5));
+  EXPECT_TRUE(blink::PageZoomValuesEqual(1.5, 1.5));
 
   // Test two values that are close enough to be considered equal.
-  EXPECT_TRUE(content::ZoomValuesEqual(1.5, 1.49999999));
+  EXPECT_TRUE(blink::PageZoomValuesEqual(1.5, 1.49999999));
 
   // Test two values that are close, but should not be considered equal.
-  EXPECT_FALSE(content::ZoomValuesEqual(1.5, 1.4));
+  EXPECT_FALSE(blink::PageZoomValuesEqual(1.5, 1.4));
 }
-
diff --git a/third_party/blink/renderer/platform/graphics/canvas_2d_layer_bridge.cc b/third_party/blink/renderer/platform/graphics/canvas_2d_layer_bridge.cc
index 85292fa3c..0212943 100644
--- a/third_party/blink/renderer/platform/graphics/canvas_2d_layer_bridge.cc
+++ b/third_party/blink/renderer/platform/graphics/canvas_2d_layer_bridge.cc
@@ -124,8 +124,6 @@
 
   if (resource_host_)
     resource_host_->RestoreCanvasMatrixClipStack(canvas);
-
-  recording_pixel_count_ = 0;
 }
 
 void Canvas2DLayerBridge::ResetResourceProvider() {
@@ -735,6 +733,8 @@
 
   ++frames_since_last_commit_;
   if (frames_since_last_commit_ >= 2) {
+    // TODO(aaronhk) Ideally we'd want to call FlushRecording() here, but this
+    // causes webview to hang for pixel 2. See crbug.com/1002946
     ResourceProvider()->FlushSkia();
     if (IsAccelerated() && !rate_limiter_) {
       // Make sure the GPU is never more than two animation frames behind.
diff --git a/third_party/blink/renderer/platform/graphics/canvas_2d_layer_bridge.h b/third_party/blink/renderer/platform/graphics/canvas_2d_layer_bridge.h
index 3bc812c..21a4d5fed 100644
--- a/third_party/blink/renderer/platform/graphics/canvas_2d_layer_bridge.h
+++ b/third_party/blink/renderer/platform/graphics/canvas_2d_layer_bridge.h
@@ -170,7 +170,6 @@
   CanvasResourceProvider* ResourceProvider() const;
   void FlushRecording();
 
-  PaintRecorder* getRecorder() { return recorder_.get(); }
   sk_sp<cc::PaintRecord> getLastRecord() { return last_recording_; }
 
  private:
@@ -195,7 +194,6 @@
   int frames_since_last_commit_ = 0;
   bool have_recorded_draw_commands_;
   bool is_hidden_;
-  // See the implementation of DisableDeferral() for more information.
   bool is_deferral_enabled_;
   bool software_rendering_while_hidden_;
   bool hibernation_scheduled_ = false;
@@ -205,7 +203,6 @@
   const AccelerationMode acceleration_mode_;
   const CanvasColorParams color_params_;
   const IntSize size_;
-  base::CheckedNumeric<int> recording_pixel_count_;
 
   enum SnapshotState {
     kInitialSnapshotState,
diff --git a/third_party/blink/renderer/platform/graphics/canvas_resource.cc b/third_party/blink/renderer/platform/graphics/canvas_resource.cc
index 4b4c5cf..f15bd95 100644
--- a/third_party/blink/renderer/platform/graphics/canvas_resource.cc
+++ b/third_party/blink/renderer/platform/graphics/canvas_resource.cc
@@ -181,397 +181,6 @@
   return filter_quality_ == kNone_SkFilterQuality ? GL_NEAREST : GL_LINEAR;
 }
 
-// CanvasResourceBitmap
-//==============================================================================
-
-CanvasResourceBitmap::CanvasResourceBitmap(
-    scoped_refptr<StaticBitmapImage> image,
-    base::WeakPtr<CanvasResourceProvider> provider,
-    SkFilterQuality filter_quality,
-    const CanvasColorParams& color_params)
-    : CanvasResource(std::move(provider), filter_quality, color_params),
-      image_(std::move(image)) {}
-
-CanvasResourceBitmap::~CanvasResourceBitmap() {
-  OnDestroy();
-}
-
-scoped_refptr<CanvasResourceBitmap> CanvasResourceBitmap::Create(
-    scoped_refptr<StaticBitmapImage> image,
-    base::WeakPtr<CanvasResourceProvider> provider,
-    SkFilterQuality filter_quality,
-    const CanvasColorParams& color_params) {
-  auto resource = AdoptRef(new CanvasResourceBitmap(
-      std::move(image), std::move(provider), filter_quality, color_params));
-  return resource->IsValid() ? resource : nullptr;
-}
-
-bool CanvasResourceBitmap::IsValid() const {
-  return image_ ? image_->IsValid() : false;
-}
-
-bool CanvasResourceBitmap::IsAccelerated() const {
-  return image_->IsTextureBacked();
-}
-
-scoped_refptr<CanvasResource> CanvasResourceBitmap::MakeAccelerated(
-    base::WeakPtr<WebGraphicsContext3DProviderWrapper>
-        context_provider_wrapper) {
-  if (IsAccelerated())
-    return base::WrapRefCounted(this);
-
-  TRACE_EVENT0("blink", "CanvasResourceBitmap::MakeAccelerated");
-
-  if (!context_provider_wrapper)
-    return nullptr;
-  scoped_refptr<StaticBitmapImage> accelerated_image =
-      image_->MakeAccelerated(context_provider_wrapper);
-  // passing nullptr for the resource provider argument creates an orphan
-  // CanvasResource, which implies that it internal resources will not be
-  // recycled.
-  scoped_refptr<CanvasResource> accelerated_resource =
-      Create(accelerated_image, nullptr, FilterQuality(), ColorParams());
-  if (!accelerated_resource)
-    return nullptr;
-  return accelerated_resource;
-}
-
-scoped_refptr<CanvasResource> CanvasResourceBitmap::MakeUnaccelerated() {
-  if (!IsAccelerated())
-    return base::WrapRefCounted(this);
-
-  TRACE_EVENT0("blink", "CanvasResourceBitmap::MakeUnaccelerated");
-
-  scoped_refptr<StaticBitmapImage> unaccelerated_image =
-      image_->MakeUnaccelerated();
-  // passing nullptr for the resource provider argument creates an orphan
-  // CanvasResource, which implies that it internal resources will not be
-  // recycled.
-  scoped_refptr<CanvasResource> unaccelerated_resource =
-      Create(unaccelerated_image, nullptr, FilterQuality(), ColorParams());
-  return unaccelerated_resource;
-}
-
-void CanvasResourceBitmap::TearDown() {
-  image_ = nullptr;
-}
-
-IntSize CanvasResourceBitmap::Size() const {
-  if (!image_)
-    return IntSize(0, 0);
-  return IntSize(image_->width(), image_->height());
-}
-
-GLenum CanvasResourceBitmap::TextureTarget() const {
-  return GL_TEXTURE_2D;
-}
-
-scoped_refptr<StaticBitmapImage> CanvasResourceBitmap::Bitmap() {
-  return image_;
-}
-
-const gpu::Mailbox& CanvasResourceBitmap::GetOrCreateGpuMailbox(
-    MailboxSyncMode sync_mode) {
-  DCHECK(image_);  // Calling code should check IsValid() before calling this.
-  image_->EnsureMailbox(sync_mode, GLFilter());
-  return image_->GetMailbox();
-}
-
-bool CanvasResourceBitmap::HasGpuMailbox() const {
-  return image_ && image_->HasMailbox();
-}
-
-const gpu::SyncToken CanvasResourceBitmap::GetSyncToken() {
-  DCHECK(image_);  // Calling code should check IsValid() before calling this.
-  return image_->GetSyncToken();
-}
-
-void CanvasResourceBitmap::Transfer() {
-  DCHECK(image_);  // Calling code should check IsValid() before calling this.
-  return image_->Transfer();
-}
-
-bool CanvasResourceBitmap::OriginClean() const {
-  DCHECK(image_);
-  return image_->OriginClean();
-}
-
-void CanvasResourceBitmap::SetOriginClean(bool value) {
-  DCHECK(image_);
-  image_->SetOriginClean(value);
-}
-
-base::WeakPtr<WebGraphicsContext3DProviderWrapper>
-CanvasResourceBitmap::ContextProviderWrapper() const {
-  if (!image_)
-    return nullptr;
-  return image_->ContextProviderWrapper();
-}
-
-void CanvasResourceBitmap::TakeSkImage(sk_sp<SkImage> image) {
-  DCHECK(IsAccelerated() == image->isTextureBacked());
-  image_ =
-      StaticBitmapImage::Create(std::move(image), ContextProviderWrapper());
-}
-
-// CanvasResourceGpuMemoryBuffer
-//==============================================================================
-
-CanvasResourceGpuMemoryBuffer::CanvasResourceGpuMemoryBuffer(
-    const IntSize& size,
-    const CanvasColorParams& color_params,
-    base::WeakPtr<WebGraphicsContext3DProviderWrapper> context_provider_wrapper,
-    base::WeakPtr<CanvasResourceProvider> provider,
-    SkFilterQuality filter_quality,
-    bool is_accelerated)
-    : CanvasResource(std::move(provider), filter_quality, color_params),
-      context_provider_wrapper_(std::move(context_provider_wrapper)),
-      is_accelerated_(is_accelerated) {
-  if (!context_provider_wrapper_)
-    return;
-  auto* gl = context_provider_wrapper_->ContextProvider()->ContextGL();
-  auto* gr = context_provider_wrapper_->ContextProvider()->GetGrContext();
-  if (!gl || !gr)
-    return;
-
-  const gfx::BufferUsage buffer_usage =
-      is_accelerated ? gfx::BufferUsage::SCANOUT
-                     : gfx::BufferUsage::SCANOUT_CPU_READ_WRITE;
-
-  gpu::GpuMemoryBufferManager* gpu_memory_buffer_manager =
-      Platform::Current()->GetGpuMemoryBufferManager();
-  if (!gpu_memory_buffer_manager)
-    return;
-  gpu_memory_buffer_ = gpu_memory_buffer_manager->CreateGpuMemoryBuffer(
-      gfx::Size(size.Width(), size.Height()), ColorParams().GetBufferFormat(),
-      buffer_usage, gpu::kNullSurfaceHandle);
-  if (!gpu_memory_buffer_)
-    return;
-
-  gpu_memory_buffer_->SetColorSpace(color_params.GetStorageGfxColorSpace());
-
-  const GLuint image_id = gl->CreateImageCHROMIUM(
-      gpu_memory_buffer_->AsClientBuffer(), size.Width(), size.Height(),
-      ColorParams().GLUnsizedInternalFormat());
-  if (!image_id) {
-    gpu_memory_buffer_ = nullptr;
-    return;
-  }
-  gl->GenTextures(1, &texture_id_);
-  const GLenum target = TextureTarget();
-  gl->BindTexture(target, texture_id_);
-  gl->TexParameteri(target, GL_TEXTURE_MIN_FILTER, GLFilter());
-  gl->TexParameteri(target, GL_TEXTURE_MAG_FILTER, GLFilter());
-  gl->BindTexImage2DCHROMIUM(target, image_id);
-  gl->DestroyImageCHROMIUM(image_id);
-
-  if (is_accelerated_ && target == GL_TEXTURE_EXTERNAL_OES) {
-    // We can't CopyTextureCHROMIUM() into a GL_TEXTURE_EXTERNAL_OES; create
-    // another image and bind a GL_TEXTURE_2D texture to it.
-    const GLuint image_2d_id_for_copy = gl->CreateImageCHROMIUM(
-        gpu_memory_buffer_->AsClientBuffer(), size.Width(), size.Height(),
-        ColorParams().GLUnsizedInternalFormat());
-
-    gl->GenTextures(1, &texture_2d_id_for_copy_);
-    gl->BindTexture(GL_TEXTURE_2D, texture_2d_id_for_copy_);
-    gl->BindTexImage2DCHROMIUM(GL_TEXTURE_2D, image_2d_id_for_copy);
-
-    gl->DestroyImageCHROMIUM(image_2d_id_for_copy);
-  }
-}
-
-CanvasResourceGpuMemoryBuffer::~CanvasResourceGpuMemoryBuffer() {
-  OnDestroy();
-}
-
-bool CanvasResourceGpuMemoryBuffer::IsValid() const {
-  return !!context_provider_wrapper_ && gpu_memory_buffer_;
-}
-
-GLenum CanvasResourceGpuMemoryBuffer::TextureTarget() const {
-  return gpu::GetPlatformSpecificTextureTarget();
-}
-
-IntSize CanvasResourceGpuMemoryBuffer::Size() const {
-  return IntSize(gpu_memory_buffer_->GetSize());
-}
-
-scoped_refptr<CanvasResourceGpuMemoryBuffer>
-CanvasResourceGpuMemoryBuffer::Create(
-    const IntSize& size,
-    const CanvasColorParams& color_params,
-    base::WeakPtr<WebGraphicsContext3DProviderWrapper> context_provider_wrapper,
-    base::WeakPtr<CanvasResourceProvider> provider,
-    SkFilterQuality filter_quality,
-    bool is_accelerated) {
-  TRACE_EVENT0("blink", "CanvasResourceGpuMemoryBuffer::Create");
-  auto resource = AdoptRef(new CanvasResourceGpuMemoryBuffer(
-      size, color_params, std::move(context_provider_wrapper),
-      std::move(provider), filter_quality, is_accelerated));
-  return resource->IsValid() ? resource : nullptr;
-}
-
-void CanvasResourceGpuMemoryBuffer::TearDown() {
-  // Unref should result in destruction.
-  DCHECK(!surface_ || surface_->unique());
-
-  surface_ = nullptr;
-  if (context_provider_wrapper_) {
-    auto* gl = ContextGL();
-    if (gl && texture_id_)
-      gl->DeleteTextures(1, &texture_id_);
-    if (gl && texture_2d_id_for_copy_)
-      gl->DeleteTextures(1, &texture_2d_id_for_copy_);
-  }
-  texture_id_ = 0;
-  texture_2d_id_for_copy_ = 0;
-  gpu_memory_buffer_ = nullptr;
-}
-
-void CanvasResourceGpuMemoryBuffer::Abandon() {
-  surface_ = nullptr;
-  texture_id_ = 0;
-  texture_2d_id_for_copy_ = 0;
-  gpu_memory_buffer_ = nullptr;
-}
-
-const gpu::Mailbox& CanvasResourceGpuMemoryBuffer::GetOrCreateGpuMailbox(
-    MailboxSyncMode sync_mode) {
-  auto* gl = ContextGL();
-  if (gpu_mailbox_.IsZero() && gl) {
-    gl->ProduceTextureDirectCHROMIUM(texture_id_, gpu_mailbox_.name);
-    mailbox_needs_new_sync_token_ = true;
-    mailbox_sync_mode_ = sync_mode;
-  }
-  return gpu_mailbox_;
-}
-
-bool CanvasResourceGpuMemoryBuffer::HasGpuMailbox() const {
-  return !gpu_mailbox_.IsZero();
-}
-
-GLuint CanvasResourceGpuMemoryBuffer::GetBackingTextureHandleForOverwrite() {
-  return texture_2d_id_for_copy_ ? texture_2d_id_for_copy_ : texture_id_;
-}
-
-const gpu::SyncToken CanvasResourceGpuMemoryBuffer::GetSyncToken() {
-  if (!mailbox_needs_new_sync_token_)
-    return sync_token_;
-  auto* gl = ContextGL();
-  if (!gl)
-    return sync_token_;
-  mailbox_needs_new_sync_token_ = false;
-  if (mailbox_sync_mode_ == kVerifiedSyncToken)
-    gl->GenSyncTokenCHROMIUM(sync_token_.GetData());
-  else
-    gl->GenUnverifiedSyncTokenCHROMIUM(sync_token_.GetData());
-
-  return sync_token_;
-}
-
-void CanvasResourceGpuMemoryBuffer::CopyFromTexture(GLuint source_texture,
-                                                    GLenum format,
-                                                    GLenum type) {
-  DCHECK(is_accelerated_);
-  if (!IsValid())
-    return;
-
-  TRACE_EVENT0("blink", "CanvasResourceGpuMemoryBuffer::CopyFromTexture");
-  GLenum target = TextureTarget();
-  GLuint texture_id = texture_id_;
-
-  if (texture_2d_id_for_copy_) {
-    // We can't CopyTextureCHROMIUM() into a GL_TEXTURE_EXTERNAL_OES; use
-    // instead GL_TEXTURE_2D for the CopyTextureChromium().
-    target = GL_TEXTURE_2D;
-    texture_id = texture_2d_id_for_copy_;
-  }
-
-  ContextGL()->CopyTextureCHROMIUM(
-      source_texture, 0 /*sourceLevel*/, target, texture_id, 0 /*destLevel*/,
-      format, type, false /*unpackFlipY*/, false /*unpackPremultiplyAlpha*/,
-      false /*unpackUnmultiplyAlpha*/);
-
-  mailbox_needs_new_sync_token_ = true;
-}
-
-void CanvasResourceGpuMemoryBuffer::TakeSkImage(sk_sp<SkImage> image) {
-  WillPaint();
-  if (!surface_)
-    return;
-  surface_->getCanvas()->drawImage(image, 0, 0);
-  DidPaint();
-}
-
-void CanvasResourceGpuMemoryBuffer::WillPaint() {
-  if (!IsValid()) {
-    surface_ = nullptr;
-    return;
-  }
-
-  TRACE_EVENT1("blink", "CanvasResourceGpuMemoryBuffer::WillPaint",
-               "accelerated", is_accelerated_);
-
-  if (is_accelerated_) {
-    if (!surface_) {  // When accelerated it is okay to re-use previous
-                      // SkSurface
-      GrGLTextureInfo texture_info;
-      texture_info.fTarget = TextureTarget();
-      texture_info.fID = texture_id_;
-      texture_info.fFormat =
-          ColorParams().GLSizedInternalFormat();  // unsized format
-      GrBackendTexture backend_texture(Size().Width(), Size().Height(),
-                                       GrMipMapped::kNo, texture_info);
-      constexpr int sample_count = 0;
-      surface_ = SkSurface::MakeFromBackendTextureAsRenderTarget(
-          GetGrContext(), backend_texture, kTopLeft_GrSurfaceOrigin,
-          sample_count, ColorParams().GetSkColorType(),
-          ColorParams().GetSkColorSpace(), nullptr /*surface props*/);
-    }
-  } else {
-    gpu_memory_buffer_->Map();
-    void* buffer_base_address = gpu_memory_buffer_->memory(0);
-    if (!surface_ || buffer_base_address != buffer_base_address_) {
-      buffer_base_address_ = buffer_base_address;
-      SkImageInfo image_info = SkImageInfo::Make(
-          Size().Width(), Size().Height(), ColorParams().GetSkColorType(),
-          ColorParams().GetSkAlphaType(), ColorParams().GetSkColorSpace());
-      surface_ = SkSurface::MakeRasterDirect(image_info, buffer_base_address_,
-                                             gpu_memory_buffer_->stride(0));
-    }
-  }
-
-  DCHECK(surface_);
-}
-
-void CanvasResourceGpuMemoryBuffer::DidPaint() {
-  TRACE_EVENT1("blink", "CanvasResourceGpuMemoryBuffer::DidPaint",
-               "accelerated", is_accelerated_);
-
-  if (is_accelerated_) {
-    auto* gr = context_provider_wrapper_->ContextProvider()->GetGrContext();
-    if (gr)
-      gr->flush();
-    mailbox_needs_new_sync_token_ = true;
-  } else {
-    gpu_memory_buffer_->Unmap();
-  }
-}
-
-base::WeakPtr<WebGraphicsContext3DProviderWrapper>
-CanvasResourceGpuMemoryBuffer::ContextProviderWrapper() const {
-  return context_provider_wrapper_;
-}
-
-scoped_refptr<StaticBitmapImage> CanvasResourceGpuMemoryBuffer::Bitmap() {
-  WillPaint();
-  scoped_refptr<StaticBitmapImage> bitmap = StaticBitmapImage::Create(
-      surface_->makeImageSnapshot(), ContextProviderWrapper());
-  DidPaint();
-  bitmap->SetOriginClean(is_origin_clean_);
-  return bitmap;
-}
-
 // CanvasResourceSharedBitmap
 //==============================================================================
 
diff --git a/third_party/blink/renderer/platform/graphics/canvas_resource.h b/third_party/blink/renderer/platform/graphics/canvas_resource.h
index fdf292e..2442ab2 100644
--- a/third_party/blink/renderer/platform/graphics/canvas_resource.h
+++ b/third_party/blink/renderer/platform/graphics/canvas_resource.h
@@ -52,42 +52,87 @@
     : public WTF::ThreadSafeRefCounted<CanvasResource> {
  public:
   virtual ~CanvasResource();
+
+  // We perform a lazy copy on write if the canvas content needs to be updated
+  // while its current resource is in use. In order to avoid re-allocating
+  // resources, its preferable to reuse a resource if its no longer in use.
+  // This API indicates whether a resource can be recycled.
   virtual bool IsRecycleable() const = 0;
+
+  // Returns true if rendering to the resource is accelerated.
   virtual bool IsAccelerated() const = 0;
+
+  // Returns true if the resource can be used with accelerated compositing. This
+  // is different from IsAccelerated since a resource may be rendered to on the
+  // CPU but can be used with GPU compositing (using GMBs).
   virtual bool SupportsAcceleratedCompositing() const = 0;
+
+  // Returns true if the resource is still usable. It maybe not be valid in the
+  // case of a context loss or if we fail to initialize the memory backing for
+  // the resource.
   virtual bool IsValid() const = 0;
+
+  // When a resource is returned by the display compositor, a sync token is
+  // provided to indicate when the compositor's commands using the resource are
+  // executed on the GPU thread.
+  // However in some cases we need to ensure that the commands using the
+  // resource have finished executing on the GPU itself. This API indicates
+  // whether this is required. The primary use-case for this is GMBs rendered to
+  // on the CPU but composited on the GPU. Its important for the GPU reads to be
+  // finished before updating the resource on the CPU.
   virtual bool NeedsReadLockFences() const { return false; }
+
+  // The bounds for this resource.
   virtual IntSize Size() const = 0;
+
+  // The mailbox which can be used to reference this resource in GPU commands.
+  // The sync mode indicates how the sync token for the resource should be
+  // prepared.
   virtual const gpu::Mailbox& GetOrCreateGpuMailbox(MailboxSyncMode) = 0;
+
+  // A CanvasResource is not thread-safe and does not allow concurrent usage
+  // from multiple threads. But it maybe used from any thread. It remains bound
+  // to the current thread until Transfer is called. Note that while the
+  // resource maybe used for reads on any thread, it can be written to only on
+  // the thread where it was created.
   virtual void Transfer() {}
+
+  // Returns the sync token to indicate when all writes to the current resource
+  // are finished on the GPU thread.
   virtual const gpu::SyncToken GetSyncToken() {
     NOTREACHED();
     return gpu::SyncToken();
   }
+
+  // Provides a TransferableResource representation of this resource to share it
+  // with the compositor.
   bool PrepareTransferableResource(viz::TransferableResource*,
                                    std::unique_ptr<viz::SingleReleaseCallback>*,
                                    MailboxSyncMode);
+
+  // Issues a wait for this sync token on the context used by this resource for
+  // rendering.
   void WaitSyncToken(const gpu::SyncToken&);
-  virtual scoped_refptr<CanvasResource> MakeAccelerated(
-      base::WeakPtr<WebGraphicsContext3DProviderWrapper>) = 0;
-  virtual scoped_refptr<CanvasResource> MakeUnaccelerated() = 0;
+
   virtual bool OriginClean() const = 0;
   virtual void SetOriginClean(bool) = 0;
-  virtual scoped_refptr<StaticBitmapImage> Bitmap() = 0;
-  virtual void CopyFromTexture(GLuint source_texture,
-                               GLenum format,
-                               GLenum type) {
-    NOTREACHED();
-  }
 
-  // Only CanvasResourceProvider and derivatives should call this.
+  // Provides a StaticBitmapImage wrapping this resource. Commonly used for
+  // snapshots not used in compositing (for instance to draw to another canvas).
+  virtual scoped_refptr<StaticBitmapImage> Bitmap() = 0;
+
+  // Copies the contents of |image| to the resource's backing memory. Only
+  // CanvasResourceProvider and derivatives should call this.
   virtual void TakeSkImage(sk_sp<SkImage> image) = 0;
 
+  // Provides the texture ID that can be used to write to this resource.
+  // TODO(khushalsagar): Won't work with OOPR.
   virtual GLuint GetBackingTextureHandleForOverwrite() {
     NOTREACHED();
     return 0;
   }
 
+  // Returns the texture target for the ID provided above.
   virtual GLenum TextureTarget() const {
     NOTREACHED();
     return 0;
@@ -107,7 +152,9 @@
     Abandon();
   }
 
+  // The filter quality to use when the resource is drawn by the compositor.
   SkFilterQuality FilterQuality() const { return filter_quality_; }
+
   SkImageInfo CreateSkImageInfo() const;
 
  protected:
@@ -123,9 +170,19 @@
   // before this resource could be deleted.
   virtual void Abandon() { TearDown(); }
 
+  // Returns true if the resource is backed by memory such that it can be used
+  // for direct scanout by the display.
   virtual bool IsOverlayCandidate() const { return false; }
+
+  // Returns true if the resource is backed by memory that can be referenced
+  // using a mailbox.
   virtual bool HasGpuMailbox() const = 0;
+
+  // Destroys the backing memory and any other references to it kept alive by
+  // this object. This must be called from the same thread where the resource
+  // was created.
   virtual void TearDown() = 0;
+
   gpu::gles2::GLES2Interface* ContextGL() const;
   GLenum GLFilter() const;
   GrContext* GetGrContext() const;
@@ -157,125 +214,6 @@
 #endif
 };
 
-// Resource type for skia Bitmaps (RAM and texture backed)
-class PLATFORM_EXPORT CanvasResourceBitmap final : public CanvasResource {
- public:
-  static scoped_refptr<CanvasResourceBitmap> Create(
-      scoped_refptr<StaticBitmapImage>,
-      base::WeakPtr<CanvasResourceProvider>,
-      SkFilterQuality,
-      const CanvasColorParams&);
-  ~CanvasResourceBitmap() override;
-
-  // Not recyclable: Skia handles texture recycling internally and bitmaps are
-  // cheap to allocate.
-  bool IsRecycleable() const final { return false; }
-  bool IsAccelerated() const final;
-  bool SupportsAcceleratedCompositing() const override {
-    return IsAccelerated();
-  }
-  bool IsValid() const final;
-  IntSize Size() const final;
-  void Transfer() final;
-  scoped_refptr<StaticBitmapImage> Bitmap() final;
-  bool OriginClean() const final;
-  void SetOriginClean(bool value) final;
-  scoped_refptr<CanvasResource> MakeAccelerated(
-      base::WeakPtr<WebGraphicsContext3DProviderWrapper>) final;
-  scoped_refptr<CanvasResource> MakeUnaccelerated() final;
-  void TakeSkImage(sk_sp<SkImage> image) final;
-
- private:
-  void TearDown() override;
-  GLenum TextureTarget() const final;
-  base::WeakPtr<WebGraphicsContext3DProviderWrapper> ContextProviderWrapper()
-      const override;
-  const gpu::Mailbox& GetOrCreateGpuMailbox(MailboxSyncMode) override;
-  bool HasGpuMailbox() const override;
-  const gpu::SyncToken GetSyncToken() override;
-
-  CanvasResourceBitmap(scoped_refptr<StaticBitmapImage>,
-                       base::WeakPtr<CanvasResourceProvider>,
-                       SkFilterQuality,
-                       const CanvasColorParams&);
-
-  scoped_refptr<StaticBitmapImage> image_;
-};
-
-// Resource type for GpuMemoryBuffers
-class PLATFORM_EXPORT CanvasResourceGpuMemoryBuffer final
-    : public CanvasResource {
- public:
-  static scoped_refptr<CanvasResourceGpuMemoryBuffer> Create(
-      const IntSize&,
-      const CanvasColorParams&,
-      base::WeakPtr<WebGraphicsContext3DProviderWrapper>,
-      base::WeakPtr<CanvasResourceProvider>,
-      SkFilterQuality,
-      bool is_accelerated);
-  ~CanvasResourceGpuMemoryBuffer() override;
-  bool IsRecycleable() const final { return IsValid(); }
-  bool IsAccelerated() const final { return is_accelerated_; }
-  bool IsValid() const override;
-  bool SupportsAcceleratedCompositing() const override { return true; }
-  bool NeedsReadLockFences() const final { return true; }
-  bool OriginClean() const final { return is_origin_clean_; }
-  void SetOriginClean(bool value) final { is_origin_clean_ = value; }
-  scoped_refptr<CanvasResource> MakeAccelerated(
-      base::WeakPtr<WebGraphicsContext3DProviderWrapper>) final {
-    NOTREACHED();
-    return nullptr;
-  }
-  scoped_refptr<CanvasResource> MakeUnaccelerated() final {
-    NOTREACHED();
-    return nullptr;
-  }
-  void Abandon() final;
-  IntSize Size() const final;
-  void TakeSkImage(sk_sp<SkImage> image) final;
-  void CopyFromTexture(GLuint source_texture,
-                       GLenum format,
-                       GLenum type) override;
-  scoped_refptr<StaticBitmapImage> Bitmap() override;
-  GLuint GetBackingTextureHandleForOverwrite() final;
-
- private:
-  void TearDown() override;
-  GLenum TextureTarget() const final;
-  bool IsOverlayCandidate() const final { return true; }
-  const gpu::Mailbox& GetOrCreateGpuMailbox(MailboxSyncMode) override;
-  bool HasGpuMailbox() const override;
-  const gpu::SyncToken GetSyncToken() override;
-  base::WeakPtr<WebGraphicsContext3DProviderWrapper> ContextProviderWrapper()
-      const override;
-  void WillPaint();
-  void DidPaint();
-
-  CanvasResourceGpuMemoryBuffer(
-      const IntSize&,
-      const CanvasColorParams&,
-      base::WeakPtr<WebGraphicsContext3DProviderWrapper>,
-      base::WeakPtr<CanvasResourceProvider>,
-      SkFilterQuality,
-      bool is_accelerated);
-
-  gpu::Mailbox gpu_mailbox_;
-  gpu::SyncToken sync_token_;
-  bool mailbox_needs_new_sync_token_ = false;
-  base::WeakPtr<WebGraphicsContext3DProviderWrapper> context_provider_wrapper_;
-  std::unique_ptr<gfx::GpuMemoryBuffer> gpu_memory_buffer_;
-  void* buffer_base_address_ = nullptr;
-  sk_sp<SkSurface> surface_;
-  GLuint texture_id_ = 0;
-  MailboxSyncMode mailbox_sync_mode_ = kVerifiedSyncToken;
-  bool is_accelerated_;
-  bool is_origin_clean_ = true;
-
-  // GL_TEXTURE_2D view of |gpu_memory_buffer_| for CopyFromTexture(); only used
-  // if TextureTarget() is GL_TEXTURE_EXTERNAL_OES.
-  GLuint texture_2d_id_for_copy_ = 0;
-};
-
 // Resource type for SharedBitmaps
 class PLATFORM_EXPORT CanvasResourceSharedBitmap final : public CanvasResource {
  public:
@@ -290,23 +228,9 @@
   bool IsValid() const final;
   bool SupportsAcceleratedCompositing() const final { return false; }
   bool NeedsReadLockFences() const final { return false; }
-  scoped_refptr<CanvasResource> MakeAccelerated(
-      base::WeakPtr<WebGraphicsContext3DProviderWrapper>) final {
-    NOTREACHED();
-    return nullptr;
-  }
-  scoped_refptr<CanvasResource> MakeUnaccelerated() final {
-    NOTREACHED();
-    return nullptr;
-  }
   void Abandon() final;
   IntSize Size() const final;
   void TakeSkImage(sk_sp<SkImage> image) final;
-  void CopyFromTexture(GLuint source_texture,
-                       GLenum format,
-                       GLenum type) override {
-    NOTREACHED();
-  }
   scoped_refptr<StaticBitmapImage> Bitmap() final;
   bool OriginClean() const final { return is_origin_clean_; }
   void SetOriginClean(bool flag) final { is_origin_clean_ = flag; }
@@ -352,15 +276,6 @@
 
   bool OriginClean() const final { return is_origin_clean_; }
   void SetOriginClean(bool value) final { is_origin_clean_ = value; }
-  scoped_refptr<CanvasResource> MakeAccelerated(
-      base::WeakPtr<WebGraphicsContext3DProviderWrapper>) final {
-    NOTREACHED();
-    return nullptr;
-  }
-  scoped_refptr<CanvasResource> MakeUnaccelerated() final {
-    NOTREACHED();
-    return nullptr;
-  }
   void TakeSkImage(sk_sp<SkImage> image) final { NOTREACHED(); }
   void NotifyResourceLost() final;
   bool NeedsReadLockFences() const final {
@@ -500,15 +415,6 @@
   bool NeedsReadLockFences() const final { return false; }
   bool OriginClean() const final { return is_origin_clean_; }
   void SetOriginClean(bool value) final { is_origin_clean_ = value; }
-  scoped_refptr<CanvasResource> MakeAccelerated(
-      base::WeakPtr<WebGraphicsContext3DProviderWrapper>) final {
-    NOTREACHED();
-    return nullptr;
-  }
-  scoped_refptr<CanvasResource> MakeUnaccelerated() final {
-    NOTREACHED();
-    return nullptr;
-  }
   void Abandon() final;
   IntSize Size() const final { return size_; }
   void TakeSkImage(sk_sp<SkImage> image) final;
@@ -559,15 +465,6 @@
   bool NeedsReadLockFences() const final { return false; }
   bool OriginClean() const final { return is_origin_clean_; }
   void SetOriginClean(bool value) final { is_origin_clean_ = value; }
-  scoped_refptr<CanvasResource> MakeAccelerated(
-      base::WeakPtr<WebGraphicsContext3DProviderWrapper>) final {
-    NOTREACHED();
-    return nullptr;
-  }
-  scoped_refptr<CanvasResource> MakeUnaccelerated() final {
-    NOTREACHED();
-    return nullptr;
-  }
   void Abandon() final;
   IntSize Size() const final { return size_; }
   void TakeSkImage(sk_sp<SkImage> image) final;
diff --git a/third_party/blink/renderer/platform/graphics/canvas_resource_test.cc b/third_party/blink/renderer/platform/graphics/canvas_resource_test.cc
index 3dc0a13..03156bb 100644
--- a/third_party/blink/renderer/platform/graphics/canvas_resource_test.cc
+++ b/third_party/blink/renderer/platform/graphics/canvas_resource_test.cc
@@ -20,382 +20,9 @@
 #include "third_party/blink/renderer/platform/wtf/functional.h"
 #include "third_party/skia/include/core/SkSurface.h"
 
-using testing::_;
-using testing::AtLeast;
-using testing::Pointee;
-using testing::Return;
-using testing::SetArrayArgument;
-using testing::Test;
-
 namespace blink {
 
-class MockGLES2InterfaceWithMailboxSupport : public FakeGLES2Interface {
- public:
-  MOCK_METHOD2(ProduceTextureDirectCHROMIUM, void(GLuint, GLbyte*));
-  MOCK_METHOD1(GenUnverifiedSyncTokenCHROMIUM, void(GLbyte*));
-  MOCK_METHOD1(GenSyncTokenCHROMIUM, void(GLbyte*));
-  MOCK_METHOD4(CreateImageCHROMIUM,
-               GLuint(ClientBuffer, GLsizei, GLsizei, GLenum));
-  MOCK_METHOD2(BindTexture, void(GLenum, GLuint));
-};
-
-class CanvasResourceTest : public Test {
- public:
-  void SetUp() override {
-    // Install our mock GL context so that it gets served by SharedGpuContext.
-    auto factory = [](FakeGLES2Interface* gl, bool* gpu_compositing_disabled)
-        -> std::unique_ptr<WebGraphicsContext3DProvider> {
-      *gpu_compositing_disabled = false;
-      return std::make_unique<FakeWebGraphicsContext3DProvider>(gl);
-    };
-    SharedGpuContext::SetContextProviderFactoryForTesting(
-        WTF::BindRepeating(factory, WTF::Unretained(&gl_)));
-    context_provider_wrapper_ = SharedGpuContext::ContextProviderWrapper();
-  }
-
-  void TearDown() override { SharedGpuContext::ResetForTesting(); }
-
-  GrContext* GetGrContext() {
-    return context_provider_wrapper_->ContextProvider()->GetGrContext();
-  }
-
- protected:
-  MockGLES2InterfaceWithMailboxSupport gl_;
-  base::WeakPtr<WebGraphicsContext3DProviderWrapper> context_provider_wrapper_;
-};
-
-gpu::SyncToken GenTestSyncToken(int id) {
-  gpu::SyncToken token;
-  token.Set(gpu::CommandBufferNamespace::GPU_IO,
-            gpu::CommandBufferId::FromUnsafeValue(id), 1);
-  return token;
-}
-
-TEST_F(CanvasResourceTest, SkiaResourceNoMailboxLeak) {
-  testing::InSequence s;
-  SkImageInfo image_info =
-      SkImageInfo::MakeN32(10, 10, kPremul_SkAlphaType, nullptr);
-  sk_sp<SkSurface> surface =
-      SkSurface::MakeRenderTarget(GetGrContext(), SkBudgeted::kYes, image_info);
-
-  testing::Mock::VerifyAndClearExpectations(&gl_);
-
-  EXPECT_TRUE(!!context_provider_wrapper_);
-  scoped_refptr<CanvasResource> resource = CanvasResourceBitmap::Create(
-      StaticBitmapImage::Create(surface->makeImageSnapshot(),
-                                context_provider_wrapper_),
-      nullptr, kLow_SkFilterQuality, CanvasColorParams());
-
-  testing::Mock::VerifyAndClearExpectations(&gl_);
-
-  gpu::Mailbox test_mailbox;
-  test_mailbox.name[0] = 1;
-  EXPECT_CALL(gl_, ProduceTextureDirectCHROMIUM(_, _))
-      .WillOnce(SetArrayArgument<1>(
-          test_mailbox.name, test_mailbox.name + GL_MAILBOX_SIZE_CHROMIUM));
-  EXPECT_CALL(gl_, BindTexture(GL_TEXTURE_2D, _)).Times(2);
-  EXPECT_CALL(gl_, GenUnverifiedSyncTokenCHROMIUM(_));
-  resource->GetOrCreateGpuMailbox(kUnverifiedSyncToken);
-
-  testing::Mock::VerifyAndClearExpectations(&gl_);
-
-  // No expected call to DeleteTextures becaus skia recycles
-  // No expected call to ProduceTextureDirectCHROMIUM(0, *) because
-  // mailbox is cached by GraphicsContext3DUtils and therefore does not need to
-  // be orphaned.
-  EXPECT_CALL(gl_, ProduceTextureDirectCHROMIUM(0, _)).Times(0);
-  resource = nullptr;
-
-  testing::Mock::VerifyAndClearExpectations(&gl_);
-
-  // Purging skia's resource cache will finally delete the GrTexture, resulting
-  // in the mailbox being orphaned via ProduceTextureDirectCHROMIUM.
-  EXPECT_CALL(gl_,
-              ProduceTextureDirectCHROMIUM(0, Pointee(test_mailbox.name[0])))
-      .Times(0);
-  GetGrContext()->performDeferredCleanup(std::chrono::milliseconds(0));
-
-  testing::Mock::VerifyAndClearExpectations(&gl_);
-}
-
-TEST_F(CanvasResourceTest, GpuMemoryBufferSyncTokenRefresh) {
-  testing::InSequence s;
-  ScopedTestingPlatformSupport<GpuMemoryBufferTestPlatform> platform;
-
-  constexpr GLuint image_id = 1;
-  const GLuint texture_target = gpu::GetPlatformSpecificTextureTarget();
-  EXPECT_CALL(gl_, CreateImageCHROMIUM(_, _, _, _)).WillOnce(Return(image_id));
-  EXPECT_CALL(gl_, BindTexture(texture_target, _));
-  if (texture_target == GL_TEXTURE_EXTERNAL_OES) {
-    constexpr GLuint image_2d_id = 2;
-    EXPECT_CALL(gl_, CreateImageCHROMIUM(_, _, _, _))
-        .WillOnce(Return(image_2d_id));
-    EXPECT_CALL(gl_, BindTexture(GL_TEXTURE_2D, _));
-  }
-  scoped_refptr<CanvasResource> resource =
-      CanvasResourceGpuMemoryBuffer::Create(
-          IntSize(10, 10), CanvasColorParams(),
-          SharedGpuContext::ContextProviderWrapper(),
-          nullptr,  // Resource provider
-          kLow_SkFilterQuality, true /*is_accelerated*/);
-
-  EXPECT_TRUE(bool(resource));
-
-  testing::Mock::VerifyAndClearExpectations(&gl_);
-
-  gpu::Mailbox test_mailbox;
-  test_mailbox.name[0] = 1;
-  EXPECT_CALL(gl_, ProduceTextureDirectCHROMIUM(_, _))
-      .WillOnce(SetArrayArgument<1>(
-          test_mailbox.name, test_mailbox.name + GL_MAILBOX_SIZE_CHROMIUM));
-  resource->GetOrCreateGpuMailbox(kVerifiedSyncToken);
-
-  testing::Mock::VerifyAndClearExpectations(&gl_);
-
-  const gpu::SyncToken reference_token1 = GenTestSyncToken(1);
-  EXPECT_CALL(gl_, GenSyncTokenCHROMIUM(_))
-      .WillOnce(SetArrayArgument<0>(
-          reinterpret_cast<const GLbyte*>(&reference_token1),
-          reinterpret_cast<const GLbyte*>(&reference_token1 + 1)));
-  gpu::SyncToken actual_token = resource->GetSyncToken();
-  EXPECT_EQ(actual_token, reference_token1);
-
-  testing::Mock::VerifyAndClearExpectations(&gl_);
-
-  // Grabbing the mailbox again without making any changes must not result in
-  // a sync token refresh
-  EXPECT_CALL(gl_, ProduceTextureDirectCHROMIUM(_, _)).Times(0);
-  EXPECT_CALL(gl_, GenSyncTokenCHROMIUM(_)).Times(0);
-  resource->GetOrCreateGpuMailbox(kVerifiedSyncToken);
-  actual_token = resource->GetSyncToken();
-  EXPECT_EQ(actual_token, reference_token1);
-
-  testing::Mock::VerifyAndClearExpectations(&gl_);
-
-  // Grabbing the mailbox again after a content change must result in
-  // a sync token refresh, but the mailbox gets re-used.
-  EXPECT_CALL(gl_, ProduceTextureDirectCHROMIUM(_, _)).Times(0);
-  const gpu::SyncToken reference_token2 = GenTestSyncToken(2);
-  EXPECT_CALL(gl_, GenSyncTokenCHROMIUM(_))
-      .WillOnce(SetArrayArgument<0>(
-          reinterpret_cast<const GLbyte*>(&reference_token2),
-          reinterpret_cast<const GLbyte*>(&reference_token2 + 1)));
-  resource->CopyFromTexture(1,  // source texture id
-                            GL_RGBA, GL_UNSIGNED_BYTE);
-  resource->GetOrCreateGpuMailbox(kVerifiedSyncToken);
-  actual_token = resource->GetSyncToken();
-  EXPECT_EQ(actual_token, reference_token2);
-
-  testing::Mock::VerifyAndClearExpectations(&gl_);
-}
-
-TEST_F(CanvasResourceTest, MakeAcceleratedFromAcceleratedResourceIsNoOp) {
-  ScopedTestingPlatformSupport<GpuMemoryBufferTestPlatform> platform;
-
-  SkImageInfo image_info =
-      SkImageInfo::MakeN32(10, 10, kPremul_SkAlphaType, nullptr);
-  sk_sp<SkSurface> surface =
-      SkSurface::MakeRenderTarget(GetGrContext(), SkBudgeted::kYes, image_info);
-
-  testing::Mock::VerifyAndClearExpectations(&gl_);
-
-  EXPECT_TRUE(!!context_provider_wrapper_);
-  scoped_refptr<CanvasResource> resource = CanvasResourceBitmap::Create(
-      StaticBitmapImage::Create(surface->makeImageSnapshot(),
-                                context_provider_wrapper_),
-      nullptr, kLow_SkFilterQuality, CanvasColorParams());
-
-  testing::Mock::VerifyAndClearExpectations(&gl_);
-
-  EXPECT_TRUE(resource->IsAccelerated());
-  scoped_refptr<CanvasResource> new_resource =
-      resource->MakeAccelerated(context_provider_wrapper_);
-
-  testing::Mock::VerifyAndClearExpectations(&gl_);
-
-  EXPECT_EQ(new_resource.get(), resource.get());
-  EXPECT_TRUE(new_resource->IsAccelerated());
-}
-
-TEST_F(CanvasResourceTest, MakeAcceleratedFromRasterResource) {
-  ScopedTestingPlatformSupport<GpuMemoryBufferTestPlatform> platform;
-
-  SkImageInfo image_info =
-      SkImageInfo::MakeN32(10, 10, kPremul_SkAlphaType, nullptr);
-  sk_sp<SkSurface> surface = SkSurface::MakeRaster(image_info);
-
-  EXPECT_TRUE(!!context_provider_wrapper_);
-  scoped_refptr<CanvasResource> resource = CanvasResourceBitmap::Create(
-      StaticBitmapImage::Create(surface->makeImageSnapshot(),
-                                context_provider_wrapper_),
-      nullptr, kLow_SkFilterQuality, CanvasColorParams());
-
-  testing::Mock::VerifyAndClearExpectations(&gl_);
-
-  EXPECT_FALSE(resource->IsAccelerated());
-  scoped_refptr<CanvasResource> new_resource =
-      resource->MakeAccelerated(context_provider_wrapper_);
-
-  testing::Mock::VerifyAndClearExpectations(&gl_);
-
-  EXPECT_NE(new_resource.get(), resource.get());
-  EXPECT_FALSE(resource->IsAccelerated());
-  EXPECT_TRUE(new_resource->IsAccelerated());
-}
-
-TEST_F(CanvasResourceTest, MakeUnacceleratedFromUnacceleratedResourceIsNoOp) {
-  ScopedTestingPlatformSupport<GpuMemoryBufferTestPlatform> platform;
-
-  SkImageInfo image_info =
-      SkImageInfo::MakeN32(10, 10, kPremul_SkAlphaType, nullptr);
-  sk_sp<SkSurface> surface = SkSurface::MakeRaster(image_info);
-
-  scoped_refptr<CanvasResource> resource = CanvasResourceBitmap::Create(
-      StaticBitmapImage::Create(surface->makeImageSnapshot(),
-                                context_provider_wrapper_),
-      nullptr, kLow_SkFilterQuality, CanvasColorParams());
-
-  EXPECT_FALSE(resource->IsAccelerated());
-  scoped_refptr<CanvasResource> new_resource = resource->MakeUnaccelerated();
-
-  EXPECT_EQ(new_resource.get(), resource.get());
-  EXPECT_FALSE(new_resource->IsAccelerated());
-}
-
-TEST_F(CanvasResourceTest, MakeUnacceleratedFromAcceleratedResource) {
-  ScopedTestingPlatformSupport<GpuMemoryBufferTestPlatform> platform;
-
-  SkImageInfo image_info =
-      SkImageInfo::MakeN32(10, 10, kPremul_SkAlphaType, nullptr);
-  sk_sp<SkSurface> surface =
-      SkSurface::MakeRenderTarget(GetGrContext(), SkBudgeted::kYes, image_info);
-
-  EXPECT_TRUE(!!context_provider_wrapper_);
-  scoped_refptr<CanvasResource> resource = CanvasResourceBitmap::Create(
-      StaticBitmapImage::Create(surface->makeImageSnapshot(),
-                                context_provider_wrapper_),
-      nullptr, kLow_SkFilterQuality, CanvasColorParams());
-
-  testing::Mock::VerifyAndClearExpectations(&gl_);
-
-  EXPECT_TRUE(resource->IsAccelerated());
-  scoped_refptr<CanvasResource> new_resource = resource->MakeUnaccelerated();
-
-  testing::Mock::VerifyAndClearExpectations(&gl_);
-
-  EXPECT_NE(new_resource.get(), resource.get());
-  EXPECT_TRUE(resource->IsAccelerated());
-  EXPECT_FALSE(new_resource->IsAccelerated());
-}
-
-void PaintToCanvasResource(CanvasResource* canvas_resource) {
-  SkImageInfo image_info = SkImageInfo::MakeN32(
-      canvas_resource->Size().Width(), canvas_resource->Size().Height(),
-      kPremul_SkAlphaType, SkColorSpace::MakeSRGB());
-  sk_sp<SkSurface> surface = SkSurface::MakeRaster(image_info);
-  SkPaint paint;
-  paint.setColor(SK_ColorYELLOW);
-  surface->getCanvas()->drawRect(SkRect::MakeXYWH(0, 0, 10, 10), paint);
-  canvas_resource->TakeSkImage(surface->makeImageSnapshot());
-}
-
-TEST_F(CanvasResourceTest, RamGpuMemoryBuffer_ResourcePreparation) {
-  testing::InSequence s;
-  ScopedTestingPlatformSupport<GpuMemoryBufferTestPlatform> platform;
-
-  EXPECT_TRUE(!!context_provider_wrapper_);
-  constexpr GLuint image_id = 1;
-  EXPECT_CALL(gl_, CreateImageCHROMIUM(_, _, _, _)).WillOnce(Return(image_id));
-  EXPECT_CALL(gl_, BindTexture(gpu::GetPlatformSpecificTextureTarget(), _));
-
-  constexpr bool is_accelerated = false;
-  scoped_refptr<CanvasResource> canvas_resource =
-      CanvasResourceGpuMemoryBuffer::Create(
-          IntSize(10, 10), CanvasColorParams(), context_provider_wrapper_,
-          nullptr /*CanvasResourceProvider*/, kLow_SkFilterQuality,
-          is_accelerated);
-
-  EXPECT_TRUE(!!canvas_resource);
-  testing::Mock::VerifyAndClearExpectations(&gl_);
-
-  if (canvas_resource) {
-    gpu::Mailbox test_mailbox;
-    test_mailbox.name[0] = 1;
-    EXPECT_CALL(gl_, ProduceTextureDirectCHROMIUM(_, _))
-        .WillOnce(SetArrayArgument<1>(
-            test_mailbox.name, test_mailbox.name + GL_MAILBOX_SIZE_CHROMIUM));
-
-    PaintToCanvasResource(canvas_resource.get());
-
-    viz::TransferableResource transferable_resource;
-    std::unique_ptr<viz::SingleReleaseCallback> release_callback;
-
-    bool success = canvas_resource->PrepareTransferableResource(
-        &transferable_resource, &release_callback, kUnverifiedSyncToken);
-
-    EXPECT_TRUE(success);
-
-    release_callback->Run(gpu::SyncToken(), false);
-  }
-}
-
-TEST_F(CanvasResourceTest, GpuMemoryBuffer_accelerated_8bit) {
-  testing::InSequence s;
-  ScopedTestingPlatformSupport<GpuMemoryBufferTestPlatform> platform;
-  EXPECT_TRUE(!!context_provider_wrapper_);
-
-  constexpr GLuint image_id = 1;
-  EXPECT_CALL(gl_, CreateImageCHROMIUM(_, _, _, _)).WillOnce(Return(image_id));
-  EXPECT_CALL(gl_, BindTexture(_, _));
-
-  if (gpu::GetPlatformSpecificTextureTarget() == GL_TEXTURE_EXTERNAL_OES) {
-    constexpr GLuint second_image_id = 2;
-    EXPECT_CALL(gl_, CreateImageCHROMIUM(_, _, _, _))
-        .WillOnce(Return(second_image_id));
-    EXPECT_CALL(gl_, BindTexture(_, _));
-  }
-
-  constexpr bool is_accelerated = true;
-  scoped_refptr<CanvasResource> canvas_resource =
-      CanvasResourceGpuMemoryBuffer::Create(
-          IntSize(10, 10), CanvasColorParams(), context_provider_wrapper_,
-          nullptr /*CanvasResourceProvider*/, kLow_SkFilterQuality,
-          is_accelerated);
-
-  EXPECT_TRUE(!!canvas_resource);
-}
-
-TEST_F(CanvasResourceTest, GpuMemoryBuffer_accelerated_float16) {
-  testing::InSequence s;
-  ScopedTestingPlatformSupport<GpuMemoryBufferTestPlatform> platform;
-  EXPECT_TRUE(!!context_provider_wrapper_);
-
-  constexpr GLuint image_id = 1;
-  EXPECT_CALL(gl_, CreateImageCHROMIUM(_, _, _, _)).WillOnce(Return(image_id));
-  EXPECT_CALL(gl_, BindTexture(_, _));
-
-  if (gpu::GetPlatformSpecificTextureTarget() == GL_TEXTURE_EXTERNAL_OES) {
-    constexpr GLuint second_image_id = 2;
-    EXPECT_CALL(gl_, CreateImageCHROMIUM(_, _, _, _))
-        .WillOnce(Return(second_image_id));
-    EXPECT_CALL(gl_, BindTexture(_, _));
-  }
-
-  constexpr bool is_accelerated = true;
-  scoped_refptr<CanvasResource> canvas_resource =
-      CanvasResourceGpuMemoryBuffer::Create(
-          IntSize(10, 10),
-          CanvasColorParams(kSRGBCanvasColorSpace, kF16CanvasPixelFormat,
-                            kNonOpaque),
-          context_provider_wrapper_, nullptr /*CanvasResourceProvider*/,
-          kLow_SkFilterQuality, is_accelerated);
-
-  EXPECT_TRUE(!!canvas_resource);
-}
-
-TEST_F(CanvasResourceTest, PrepareTransferableResource_SharedBitmap) {
-  testing::InSequence s;
-  ScopedTestingPlatformSupport<GpuMemoryBufferTestPlatform> platform;
+TEST(CanvasResourceTest, PrepareTransferableResource_SharedBitmap) {
   scoped_refptr<CanvasResource> canvas_resource =
       CanvasResourceSharedBitmap::Create(IntSize(10, 10), CanvasColorParams(),
                                          nullptr,  // CanvasResourceProvider
diff --git a/third_party/blink/renderer/platform/runtime_enabled_features.json5 b/third_party/blink/renderer/platform/runtime_enabled_features.json5
index cc8669b1..706b9a1b 100644
--- a/third_party/blink/renderer/platform/runtime_enabled_features.json5
+++ b/third_party/blink/renderer/platform/runtime_enabled_features.json5
@@ -137,6 +137,13 @@
       status: "experimental",
     },
     {
+      // Use an aspect ratio from the HTML attributes even when we use sizing
+      // from CSS.
+      // https://github.com/WICG/intrinsicsize-attribute/issues/16
+      name: "AspectRatioFromWidthAndHeight",
+      status: "test",
+    },
+    {
       name: "AsyncClipboard",
       status: "stable",
     },
@@ -962,6 +969,9 @@
       name: "MediaEngagementBypassAutoplayPolicies",
     },
     {
+      name: "MediaQueryNavigationControls",
+    },
+    {
       name: "MediaQueryPrefersColorScheme",
       status: "stable",
     },
diff --git a/third_party/blink/web_tests/TestExpectations b/third_party/blink/web_tests/TestExpectations
index 8b0e003..70dd45d 100644
--- a/third_party/blink/web_tests/TestExpectations
+++ b/third_party/blink/web_tests/TestExpectations
@@ -2160,7 +2160,6 @@
 crbug.com/983788 http/tests/cookies/http-get-cookie-set-in-js.html [ Pass Failure ]
 crbug.com/983788 http/tests/cookies/same-site/popup-cross-site.https.html [ Pass Timeout ]
 crbug.com/984438 external/wpt/css/css-scroll-snap/snap-at-user-scroll-end-manual.html [ Failure Pass ]
-crbug.com/964239 [ Win7 ] virtual/threaded/external/wpt/css/css-scroll-snap/scroll-margin.html [ Failure ]
 crbug.com/984467 virtual/gpu/fast/canvas/canvas-composite-stroke-alpha.html [ Pass Timeout ]
 
 ### sheriff 2018-05-28
diff --git a/third_party/blink/web_tests/WPTOverrideExpectations b/third_party/blink/web_tests/WPTOverrideExpectations
index f462ea6..9d1118a 100644
--- a/third_party/blink/web_tests/WPTOverrideExpectations
+++ b/third_party/blink/web_tests/WPTOverrideExpectations
@@ -1014,7 +1014,6 @@
 crbug.com/lpz external/wpt/html/rendering/non-replaced-elements/the-page/iframe-marginwidth-marginheight.html [ Failure ]
 crbug.com/lpz external/wpt/html/rendering/pixel-length-attributes.html [ Failure ]
 crbug.com/lpz external/wpt/html/rendering/replaced-elements/attributes-for-embedded-content-and-images/align.html [ Failure ]
-crbug.com/lpz external/wpt/html/rendering/replaced-elements/attributes-for-embedded-content-and-images/img-aspect-ratio.tentative.html [ Failure ]
 crbug.com/lpz external/wpt/html/rendering/unmapped-attributes.html [ Failure ]
 crbug.com/lpz external/wpt/html/rendering/widgets/button-layout/abspos.html [ Failure ]
 crbug.com/lpz external/wpt/html/rendering/widgets/button-layout/computed-style.html [ Failure ]
diff --git a/third_party/blink/web_tests/accessibility/element-role-mapping-normal-expected.txt b/third_party/blink/web_tests/accessibility/element-role-mapping-normal-expected.txt
index 1f13b59a..eb17323 100644
--- a/third_party/blink/web_tests/accessibility/element-role-mapping-normal-expected.txt
+++ b/third_party/blink/web_tests/accessibility/element-role-mapping-normal-expected.txt
@@ -64,7 +64,7 @@
             AXRole: AXStaticText "Paragraph"
                 AXRole: AXInlineTextBox "Paragraph"
         AXRole: AXRuby
-            AXRole: AXAnnotation
+            AXRole: AXRubyAnnotation
                 AXRole: AXStaticText "한국"
                     AXRole: AXInlineTextBox "한국"
             AXRole: AXStaticText "韓國"
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 afb1ae3..ce975a9 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
@@ -177895,13 +177895,13 @@
    "workers/semantics/interface-objects/003-expected.txt": [
     []
    ],
-   "workers/semantics/interface-objects/003.js": [
+   "workers/semantics/interface-objects/003.any.sharedworker-expected.txt": [
     []
    ],
    "workers/semantics/interface-objects/004-expected.txt": [
     []
    ],
-   "workers/semantics/interface-objects/004.js": [
+   "workers/semantics/interface-objects/004.any.sharedworker-expected.txt": [
     []
    ],
    "workers/semantics/multiple-workers/001.js": [
@@ -178219,6 +178219,12 @@
    "worklets/resources/import-remote-origin-referrer-checker-worklet-script.sub.js.headers": [
     []
    ],
+   "worklets/resources/import-same-origin-referrer-checker-worklet-script-from-remote-origin.sub.js": [
+    []
+   ],
+   "worklets/resources/import-same-origin-referrer-checker-worklet-script-from-remote-origin.sub.js.headers": [
+    []
+   ],
    "worklets/resources/import-syntax-error-worklet-script.js": [
     []
    ],
@@ -309540,16 +309546,30 @@
      {}
     ]
    ],
-   "workers/semantics/interface-objects/003.html": [
+   "workers/semantics/interface-objects/003.any.js": [
     [
-     "workers/semantics/interface-objects/003.html",
-     {}
+     "workers/semantics/interface-objects/003.any.sharedworker.html",
+     {
+      "script_metadata": [
+       [
+        "global",
+        "!default,sharedworker"
+       ]
+      ]
+     }
     ]
    ],
-   "workers/semantics/interface-objects/004.html": [
+   "workers/semantics/interface-objects/004.any.js": [
     [
-     "workers/semantics/interface-objects/004.html",
-     {}
+     "workers/semantics/interface-objects/004.any.sharedworker.html",
+     {
+      "script_metadata": [
+       [
+        "global",
+        "!default,sharedworker"
+       ]
+      ]
+     }
     ]
    ],
    "workers/semantics/multiple-workers/001.html": [
@@ -424503,7 +424523,7 @@
    "support"
   ],
   "encrypted-media/scripts/clearkey-update-non-ascii-input.js": [
-   "b34c493fd113a91780bf365800b71b98b6a3bfd6",
+   "7a5c073bfab150be54d8bce4b890bf36d731f7ef",
    "support"
   ],
   "encrypted-media/scripts/events-session-closed-event.js": [
@@ -424519,11 +424539,11 @@
    "support"
   ],
   "encrypted-media/scripts/generate-request-disallowed-input.js": [
-   "8b883ccacff17468f98df5d9d704c494abbac51d",
+   "9fd42ee85fa7d9390cbadba60dc4236891e99a54",
    "support"
   ],
   "encrypted-media/scripts/invalid-license.js": [
-   "992eef5dcf78961ad8f1c45f939e67330de78c57",
+   "89d43769e5346c6dc4046d0227af490e029473a2",
    "support"
   ],
   "encrypted-media/scripts/keystatuses-multiple-sessions.js": [
@@ -424619,7 +424639,7 @@
    "support"
   ],
   "encrypted-media/scripts/requestmediakeysystemaccess.js": [
-   "edfcbfc613310ef8db33e9d6ae2d97032ea73783",
+   "a60d4a1e768618ae0952063e7cdd9192ccfd7ecf",
    "support"
   ],
   "encrypted-media/scripts/reset-src-after-setmediakeys.js": [
@@ -424651,7 +424671,7 @@
    "support"
   ],
   "encrypted-media/scripts/setmediakeys.js": [
-   "f161e6712fd09c43b6659021c33554ba6680a775",
+   "a85adeaeafc63cf3f8224eee52d89aebab9b6450",
    "support"
   ],
   "encrypted-media/scripts/syntax-mediakeys.js": [
@@ -424659,7 +424679,7 @@
    "support"
   ],
   "encrypted-media/scripts/syntax-mediakeysession.js": [
-   "3663a7b930db8c8d66cc9345a394ee59aa85ae50",
+   "fac31cbb3e57e9890d83f4c4fb671eaa7de11c3a",
    "support"
   ],
   "encrypted-media/scripts/syntax-mediakeysystemaccess.js": [
@@ -424667,7 +424687,7 @@
    "support"
   ],
   "encrypted-media/scripts/temporary-license-type.js": [
-   "cb0b0e67fefc1558a0e1caeb477d3ef4227165ab",
+   "44c9f158085fcffd42a8573e080d17939a7a866c",
    "support"
   ],
   "encrypted-media/scripts/unique-origin.js": [
@@ -424675,7 +424695,7 @@
    "support"
   ],
   "encrypted-media/scripts/update-disallowed-input.js": [
-   "b5adaf7f125c10c57d1bde2a19f9536270425f55",
+   "2a30ad38d4310db6da0f84752b23d46f0a9c81e8",
    "support"
   ],
   "encrypted-media/scripts/waiting-for-a-key.js": [
@@ -424703,7 +424723,7 @@
    "support"
   ],
   "encrypted-media/util/utils.js": [
-   "41bd71f9b880711e088daed2bd223404018b9d8e",
+   "79f8c7ea6d1b5c5c38131205c20499019ba7371c",
    "support"
   ],
   "entries-api/META.yml": [
@@ -451115,7 +451135,7 @@
    "support"
   ],
   "interfaces/css-masking.idl": [
-   "80f908d42f3e4e4d0eadb1825dc6a4dbf20b1f2d",
+   "6db59eddbe087310010be1e60b826b72924c7849",
    "support"
   ],
   "interfaces/css-paint-api.idl": [
@@ -451199,7 +451219,7 @@
    "support"
   ],
   "interfaces/generic-sensor.idl": [
-   "2921c50a65e8d4467a56c5d337ef4d476e3ee0a2",
+   "f3eeb9bf968f7a1a0ba6a772ad935b855b1e9e8a",
    "support"
   ],
   "interfaces/geolocation-API.idl": [
@@ -451319,7 +451339,7 @@
    "support"
   ],
   "interfaces/orientation-sensor.idl": [
-   "9ee099881b0a59ca69da02e9aa3d1886e4bc05bc",
+   "2ea3d38240e527f10af7ef392e0c0935a3750572",
    "support"
   ],
   "interfaces/page-visibility.idl": [
@@ -453671,7 +453691,7 @@
    "support"
   ],
   "media-capabilities/decodingInfo.any.js": [
-   "737ded18c9f3a24f7ef65af2b770dbb9b5865fef",
+   "edd58ac3409bcbab14277f38560c7045178a2dcb",
    "testharness"
   ],
   "media-capabilities/decodingInfoEncryptedMedia.http.html": [
@@ -483703,7 +483723,7 @@
    "testharness"
   ],
   "storage/opaque-origin.https.window.js": [
-   "3e101dde6759b2b8c379d6adc95190742328630f",
+   "cc1d31fdf2c68322f72a40b1e84ec05ab1930779",
    "testharness"
   ],
   "storage/permission-query.https.any.js": [
@@ -495063,7 +495083,7 @@
    "support"
   ],
   "webaudio/resources/audit.js": [
-   "1876c0fc77174d861d3847f874dbad26c737d430",
+   "2aac1fffb492ea46e737257df08a09e65f6d3415",
    "support"
   ],
   "webaudio/resources/biquad-filters.js": [
@@ -495859,7 +495879,7 @@
    "testharness"
   ],
   "webaudio/the-audio-api/the-pannernode-interface/ctor-panner.html": [
-   "d330c9c3de4482ee70c036e0ab7ff8feb1fd5a36",
+   "c434aa8c6a50bc2763f7a9233db06466c501c7ac",
    "testharness"
   ],
   "webaudio/the-audio-api/the-pannernode-interface/distance-exponential.html": [
@@ -505070,24 +505090,24 @@
    "e72a68ecc1b03d21f201b48630cea1a481978c8d",
    "support"
   ],
-  "workers/semantics/interface-objects/003.html": [
-   "99e8b3a0f3dd426eb4b1a5f22a2fd0fcab01d98d",
+  "workers/semantics/interface-objects/003.any.js": [
+   "1942a4658dfb8e2ecab52c914af9aff8cf79cc2a",
    "testharness"
   ],
-  "workers/semantics/interface-objects/003.js": [
-   "a91e7c38759f23e9ca50602a853d67a26830adad",
+  "workers/semantics/interface-objects/003.any.sharedworker-expected.txt": [
+   "c20fb7c8107257ae1da5d1f8ad498671c2bfcd64",
    "support"
   ],
   "workers/semantics/interface-objects/004-expected.txt": [
    "d834acc47ce940bf6d1c9eeb9c36bd76d49b2a33",
    "support"
   ],
-  "workers/semantics/interface-objects/004.html": [
-   "b4a09c50e4e50983f814fb08fc0d797a661b2ff5",
+  "workers/semantics/interface-objects/004.any.js": [
+   "358af74ca2c2bda6a0805ec8bded53a92674eb17",
    "testharness"
   ],
-  "workers/semantics/interface-objects/004.js": [
-   "00e50d19cae409d6b69688e22ed926ac9893e2d9",
+  "workers/semantics/interface-objects/004.any.sharedworker-expected.txt": [
+   "d834acc47ce940bf6d1c9eeb9c36bd76d49b2a33",
    "support"
   ],
   "workers/semantics/multiple-workers/001.html": [
@@ -505523,7 +505543,7 @@
    "testharness"
   ],
   "worklets/animation-worklet-referrer.https-expected.txt": [
-   "d605c7563fa39501b27a0d574fc611c9a2084356",
+   "afa4657822c86046a8127c674f507b7c0d07d6db",
    "support"
   ],
   "worklets/animation-worklet-referrer.https.html": [
@@ -505551,7 +505571,7 @@
    "testharness"
   ],
   "worklets/audio-worklet-referrer.https-expected.txt": [
-   "d605c7563fa39501b27a0d574fc611c9a2084356",
+   "afa4657822c86046a8127c674f507b7c0d07d6db",
    "support"
   ],
   "worklets/audio-worklet-referrer.https.html": [
@@ -505583,7 +505603,7 @@
    "testharness"
   ],
   "worklets/layout-worklet-referrer.https-expected.txt": [
-   "d605c7563fa39501b27a0d574fc611c9a2084356",
+   "afa4657822c86046a8127c674f507b7c0d07d6db",
    "support"
   ],
   "worklets/layout-worklet-referrer.https.html": [
@@ -505611,7 +505631,7 @@
    "testharness"
   ],
   "worklets/paint-worklet-referrer.https-expected.txt": [
-   "d605c7563fa39501b27a0d574fc611c9a2084356",
+   "afa4657822c86046a8127c674f507b7c0d07d6db",
    "support"
   ],
   "worklets/paint-worklet-referrer.https.html": [
@@ -505706,6 +505726,14 @@
    "cb762eff806849df46dc758ef7b98b63f27f54c9",
    "support"
   ],
+  "worklets/resources/import-same-origin-referrer-checker-worklet-script-from-remote-origin.sub.js": [
+   "33af2258ff3504db7dc1f7c7564fd2306779c206",
+   "support"
+  ],
+  "worklets/resources/import-same-origin-referrer-checker-worklet-script-from-remote-origin.sub.js.headers": [
+   "cb762eff806849df46dc758ef7b98b63f27f54c9",
+   "support"
+  ],
   "worklets/resources/import-syntax-error-worklet-script.js": [
    "1539ad004ed41f521f42b7e564fdf1cbfbdc2de3",
    "support"
@@ -505719,11 +505747,11 @@
    "support"
   ],
   "worklets/resources/referrer-tests.js": [
-   "01b8e2ab57acb4c41d898f1541e2011c4946bd0a",
+   "b3c4a048f5a12350ce79ba93049d81858f54201f",
    "support"
   ],
   "worklets/resources/referrer-window.html": [
-   "4817f0452a2999ae2774b2849130cb11e670778d",
+   "934e3dc41b1cda364a00fe4fae0e4bc114e9c4b6",
    "support"
   ],
   "worklets/resources/service-worker-interception-tests.js": [
diff --git a/third_party/blink/web_tests/external/wpt/css/CSS2/floats/negative-margin-float-positioning.html b/third_party/blink/web_tests/external/wpt/css/CSS2/floats/negative-margin-float-positioning.html
new file mode 100644
index 0000000..8b4aef2
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/CSS2/floats/negative-margin-float-positioning.html
@@ -0,0 +1,9 @@
+<!DOCTYPE html>
+<link rel="help" href="https://www.w3.org/TR/CSS22/visudet.html#float-width" title="10.3.5 Floating, non-replaced elements">
+<link rel="match" href="../../reference/ref-filled-green-100px-square-only.html">
+<p>Test passes if there is a filled green square.</p>
+<div style="width: 100px;">
+  <div style="float: left; width: 50px; height: 100px; margin-left: 50px; margin-right: 50px;background: green;"></div>
+  <div style="float: left; width: 50px; height: 100px; margin-left: -150px; background: green;"></div>
+</div>
+
diff --git a/third_party/blink/web_tests/external/wpt/css/css-transitions/AnimationEffect-getComputedTiming.tentative-expected.txt b/third_party/blink/web_tests/external/wpt/css/css-transitions/AnimationEffect-getComputedTiming.tentative-expected.txt
deleted file mode 100644
index dfb103d1..0000000
--- a/third_party/blink/web_tests/external/wpt/css/css-transitions/AnimationEffect-getComputedTiming.tentative-expected.txt
+++ /dev/null
@@ -1,24 +0,0 @@
-This is a testharness.js-based test.
-PASS delay of a new tranisition
-PASS Positive delay of a new transition
-PASS Negative delay of a new transition
-PASS endDelay of a new transition
-PASS fill of a new transition
-PASS iterationStart of a new transition
-PASS iterations of a new transition
-PASS duration of a new transition
-PASS direction of a new transition
-FAIL easing of a new transition assert_equals: Initial value of easing expected "linear" but got "ease"
-PASS endTime of a new transition
-PASS activeDuration of a new transition
-PASS localTime of a new transition
-PASS localTime is always equal to currentTime
-PASS localTime reflects playbackRate immediately
-PASS progress of a new transition
-PASS progress of a new transition with positive delay in before phase
-PASS progress of a finished transition
-PASS currentIteration of a new transition
-PASS currentIteration of a new transition with positive delay in before phase
-PASS currentIteration of a finished transition
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/external/wpt/css/css-transitions/AnimationEffect-getComputedTiming.tentative.html b/third_party/blink/web_tests/external/wpt/css/css-transitions/AnimationEffect-getComputedTiming.tentative.html
index 4b6a28b5..32c15f07 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-transitions/AnimationEffect-getComputedTiming.tentative.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-transitions/AnimationEffect-getComputedTiming.tentative.html
@@ -162,10 +162,21 @@
   div.style.marginLeft = '10px';
 
   const effect = div.getAnimations()[0].effect;
-  assert_equals(effect.getComputedTiming().easing, 'linear',
+  assert_equals(effect.getComputedTiming().easing, 'ease',
                 'Initial value of easing');
 }, 'easing of a new transition');
 
+test(t => {
+  const div = addDiv(t, { class: 'animated-div' });
+  div.style.transition = 'margin-left 10s steps(4)';
+  getComputedStyle(div).marginLeft;
+  div.style.marginLeft = '10px';
+
+  const effect = div.getAnimations()[0].effect;
+  assert_equals(effect.getComputedTiming().easing, 'steps(4)',
+                'Initial value of easing');
+}, 'non-default easing of a new transition');
+
 
 // ------------------------------
 // endTime
diff --git a/third_party/blink/web_tests/external/wpt/css/css-transitions/KeyframeEffect-getKeyframes.tentative-expected.txt b/third_party/blink/web_tests/external/wpt/css/css-transitions/KeyframeEffect-getKeyframes.tentative-expected.txt
index 30c5dac..18311024 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-transitions/KeyframeEffect-getKeyframes.tentative-expected.txt
+++ b/third_party/blink/web_tests/external/wpt/css/css-transitions/KeyframeEffect-getKeyframes.tentative-expected.txt
@@ -1,6 +1,6 @@
 This is a testharness.js-based test.
 FAIL KeyframeEffect.getKeyframes() returns expected frames for a simple transition assert_equals: properties on ComputedKeyframe #0 expected "composite,computedOffset,easing,left,offset" but got "composite,computedOffset,easing,offset"
-FAIL KeyframeEffect.getKeyframes() returns expected frames for a simple transition with a non-default easing function assert_equals: properties on ComputedKeyframe #0 expected "composite,computedOffset,easing,left,offset" but got "composite,computedOffset,easing,offset"
+FAIL KeyframeEffect.getKeyframes() returns frames unaffected by a non-default easing function assert_equals: properties on ComputedKeyframe #0 expected "composite,computedOffset,easing,left,offset" but got "composite,computedOffset,easing,offset"
 FAIL KeyframeEffect.getKeyframes() returns expected frames for a transition with a CSS variable endpoint assert_equals: properties on ComputedKeyframe #0 expected "composite,computedOffset,easing,left,offset" but got "composite,computedOffset,easing,offset"
 Harness: the test ran to completion.
 
diff --git a/third_party/blink/web_tests/external/wpt/css/css-transitions/KeyframeEffect-getKeyframes.tentative.html b/third_party/blink/web_tests/external/wpt/css/css-transitions/KeyframeEffect-getKeyframes.tentative.html
index 08a907343..47b769b1 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-transitions/KeyframeEffect-getKeyframes.tentative.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-transitions/KeyframeEffect-getKeyframes.tentative.html
@@ -45,7 +45,7 @@
   const expected = [
     { offset: 0,
       computedOffset: 0,
-      easing: 'ease',
+      easing: 'linear',
       composite: 'auto',
       left: '0px',
     },
@@ -80,7 +80,7 @@
     {
       offset: 0,
       computedOffset: 0,
-      easing: 'steps(2)',
+      easing: 'linear',
       composite: 'auto',
       left: '0px',
     },
@@ -96,8 +96,7 @@
   for (let i = 0; i < frames.length; i++) {
     assert_frames_equal(frames[i], expected[i], `ComputedKeyframe #${i}`);
   }
-}, 'KeyframeEffect.getKeyframes() returns expected frames for a simple'
-   + ' transition with a non-default easing function');
+}, 'KeyframeEffect.getKeyframes() returns frames unaffected by a non-default easing function');
 
 test(t => {
   const div = addDiv(t);
@@ -114,7 +113,7 @@
     {
       offset: 0,
       computedOffset: 0,
-      easing: 'ease',
+      easing: 'linear',
       composite: 'auto',
       left: '0px',
     },
diff --git a/third_party/blink/web_tests/external/wpt/css/mediaqueries/navigation-controls.tentative-expected.txt b/third_party/blink/web_tests/external/wpt/css/mediaqueries/navigation-controls.tentative-expected.txt
new file mode 100644
index 0000000..f23231c
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/mediaqueries/navigation-controls.tentative-expected.txt
@@ -0,0 +1,14 @@
+This is a testharness.js-based test.
+PASS Should be parseable in a CSS stylesheet: '(navigation-controls)'
+FAIL Should be parseable in a CSS stylesheet: '(navigation-controls: none)' assert_true: expected true got false
+FAIL Should be parseable in a CSS stylesheet: '(navigation-controls: back-button)' assert_true: expected true got false
+PASS Should not be parseable in a CSS stylesheet: '(navigation-controls: none back-button)'
+PASS Should not be parseable in a CSS stylesheet: '(navigation-controls: back-button/none)'
+PASS Should be parseable in JS: '(navigation-controls)'
+FAIL Should be parseable in JS: '(navigation-controls: none)' assert_true: expected true got false
+FAIL Should be parseable in JS: '(navigation-controls: back-button)' assert_true: expected true got false
+PASS Should not be parseable in JS: '(navigation-controls: none back-button)'
+PASS Should not be parseable in JS: '(navigation-controls: back-button/none)'
+FAIL Check that none evaluates to false in the boolean context assert_equals: expected true but got false
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/external/wpt/css/mediaqueries/navigation-controls.tentative.html b/third_party/blink/web_tests/external/wpt/css/mediaqueries/navigation-controls.tentative.html
new file mode 100644
index 0000000..ac1087bb
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/mediaqueries/navigation-controls.tentative.html
@@ -0,0 +1,22 @@
+<!DOCTYPE html>
+<link rel="help" href="https://github.com/fallaciousreasoning/backbutton-mediaquery/blob/master/explainer.md" />
+<script type="text/javascript" src="/resources/testharness.js"></script>
+<script type="text/javascript" src="/resources/testharnessreport.js"></script>
+<script type="text/javascript" src="resources/matchmedia-utils.js"></script>
+<script>
+    query_should_be_css_parseable("(navigation-controls)");
+    query_should_be_css_parseable("(navigation-controls: none)");
+    query_should_be_css_parseable("(navigation-controls: back-button)");
+    query_should_not_be_css_parseable("(navigation-controls: none back-button)");
+    query_should_not_be_css_parseable("(navigation-controls: back-button/none)");
+    query_should_be_js_parseable("(navigation-controls)");
+    query_should_be_js_parseable("(navigation-controls: none)");
+    query_should_be_js_parseable("(navigation-controls: back-button)");
+    query_should_not_be_js_parseable("(navigation-controls: none back-button)");
+    query_should_not_be_js_parseable("(navigation-controls: back-button/none)");
+    test(() => {
+        let booleanContext = window.matchMedia("(navigation-controls)");
+        let none = window.matchMedia("(navigation-controls: none)");
+        assert_equals(booleanContext.matches, !none.matches);
+    }, "Check that none evaluates to false in the boolean context");
+</script>
\ No newline at end of file
diff --git a/third_party/blink/web_tests/external/wpt/encrypted-media/scripts/clearkey-update-non-ascii-input.js b/third_party/blink/web_tests/external/wpt/encrypted-media/scripts/clearkey-update-non-ascii-input.js
index b34c493..7a5c073b 100644
--- a/third_party/blink/web_tests/external/wpt/encrypted-media/scripts/clearkey-update-non-ascii-input.js
+++ b/third_party/blink/web_tests/external/wpt/encrypted-media/scripts/clearkey-update-non-ascii-input.js
@@ -15,7 +15,7 @@
         var mediaKeySession;
         var messageEventFired = false;
 
-        return navigator.requestMediaKeySystemAccess(config.keysystem, [configuration]).then(function (access) {
+        var p = navigator.requestMediaKeySystemAccess(config.keysystem, [configuration]).then(function (access) {
             initDataType = access.getConfiguration().initDataTypes[0];
             initData = getInitData(config.content, initDataType);
             return access.createMediaKeys();
@@ -34,15 +34,19 @@
                 + '}]}';
             messageEventFired = true;
             return messageEvent.target.update(stringToUint8Array(jwkSet));
-        }).then(function () {
-            assert_unreached('Error: update() should fail because the processed message has non-ASCII character.');
         }).catch(function (error) {
-            if(messageEventFired){
-                assert_equals(error.name, 'TypeError');
+            // Ensure we reached the update() call we are trying to test.
+            if (!messageEventFired) {
+                assert_unreached(
+                    `Failed to reach the update() call.  Error: '${error.name}' '${error.message}'`);
             }
-            else {
-                assert_unreached('Error: ' + error.name);
-            }
+
+            // Propagate the error on through.
+            throw error;
         });
+
+        return promise_rejects_js(
+            test, TypeError, p,
+            'update() should fail because the processed message has non-ASCII character.');
     }, testname);
-}
\ No newline at end of file
+}
diff --git a/third_party/blink/web_tests/external/wpt/encrypted-media/scripts/generate-request-disallowed-input.js b/third_party/blink/web_tests/external/wpt/encrypted-media/scripts/generate-request-disallowed-input.js
index 8b883cca..9fd42ee8 100644
--- a/third_party/blink/web_tests/external/wpt/encrypted-media/scripts/generate-request-disallowed-input.js
+++ b/third_party/blink/web_tests/external/wpt/encrypted-media/scripts/generate-request-disallowed-input.js
@@ -57,17 +57,16 @@
                 // with the provided initData. generateRequest() should fail with an
                 // TypeError. Returns a promise that is resolved
                 // if the error occurred and rejected otherwise.
-                return navigator.requestMediaKeySystemAccess(testspec.keysystem, getSimpleConfigurationForInitDataType(testspec.initDataType)).then(function(access) {
+                var p = navigator.requestMediaKeySystemAccess(testspec.keysystem, getSimpleConfigurationForInitDataType(testspec.initDataType)).then(function(access) {
                     return access.createMediaKeys();
                 }).then(function(mediaKeys) {
                     var mediaKeySession = mediaKeys.createSession("temporary");
                     return mediaKeySession.generateRequest(testspec.initDataType, testspec.initData);
-                }).then(test.step_func(function() {
-                    assert_unreached('generateRequest() succeeded unexpectedly');
-                }), test.step_func(function(error) {
-                    assert_equals(error.name, 'TypeError');
-                }));
-            },testspec.testname);
+                });
+
+                return promise_rejects_js(test, TypeError, p,
+                                          "generateRequest() should fail");
+            }, testspec.testname);
         });
     });
 }
diff --git a/third_party/blink/web_tests/external/wpt/encrypted-media/scripts/invalid-license.js b/third_party/blink/web_tests/external/wpt/encrypted-media/scripts/invalid-license.js
index 992eef5..89d4376 100644
--- a/third_party/blink/web_tests/external/wpt/encrypted-media/scripts/invalid-license.js
+++ b/third_party/blink/web_tests/external/wpt/encrypted-media/scripts/invalid-license.js
@@ -7,7 +7,7 @@
         var invalidLicense = new Uint8Array([0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77]);
         var messageEventFired = false;
 
-        return navigator.requestMediaKeySystemAccess(keySystem, getSimpleConfiguration()).then(function (access) {
+        var p = navigator.requestMediaKeySystemAccess(keySystem, getSimpleConfiguration()).then(function (access) {
             initDataType = access.getConfiguration().initDataTypes[0];
             initData = getInitData(initDataType);
             return access.createMediaKeys();
@@ -20,14 +20,19 @@
         }).then(function (messageEvent) {
             messageEventFired = true;
             return messageEvent.target.update(invalidLicense);
-        }).then(function () {
-            assert_unreached('Error: update() should fail because of an invalid license.');
         }).catch(function (error) {
-            if(messageEventFired) {
-                assert_equals(error.name, 'TypeError');
-            } else {
-                assert_unreached('Error: ' + error.name);
+            // Ensure we reached the update() call we are trying to test.
+            if (!messageEventFired) {
+                assert_unreached(
+                    `Failed to reach the update() call.  Error: '${error.name}' '${error.message}'`);
             }
+
+            // Propagate the error on through.
+            throw error;
         });
+
+        return promise_rejects_js(
+            test, TypeError, p,
+            'update() should fail because of an invalid license.');
     }, 'Update with invalid Clear Key license');
-}
\ No newline at end of file
+}
diff --git a/third_party/blink/web_tests/external/wpt/encrypted-media/scripts/requestmediakeysystemaccess.js b/third_party/blink/web_tests/external/wpt/encrypted-media/scripts/requestmediakeysystemaccess.js
index edfcbfc6..a60d4a1 100644
--- a/third_party/blink/web_tests/external/wpt/encrypted-media/scripts/requestmediakeysystemaccess.js
+++ b/third_party/blink/web_tests/external/wpt/encrypted-media/scripts/requestmediakeysystemaccess.js
@@ -11,11 +11,17 @@
             modifiedtestname = testname.replace( '%audiocontenttype', audiocontenttypes ).replace( '%videocontenttype', videocontenttypes );
 
         promise_test(function(test) {
-            return navigator.requestMediaKeySystemAccess(keySystem, configurations).then(function(a) {
-                assert_unreached('Unexpected requestMediaKeySystemAccess() success.');
-            }, function(e) {
-                assert_equals(e.name, expectedError);
-            });
+            var p = navigator.requestMediaKeySystemAccess(keySystem, configurations);
+            // expectedError is a string name for the error.  We can differentiate
+            // JS Errors from DOMExceptions by checking whether
+            // window[expectedError] exists.  If it does, expectedError is the name
+            // of a JS Error subclass and window[expectedError] is the constructor
+            // for that subclass.  Otherwise it's a name for a DOMException.
+            if (window[expectedError]) {
+                return promise_rejects_js(test, window[expectedError], p);
+            } else {
+                return promise_rejects_dom(test, expectedError, p);
+            }
         }, prefix + modifiedtestname + ' should result in ' + expectedError );
     }
 
diff --git a/third_party/blink/web_tests/external/wpt/encrypted-media/scripts/setmediakeys.js b/third_party/blink/web_tests/external/wpt/encrypted-media/scripts/setmediakeys.js
index f161e671..a85adea 100644
--- a/third_party/blink/web_tests/external/wpt/encrypted-media/scripts/setmediakeys.js
+++ b/third_party/blink/web_tests/external/wpt/encrypted-media/scripts/setmediakeys.js
@@ -30,7 +30,8 @@
             assert_unreached('setMediaKeys should fail when setting to wrong kind of object (Date)');
         }, function(error) {
             // The error should be TypeError.
-            assert_equals(error.name, 'TypeError', 'setMediaKeys should return a TypeError when setting to wrong kind of object (Date)');
+            assert_throws_js(TypeError, () => { throw error; },
+                             'setMediaKeys should return a TypeError when setting to wrong kind of object (Date)');
             return navigator.requestMediaKeySystemAccess(config.keysystem, [configuration]);
         }).then(function(access) {
             assert_equals(access.keySystem, config.keysystem)
diff --git a/third_party/blink/web_tests/external/wpt/encrypted-media/scripts/syntax-mediakeysession.js b/third_party/blink/web_tests/external/wpt/encrypted-media/scripts/syntax-mediakeysession.js
index 3663a7b..fac31cb 100644
--- a/third_party/blink/web_tests/external/wpt/encrypted-media/scripts/syntax-mediakeysession.js
+++ b/third_party/blink/web_tests/external/wpt/encrypted-media/scripts/syntax-mediakeysession.js
@@ -57,7 +57,14 @@
             func: function (mk6, type) {
                 return mk6.createSession().generateRequest(type, new Uint8Array(0));
             }
-        }
+        },
+        // Using an empty type should return a 'TypeError'.
+        {
+            exception: 'TypeError',
+            func: function (mk7, type) {
+                return mk7.createSession().generateRequest('', initData);
+            }
+        },
     ];
     function generateRequestTestExceptions(){
         return new Promise(function(resolve, reject){
diff --git a/third_party/blink/web_tests/external/wpt/encrypted-media/scripts/temporary-license-type.js b/third_party/blink/web_tests/external/wpt/encrypted-media/scripts/temporary-license-type.js
index cb0b0e6..44c9f1580 100644
--- a/third_party/blink/web_tests/external/wpt/encrypted-media/scripts/temporary-license-type.js
+++ b/third_party/blink/web_tests/external/wpt/encrypted-media/scripts/temporary-license-type.js
@@ -37,7 +37,7 @@
                         return;
                     }
 
-                    assert_equals(error.name, 'TypeError' );
+                    assert_throws_js(TypeError, () => { throw error; });
                     test.done();
                 } ) );
             }).catch(onFailure);
diff --git a/third_party/blink/web_tests/external/wpt/encrypted-media/scripts/update-disallowed-input.js b/third_party/blink/web_tests/external/wpt/encrypted-media/scripts/update-disallowed-input.js
index b5adaf7f1..2a30ad3 100644
--- a/third_party/blink/web_tests/external/wpt/encrypted-media/scripts/update-disallowed-input.js
+++ b/third_party/blink/web_tests/external/wpt/encrypted-media/scripts/update-disallowed-input.js
@@ -21,7 +21,7 @@
             return jwkSet + ',"test":"unknown"'.repeat(4000) + '}';
         }
 
-        return navigator.requestMediaKeySystemAccess(keySystem, getSimpleConfiguration()).then(function(access) {
+        var p = navigator.requestMediaKeySystemAccess(keySystem, getSimpleConfiguration()).then(function(access) {
             initDataType = access.getConfiguration().initDataTypes[0];
             initData = getInitData(initDataType);
             return access.createMediaKeys();
@@ -36,10 +36,8 @@
             assert_greater_than(jwkSet.length, 65536);
             var jwkSetArray = stringToUint8Array(jwkSet);
             return mediaKeySession.update(jwkSetArray);
-        }).then(function () {
-            assert_unreached('update() with a response longer than 64Kb succeed');
-        }).catch(function (error) {
-            assert_equals(error.name, 'TypeError');
         });
+
+        return promise_rejects_js(test, TypeError, p);
     }, 'update() with invalid response (longer than 64Kb characters) should fail.');
 }
diff --git a/third_party/blink/web_tests/external/wpt/encrypted-media/util/utils.js b/third_party/blink/web_tests/external/wpt/encrypted-media/util/utils.js
index 41bd71f..79f8c7e 100644
--- a/third_party/blink/web_tests/external/wpt/encrypted-media/util/utils.js
+++ b/third_party/blink/web_tests/external/wpt/encrypted-media/util/utils.js
@@ -240,25 +240,31 @@
     var exception = testCase.exception;
     var args = Array.prototype.slice.call(arguments, 1);
 
-    // Currently blink throws for TypeErrors rather than returning
-    // a rejected promise (http://crbug.com/359386).
-    // FIXME: Remove try/catch once they become failed promises.
-    try {
-        return func.apply(null, args).then(
-            function (result) {
-                assert_unreached(format_value(func));
-            },
-            function (error) {
-                assert_equals(error.name, exception, format_value(func));
-                assert_not_equals(error.message, "", format_value(func));
+    // This should really be rewritten in terms of the promise_rejects_*
+    // testharness utility functions, but that needs the async test involved
+    // passed in, and we don't have that here.
+    return func.apply(null, args).then(
+        function (result) {
+            assert_unreached(format_value(func));
+        },
+        function (error) {
+            assert_not_equals(error.message, "", format_value(func));
+            // `exception` is a string name for the error.  We can differentiate
+            // JS Errors from DOMExceptions by checking whether
+            // window[exception] exists.  If it does, expectedError is the name
+            // of a JS Error subclass and window[exception] is the constructor
+            // for that subclass.  Otherwise it's a name for a DOMException.
+            if (window[exception]) {
+                assert_throws_js(window[exception],
+                                 () => { throw error; },
+                                 format_value(func));
+            } else {
+                assert_throws_dom(exception,
+                                  () => { throw error; },
+                                  format_value(func));
             }
-        );
-    } catch (e) {
-        // Only allow 'TypeError' exceptions to be thrown.
-        // Everything else should be a failed promise.
-        assert_equals('TypeError', exception, format_value(func));
-        assert_equals(e.name, exception, format_value(func));
-    }
+        }
+    );
 }
 
 // Check that the events sequence (array of strings) matches the pattern (array of either strings, or
diff --git a/third_party/blink/web_tests/external/wpt/fetch/sec-metadata/portal.tentative.https.sub.html b/third_party/blink/web_tests/external/wpt/fetch/sec-metadata/portal.tentative.https.sub.html
new file mode 100644
index 0000000..2a9df1f
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/fetch/sec-metadata/portal.tentative.https.sub.html
@@ -0,0 +1,48 @@
+<!DOCTYPE html>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src=/resources/testdriver.js></script>
+<script src=/resources/testdriver-vendor.js></script>
+<script src=/fetch/sec-metadata/resources/helper.js></script>
+<script src=/common/utils.js></script>
+<body>
+<script>
+  const USER = true;
+  const FORCED = false;
+
+  function create_test(host, expectations) {
+    async_test(t => {
+      let p = document.createElement('portal');
+      p.addEventListener('message', t.step_func(e => {
+        assert_header_equals(e.data, expectations);
+        t.done();
+      }));
+
+      let url = `https://${host}/fetch/sec-metadata/resources/post-to-owner.py`;
+      p.src = url;
+      document.body.appendChild(p);
+    }, `{{host}} -> ${host} portal`);
+  }
+
+  create_test("{{host}}:{{ports[https][0]}}", {
+    // TODO(mkwst): 'document' seems right, I guess? Perhaps a portal-specific destination would be better?
+    "dest": "document",
+    "site": "same-origin",
+    "user": "",
+    "mode": "nested-navigate"
+  });
+
+  create_test("{{hosts[][www]}}:{{ports[https][0]}}", {
+    "dest": "document",
+    "site": "same-site",
+    "user": "",
+    "mode": "nested-navigate"
+  });
+
+  create_test("{{hosts[alt][www]}}:{{ports[https][0]}}", {
+    "dest": "document",
+    "site": "cross-site",
+    "user": "",
+    "mode": "nested-navigate"
+  });
+</script>
diff --git a/third_party/blink/web_tests/external/wpt/fetch/sec-metadata/resources/post-to-owner.py b/third_party/blink/web_tests/external/wpt/fetch/sec-metadata/resources/post-to-owner.py
index c5ded49d..096f33b 100644
--- a/third_party/blink/web_tests/external/wpt/fetch/sec-metadata/resources/post-to-owner.py
+++ b/third_party/blink/web_tests/external/wpt/fetch/sec-metadata/resources/post-to-owner.py
@@ -14,6 +14,8 @@
                 window.opener.postMessage(data, "*");
             if (window.top != window)
                 window.top.postMessage(data, "*");
+            if (window.portalHost)
+                window.portalHost.postMessage(data, "*");
         </script>
     """ % json.dumps({
         "dest": request.headers.get("sec-fetch-dest", ""),
diff --git a/third_party/blink/web_tests/external/wpt/html/dom/idlharness.worker-expected.txt b/third_party/blink/web_tests/external/wpt/html/dom/idlharness.worker-expected.txt
index 7354ff6..33d339f 100644
--- a/third_party/blink/web_tests/external/wpt/html/dom/idlharness.worker-expected.txt
+++ b/third_party/blink/web_tests/external/wpt/html/dom/idlharness.worker-expected.txt
@@ -1,5 +1,5 @@
 This is a testharness.js-based test.
-Found 673 tests; 647 PASS, 26 FAIL, 0 TIMEOUT, 0 NOTRUN.
+Found 673 tests; 653 PASS, 20 FAIL, 0 TIMEOUT, 0 NOTRUN.
 PASS idl_test setup
 PASS Partial interface Document: original interface defined
 PASS Partial interface mixin DocumentOrShadowRoot: original interface mixin defined
@@ -517,8 +517,8 @@
 PASS DedicatedWorkerGlobalScope interface: operation close()
 PASS DedicatedWorkerGlobalScope interface: attribute onmessage
 PASS DedicatedWorkerGlobalScope interface: attribute onmessageerror
-FAIL DedicatedWorkerGlobalScope interface: operation requestAnimationFrame(FrameRequestCallback) assert_own_property: global object missing non-static operation expected property "requestAnimationFrame" missing
-FAIL DedicatedWorkerGlobalScope interface: operation cancelAnimationFrame(unsigned long) assert_own_property: global object missing non-static operation expected property "cancelAnimationFrame" missing
+PASS DedicatedWorkerGlobalScope interface: operation requestAnimationFrame(FrameRequestCallback)
+PASS DedicatedWorkerGlobalScope interface: operation cancelAnimationFrame(unsigned long)
 PASS DedicatedWorkerGlobalScope interface: internal [[SetPrototypeOf]] method of global platform object - setting to a new value via Object.setPrototypeOf should throw a TypeError
 PASS DedicatedWorkerGlobalScope interface: internal [[SetPrototypeOf]] method of global platform object - setting to a new value via __proto__ should throw a TypeError
 PASS DedicatedWorkerGlobalScope interface: internal [[SetPrototypeOf]] method of global platform object - setting to a new value via Reflect.setPrototypeOf should return false
@@ -535,10 +535,10 @@
 PASS DedicatedWorkerGlobalScope interface: self must inherit property "close()" with the proper type
 PASS DedicatedWorkerGlobalScope interface: self must inherit property "onmessage" with the proper type
 PASS DedicatedWorkerGlobalScope interface: self must inherit property "onmessageerror" with the proper type
-FAIL DedicatedWorkerGlobalScope interface: self must inherit property "requestAnimationFrame(FrameRequestCallback)" with the proper type assert_own_property: expected property "requestAnimationFrame" missing
-FAIL DedicatedWorkerGlobalScope interface: calling requestAnimationFrame(FrameRequestCallback) on self with too few arguments must throw TypeError assert_own_property: expected property "requestAnimationFrame" missing
-FAIL DedicatedWorkerGlobalScope interface: self must inherit property "cancelAnimationFrame(unsigned long)" with the proper type assert_own_property: expected property "cancelAnimationFrame" missing
-FAIL DedicatedWorkerGlobalScope interface: calling cancelAnimationFrame(unsigned long) on self with too few arguments must throw TypeError assert_own_property: expected property "cancelAnimationFrame" missing
+PASS DedicatedWorkerGlobalScope interface: self must inherit property "requestAnimationFrame(FrameRequestCallback)" with the proper type
+PASS DedicatedWorkerGlobalScope interface: calling requestAnimationFrame(FrameRequestCallback) on self with too few arguments must throw TypeError
+PASS DedicatedWorkerGlobalScope interface: self must inherit property "cancelAnimationFrame(unsigned long)" with the proper type
+PASS DedicatedWorkerGlobalScope interface: calling cancelAnimationFrame(unsigned long) on self with too few arguments must throw TypeError
 PASS WorkerGlobalScope interface: self must inherit property "self" with the proper type
 PASS WorkerGlobalScope interface: self must inherit property "location" with the proper type
 PASS WorkerGlobalScope interface: self must inherit property "navigator" with the proper type
diff --git a/third_party/blink/web_tests/external/wpt/html/rendering/replaced-elements/attributes-for-embedded-content-and-images/canvas-aspect-ratio.html b/third_party/blink/web_tests/external/wpt/html/rendering/replaced-elements/attributes-for-embedded-content-and-images/canvas-aspect-ratio.html
new file mode 100644
index 0000000..1d231d5
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/html/rendering/replaced-elements/attributes-for-embedded-content-and-images/canvas-aspect-ratio.html
@@ -0,0 +1,30 @@
+<!doctype html>
+<title>Canvas width and height attributes are used to infer aspect-ratio</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<style>
+  canvas {
+    width: 100%;
+    max-width: 100px;
+    height: auto;
+  }
+</style>
+<body>
+<script>
+let t = async_test("Canvas width and height attributes are used to infer aspect-ratio");
+function assert_ratio(img, expected) {
+  let epsilon = 0.001;
+  assert_approx_equals(parseInt(getComputedStyle(img).width, 10) / parseInt(getComputedStyle(img).height, 10), expected, epsilon);
+}
+// Create and append a new canvas and immediately check the ratio.
+t.step(function() {
+  var canvas = document.createElement("canvas");
+  canvas.setAttribute("width", "250");
+  canvas.setAttribute("height", "100");
+  document.body.appendChild(canvas);
+  // Canvases always use the aspect ratio from their surface size.
+  assert_ratio(canvas, 2.5);
+
+  t.done();
+});
+</script>
diff --git a/third_party/blink/web_tests/external/wpt/html/rendering/replaced-elements/attributes-for-embedded-content-and-images/img-aspect-ratio.tentative-expected.txt b/third_party/blink/web_tests/external/wpt/html/rendering/replaced-elements/attributes-for-embedded-content-and-images/img-aspect-ratio.tentative-expected.txt
deleted file mode 100644
index 0c42ffc..0000000
--- a/third_party/blink/web_tests/external/wpt/html/rendering/replaced-elements/attributes-for-embedded-content-and-images/img-aspect-ratio.tentative-expected.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-This is a testharness.js-based test.
-FAIL Image width and height attributes are used to infer aspect-ratio assert_approx_equals: expected 0.8 +/- 0.001 but got 1
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/external/wpt/html/rendering/replaced-elements/attributes-for-embedded-content-and-images/img-aspect-ratio.tentative.html b/third_party/blink/web_tests/external/wpt/html/rendering/replaced-elements/attributes-for-embedded-content-and-images/img-aspect-ratio.tentative.html
index ae26688..c9ce868 100644
--- a/third_party/blink/web_tests/external/wpt/html/rendering/replaced-elements/attributes-for-embedded-content-and-images/img-aspect-ratio.tentative.html
+++ b/third_party/blink/web_tests/external/wpt/html/rendering/replaced-elements/attributes-for-embedded-content-and-images/img-aspect-ratio.tentative.html
@@ -9,7 +9,6 @@
     height: auto;
   }
 </style>
-<img src=broken width=100 height=125>
 <img src="/images/green.png">
 <img src="/images/green.png" width=100 height=125>
 <script>
@@ -18,10 +17,37 @@
   let epsilon = 0.001;
   assert_approx_equals(parseInt(getComputedStyle(img).width, 10) / parseInt(getComputedStyle(img).height, 10), expected, epsilon);
 }
+// Create and append a new image and immediately check the ratio.
+// This is not racy because the spec requires the user agent to queue a task:
+// https://html.spec.whatwg.org/multipage/images.html#updating-the-image-data
+t.step(function() {
+  var img = new Image();
+  img.width = 250;
+  img.height = 100;
+  img.src = "/images/blue.png";
+  document.body.appendChild(img);
+  assert_ratio(img, 2.5);
+
+  img = new Image();
+  img.setAttribute("width", "0.8");
+  img.setAttribute("height", "0.2");
+  img.src = "/images/blue.png";
+  document.body.appendChild(img);
+  assert_ratio(img, 4);
+
+  img = new Image();
+  img.setAttribute("width", "50%");
+  img.setAttribute("height", "25%");
+  img.src = "/images/blue.png";
+  document.body.appendChild(img);
+  // Percentages should be  ignored.
+  assert_equals(getComputedStyle(img).height, "0px");
+});
+
 onload = t.step_func_done(function() {
   let images = document.querySelectorAll("img");
-  assert_ratio(images[0], 0.8);
-  assert_ratio(images[2], 2.0); // 2.0 is the original aspect ratio of green.png
-  assert_ratio(images[1], 2.0); // Loaded image's aspect ratio, at least by default, overrides width / height ratio.
+  assert_ratio(images[2], 1.266); // 1.266 is the original aspect ratio of blue.png
+  assert_ratio(images[1], 2.0); // 2.0 is the original aspect ratio of green.png
+  assert_ratio(images[0], 2.0); // Loaded image's aspect ratio, at least by default, overrides width / height ratio.
 });
 </script>
diff --git a/third_party/blink/web_tests/external/wpt/html/rendering/replaced-elements/attributes-for-embedded-content-and-images/video-aspect-ratio.html b/third_party/blink/web_tests/external/wpt/html/rendering/replaced-elements/attributes-for-embedded-content-and-images/video-aspect-ratio.html
new file mode 100644
index 0000000..2b49370
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/html/rendering/replaced-elements/attributes-for-embedded-content-and-images/video-aspect-ratio.html
@@ -0,0 +1,39 @@
+<!doctype html>
+<title>Video width and height attributes are not used to infer aspect-ratio</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/common/media.js"></script>
+<style>
+  video {
+    width: 100%;
+    max-width: 100px;
+    height: auto;
+  }
+</style>
+<body>
+<script>
+let t = async_test("Video width and height attributes are not used to infer aspect-ratio");
+function assert_ratio(img, expected) {
+  let epsilon = 0.001;
+  assert_approx_equals(parseInt(getComputedStyle(img).width, 10) / parseInt(getComputedStyle(img).height, 10), expected, epsilon);
+}
+// Create and append a new video and immediately check the ratio.
+// This is not racy because the spec requires the user agent to queue a task:
+// https://html.spec.whatwg.org/multipage/media.html#concept-media-load-algorithm
+t.step(function() {
+  var video = document.createElement("video");
+  video.setAttribute("width", "250");
+  video.setAttribute("height", "100");
+  video.src = getVideoURI('/media/2x2-green');
+  document.body.appendChild(video);
+  // Videos default to a size of 300x150px and calculate their aspect ratio
+  // based on that before the video is loaded. So this should be 2, ignoring
+  // the 2.5 that it would be based on the attributes.
+  assert_ratio(video, 2);
+
+  video.onloadeddata = t.step_func_done(function() {
+    // When loaded this video is square.
+    assert_ratio(video, 1);
+  });
+});
+</script>
diff --git a/third_party/blink/web_tests/external/wpt/interfaces/css-masking.idl b/third_party/blink/web_tests/external/wpt/interfaces/css-masking.idl
index 80f908d..6db59ed 100644
--- a/third_party/blink/web_tests/external/wpt/interfaces/css-masking.idl
+++ b/third_party/blink/web_tests/external/wpt/interfaces/css-masking.idl
@@ -3,11 +3,13 @@
 // (https://github.com/tidoust/reffy-reports)
 // Source: CSS Masking Module Level 1 (https://drafts.fxtf.org/css-masking-1/)
 
+[Exposed=Window]
 interface SVGClipPathElement : SVGElement {
   readonly attribute SVGAnimatedEnumeration clipPathUnits;
   readonly attribute SVGAnimatedTransformList transform;
 };
 
+[Exposed=Window]
 interface SVGMaskElement : SVGElement {
   readonly attribute SVGAnimatedEnumeration maskUnits;
   readonly attribute SVGAnimatedEnumeration maskContentUnits;
diff --git a/third_party/blink/web_tests/external/wpt/interfaces/generic-sensor.idl b/third_party/blink/web_tests/external/wpt/interfaces/generic-sensor.idl
index 2921c50..f3eeb9b 100644
--- a/third_party/blink/web_tests/external/wpt/interfaces/generic-sensor.idl
+++ b/third_party/blink/web_tests/external/wpt/interfaces/generic-sensor.idl
@@ -19,9 +19,9 @@
   double frequency;
 };
 
-[Constructor(DOMString type, SensorErrorEventInit errorEventInitDict),
- SecureContext, Exposed=(DedicatedWorker, Window)]
+[SecureContext, Exposed=(DedicatedWorker, Window)]
 interface SensorErrorEvent : Event {
+  constructor(DOMString type, SensorErrorEventInit errorEventInitDict);
   readonly attribute DOMException error;
 };
 
diff --git a/third_party/blink/web_tests/external/wpt/interfaces/orientation-sensor.idl b/third_party/blink/web_tests/external/wpt/interfaces/orientation-sensor.idl
index 9ee0998..2ea3d38 100644
--- a/third_party/blink/web_tests/external/wpt/interfaces/orientation-sensor.idl
+++ b/third_party/blink/web_tests/external/wpt/interfaces/orientation-sensor.idl
@@ -17,12 +17,14 @@
   OrientationSensorLocalCoordinateSystem referenceFrame = "device";
 };
 
-[Constructor(optional OrientationSensorOptions sensorOptions = {}), SecureContext, Exposed=Window]
+[SecureContext, Exposed=Window]
 interface AbsoluteOrientationSensor : OrientationSensor {
+  constructor(optional OrientationSensorOptions sensorOptions = {});
 };
 
-[Constructor(optional OrientationSensorOptions sensorOptions = {}), SecureContext, Exposed=Window]
+[SecureContext, Exposed=Window]
 interface RelativeOrientationSensor : OrientationSensor {
+  constructor(optional OrientationSensorOptions sensorOptions = {});
 };
 
 dictionary AbsoluteOrientationReadingValues {
diff --git a/third_party/blink/web_tests/external/wpt/media-capabilities/decodingInfo.any.js b/third_party/blink/web_tests/external/wpt/media-capabilities/decodingInfo.any.js
index 737ded1..edd58ac 100644
--- a/third_party/blink/web_tests/external/wpt/media-capabilities/decodingInfo.any.js
+++ b/third_party/blink/web_tests/external/wpt/media-capabilities/decodingInfo.any.js
@@ -17,28 +17,28 @@
 };
 
 promise_test(t => {
-  return promise_rejects(t, new TypeError(), navigator.mediaCapabilities.decodingInfo());
+  return promise_rejects_js(t, TypeError, navigator.mediaCapabilities.decodingInfo());
 }, "Test that decodingInfo rejects if it doesn't get a configuration");
 
 promise_test(t => {
-  return promise_rejects(t, new TypeError(), navigator.mediaCapabilities.decodingInfo({}));
+  return promise_rejects_js(t, TypeError, navigator.mediaCapabilities.decodingInfo({}));
 }, "Test that decodingInfo rejects if the MediaConfiguration isn't valid");
 
 promise_test(t => {
-  return promise_rejects(t, new TypeError(), navigator.mediaCapabilities.decodingInfo({
+  return promise_rejects_js(t, TypeError, navigator.mediaCapabilities.decodingInfo({
     video: minimalVideoConfiguration,
     audio: minimalAudioConfiguration,
   }));
 }, "Test that decodingInfo rejects if the MediaConfiguration does not have a type");
 
 promise_test(t => {
-  return promise_rejects(t, new TypeError(), navigator.mediaCapabilities.decodingInfo({
+  return promise_rejects_js(t, TypeError, navigator.mediaCapabilities.decodingInfo({
     type: 'file',
   }));
 }, "Test that decodingInfo rejects if the configuration doesn't have an audio or video field");
 
 promise_test(t => {
-  return promise_rejects(t, new TypeError(), navigator.mediaCapabilities.decodingInfo({
+  return promise_rejects_js(t, TypeError, navigator.mediaCapabilities.decodingInfo({
     type: 'file',
     video: {
       contentType: 'video/webm; codecs="vp09.00.10.08"',
@@ -51,7 +51,7 @@
 }, "Test that decodingInfo rejects if the video configuration has a negative framerate");
 
 promise_test(t => {
-  return promise_rejects(t, new TypeError(), navigator.mediaCapabilities.decodingInfo({
+  return promise_rejects_js(t, TypeError, navigator.mediaCapabilities.decodingInfo({
     type: 'file',
     video: {
       contentType: 'video/webm; codecs="vp09.00.10.08"',
@@ -64,7 +64,7 @@
 }, "Test that decodingInfo rejects if the video configuration has a framerate set to 0");
 
 promise_test(t => {
-  return promise_rejects(t, new TypeError(), navigator.mediaCapabilities.decodingInfo({
+  return promise_rejects_js(t, TypeError, navigator.mediaCapabilities.decodingInfo({
     type: 'file',
     video: {
       contentType: 'video/webm; codecs="vp09.00.10.08"',
@@ -77,7 +77,7 @@
 }, "Test that decodingInfo rejects if the video configuration has a framerate set to Infinity");
 
 promise_test(t => {
-  return promise_rejects(t, new TypeError(), navigator.mediaCapabilities.decodingInfo({
+  return promise_rejects_js(t, TypeError, navigator.mediaCapabilities.decodingInfo({
     type: 'file',
     video: {
       contentType: 'fgeoa',
@@ -90,7 +90,7 @@
 }, "Test that decodingInfo rejects if the video configuration contentType doesn't parse");
 
 promise_test(t => {
-  return promise_rejects(t, new TypeError(), navigator.mediaCapabilities.decodingInfo({
+  return promise_rejects_js(t, TypeError, navigator.mediaCapabilities.decodingInfo({
     type: 'file',
     video: {
       contentType: 'audio/fgeoa',
@@ -103,7 +103,7 @@
 }, "Test that decodingInfo rejects if the video configuration contentType isn't of type video");
 
 promise_test(t => {
-  return promise_rejects(t, new TypeError(), navigator.mediaCapabilities.decodingInfo({
+  return promise_rejects_js(t, TypeError, navigator.mediaCapabilities.decodingInfo({
     type: 'file',
     video: {
       contentType: 'video/webm; codecs="vp09.00.10.08"; foo="bar"',
@@ -116,7 +116,7 @@
 }, "Test that decodingInfo rejects if the video configuration contentType has more than one parameter");
 
 promise_test(t => {
-  return promise_rejects(t, new TypeError(), navigator.mediaCapabilities.decodingInfo({
+  return promise_rejects_js(t, TypeError, navigator.mediaCapabilities.decodingInfo({
     type: 'file',
     video: {
       contentType: 'video/webm; foo="bar"',
@@ -142,7 +142,7 @@
 }, "Test that decodingInfo() accepts framerate in the form of x/y");
 
 promise_test(t => {
-  return promise_rejects(t, new TypeError(), navigator.mediaCapabilities.decodingInfo({
+  return promise_rejects_js(t, TypeError, navigator.mediaCapabilities.decodingInfo({
     type: 'file',
     video: {
       contentType: 'video/webm; codecs="vp09.00.10.08"',
@@ -155,7 +155,7 @@
 }, "Test that decodingInfo() rejects framerate in the form of x/0");
 
 promise_test(t => {
-  return promise_rejects(t, new TypeError(), navigator.mediaCapabilities.decodingInfo({
+  return promise_rejects_js(t, TypeError, navigator.mediaCapabilities.decodingInfo({
     type: 'file',
     video: {
       contentType: 'video/webm; codecs="vp09.00.10.08"',
@@ -168,7 +168,7 @@
 }, "Test that decodingInfo() rejects framerate in the form of 0/y");
 
 promise_test(t => {
-  return promise_rejects(t, new TypeError(), navigator.mediaCapabilities.decodingInfo({
+  return promise_rejects_js(t, TypeError, navigator.mediaCapabilities.decodingInfo({
     type: 'file',
     video: {
       contentType: 'video/webm; codecs="vp09.00.10.08"',
@@ -181,7 +181,7 @@
 }, "Test that decodingInfo() rejects framerate in the form of -x/y");
 
 promise_test(t => {
-  return promise_rejects(t, new TypeError(), navigator.mediaCapabilities.decodingInfo({
+  return promise_rejects_js(t, TypeError, navigator.mediaCapabilities.decodingInfo({
     type: 'file',
     video: {
       contentType: 'video/webm; codecs="vp09.00.10.08"',
@@ -194,7 +194,7 @@
 }, "Test that decodingInfo() rejects framerate in the form of x/-y");
 
 promise_test(t => {
-  return promise_rejects(t, new TypeError(), navigator.mediaCapabilities.decodingInfo({
+  return promise_rejects_js(t, TypeError, navigator.mediaCapabilities.decodingInfo({
     type: 'file',
     video: {
       contentType: 'video/webm; codecs="vp09.00.10.08"',
@@ -233,7 +233,7 @@
 }, "Test that decodingInfo() accepts framerate as fraction with decimals");
 
 promise_test(t => {
-  return promise_rejects(t, new TypeError(), navigator.mediaCapabilities.decodingInfo({
+  return promise_rejects_js(t, TypeError, navigator.mediaCapabilities.decodingInfo({
     type: 'file',
     video: {
       contentType: 'video/webm; codecs="vp09.00.10.08"',
@@ -246,28 +246,28 @@
 }, "Test that decodingInfo() rejects framerate with trailing unallowed characters");
 
 promise_test(t => {
-  return promise_rejects(t, new TypeError(), navigator.mediaCapabilities.decodingInfo({
+  return promise_rejects_js(t, TypeError, navigator.mediaCapabilities.decodingInfo({
     type: 'file',
     audio: { contentType: 'fgeoa' },
   }));
 }, "Test that decodingInfo rejects if the audio configuration contenType doesn't parse");
 
 promise_test(t => {
-  return promise_rejects(t, new TypeError(), navigator.mediaCapabilities.decodingInfo({
+  return promise_rejects_js(t, TypeError, navigator.mediaCapabilities.decodingInfo({
     type: 'file',
     audio: { contentType: 'video/fgeoa' },
   }));
 }, "Test that decodingInfo rejects if the audio configuration contentType isn't of type audio");
 
 promise_test(t => {
-  return promise_rejects(t, new TypeError(), navigator.mediaCapabilities.decodingInfo({
+  return promise_rejects_js(t, TypeError, navigator.mediaCapabilities.decodingInfo({
     type: 'file',
     audio: { contentType: 'audio/webm; codecs="opus"; foo="bar"' },
   }));
 }, "Test that decodingInfo rejects if the audio configuration contentType has more than one parameters");
 
 promise_test(t => {
-  return promise_rejects(t, new TypeError(), navigator.mediaCapabilities.decodingInfo({
+  return promise_rejects_js(t, TypeError, navigator.mediaCapabilities.decodingInfo({
     type: 'file',
     audio: { contentType: 'audio/webm; foo="bar"' },
   }));
diff --git a/third_party/blink/web_tests/external/wpt/storage/opaque-origin.https.window.js b/third_party/blink/web_tests/external/wpt/storage/opaque-origin.https.window.js
index 3e101dde..cc1d31f 100644
--- a/third_party/blink/web_tests/external/wpt/storage/opaque-origin.https.window.js
+++ b/third_party/blink/web_tests/external/wpt/storage/opaque-origin.https.window.js
@@ -15,7 +15,7 @@
 function wait_for_message(iframe) {
   return new Promise(resolve => {
     self.addEventListener('message', function listener(e) {
-      if (e.source === iframe.contentWindow) {
+      if (e.source === iframe.contentWindow && "result" in e.data) {
         resolve(e.data);
         self.removeEventListener('message', listener);
       }
@@ -24,7 +24,8 @@
 }
 
 function make_script(snippet) {
-  return '<script>' +
+  return '<script src="/resources/testharness.js"></script>' +
+         '<script>' +
          '  window.onmessage = () => {' +
          '    try {' +
          '      (' + snippet + ')' +
@@ -33,19 +34,25 @@
          '            window.parent.postMessage({result: "no rejection"}, "*");' +
          '          }, ' +
          '          error => {' +
-         '            window.parent.postMessage({result: error.name}, "*");' +
+         '            try {' +
+         '              assert_throws_js(TypeError, () => { throw error; });' +
+         '              window.parent.postMessage({result: "correct rejection"}, "*");' +
+         '            } catch (e) {' +
+         '              window.parent.postMessage({result: "incorrect rejection"}, "*");' +
+         '            }' +
          '          });' +
          '    } catch (ex) {' +
          // Report if not implemented/exposed, rather than time out.
-         '      window.parent.postMessage({result: ex.message}, "*");' +
+         '      window.parent.postMessage({result: "API access threw"}, "*");' +
          '    }' +
          '  };' +
          '<\/script>';
 }
 
-['navigator.storage.persist()',
- 'navigator.storage.persisted()',
- 'navigator.storage.estimate()'
+['navigator.storage.persisted()',
+ 'navigator.storage.estimate()',
+ // persist() can prompt, so make sure we test that last
+ 'navigator.storage.persist()',
 ].forEach(snippet => {
   promise_test(t => {
     return load_iframe(make_script(snippet))
@@ -66,7 +73,7 @@
         return wait_for_message(iframe);
       })
       .then(message => {
-        assert_equals(message.result, 'TypeError',
+        assert_equals(message.result, 'correct rejection',
                       `${snippet} should reject with TypeError`);
       });
   }, `${snippet} in sandboxed iframe should reject with TypeError`);
diff --git a/third_party/blink/web_tests/external/wpt/svg/animations/keysplines-x-limits.html b/third_party/blink/web_tests/external/wpt/svg/animations/keysplines-x-limits.html
new file mode 100644
index 0000000..3c9ba65
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/svg/animations/keysplines-x-limits.html
@@ -0,0 +1,42 @@
+<!DOCTYPE html>
+<title>'keySplines' with x-values outside of the 0 to 1 range</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<svg>
+  <rect x="10" width="10" height="10" fill="blue">
+    <animate attributeName="x" values="0; 250" dur="5s"/>
+  </rect>
+  <rect x="10" width="10" height="10" y="20" fill="blue">
+    <animate attributeName="x" values="0; 250" dur="5s"
+             keyTimes="0; 1" keySplines="-1 0 1 1" calcMode="spline"/>
+  </rect>
+  <rect x="10" width="10" height="10" y="30" fill="blue">
+    <animate attributeName="x" values="0; 250" dur="5s"
+             keyTimes="0; 1" keySplines="2 0 1 1" calcMode="spline"/>
+  </rect>
+  <rect x="10" width="10" height="10" y="10" fill="blue">
+    <animate attributeName="x" values="0; 250" dur="5s"
+             keyTimes="0; 1" keySplines="0 0 -1 1" calcMode="spline"/>
+  </rect>
+  <rect x="10" width="10" height="10" y="40" fill="blue">
+    <animate attributeName="x" values="0; 250" dur="5s"
+             keyTimes="0; 1" keySplines="0 0 2 1" calcMode="spline"/>
+  </rect>
+</svg>
+<script>
+  async_test(t => {
+    let svg = document.querySelector('svg');
+    svg.pauseAnimations();
+    svg.setCurrentTime(2.5);
+    window.onload = t.step_func(() => {
+      requestAnimationFrame(t.step_func_done(() => {
+        let rects = document.getElementsByTagName('rect');
+        assert_equals(rects[0].getBBox().x, 125, 'animations applied');
+        assert_equals(rects[1].getBBox().x, 10, 'first control point x less than zero');
+        assert_equals(rects[2].getBBox().x, 10, 'first control point x greater than one');
+        assert_equals(rects[3].getBBox().x, 10, 'second control point x less than zero');
+        assert_equals(rects[4].getBBox().x, 10, 'second control point x greater than one');
+      }));
+    });
+  });
+</script>
diff --git a/third_party/blink/web_tests/external/wpt/webaudio/resources/audit.js b/third_party/blink/web_tests/external/wpt/webaudio/resources/audit.js
index 1876c0fc..2aac1ff 100644
--- a/third_party/blink/web_tests/external/wpt/webaudio/resources/audit.js
+++ b/third_party/blink/web_tests/external/wpt/webaudio/resources/audit.js
@@ -1187,7 +1187,21 @@
           '> [' + this._label + '] ' +
           (this._description ? this._description : ''));
 
-      this._taskFunction(this, this.should.bind(this));
+      // Ideally we would just use testharness async_test instead of reinventing
+      // that wheel, but since it's been reinvented...  At least make sure that
+      // an exception while running a task doesn't preclude us running all the
+      // _other_ tasks for the test.
+      try {
+        this._taskFunction(this, this.should.bind(this));
+      } catch (e) {
+        // Log the failure.
+        test(() => { throw e; }, `Executing "${this.label}"`);
+        if (this.state != TaskState.FINISHED) {
+          // We threw before calling done(), so do that manually to run our
+          // other tasks.
+          this.done();
+        }
+      }
     }
 
     // Update the task success based on the individual assertion/test inside.
diff --git a/third_party/blink/web_tests/external/wpt/webaudio/the-audio-api/the-pannernode-interface/ctor-panner.html b/third_party/blink/web_tests/external/wpt/webaudio/the-audio-api/the-pannernode-interface/ctor-panner.html
index d330c9c..c434aa8c 100644
--- a/third_party/blink/web_tests/external/wpt/webaudio/the-audio-api/the-pannernode-interface/ctor-panner.html
+++ b/third_party/blink/web_tests/external/wpt/webaudio/the-audio-api/the-pannernode-interface/ctor-panner.html
@@ -108,6 +108,16 @@
             },
             'new PannerNode(c, ' + JSON.stringify(options) + ')')
             .throw(DOMException, 'NotSupportedError');
+        should(
+            () => {
+              node = new PannerNode(context);
+              node.channelCount = options.channelCount;
+            },
+            `node.channelCount = ${options.channelCount}`)
+            .throw(DOMException, "NotSupportedError");
+        should(node.channelCount,
+               `node.channelCount after setting to ${options.channelCount}`)
+            .beEqualTo(2);
 
         options = {channelCount: 3};
         should(
@@ -116,6 +126,16 @@
             },
             'new PannerNode(c, ' + JSON.stringify(options) + ')')
             .throw(DOMException, 'NotSupportedError');
+        should(
+            () => {
+              node = new PannerNode(context);
+              node.channelCount = options.channelCount;
+            },
+            `node.channelCount = ${options.channelCount}`)
+            .throw(DOMException, "NotSupportedError");
+        should(node.channelCount,
+               `node.channelCount after setting to ${options.channelCount}`)
+            .beEqualTo(2);
 
         options = {channelCount: 99};
         should(
@@ -124,6 +144,16 @@
             },
             'new PannerNode(c, ' + JSON.stringify(options) + ')')
             .throw(DOMException, 'NotSupportedError');
+        should(
+            () => {
+              node = new PannerNode(context);
+              node.channelCount = options.channelCount;
+            },
+            `node.channelCount = ${options.channelCount}`)
+            .throw(DOMException, "NotSupportedError");
+        should(node.channelCount,
+               `node.channelCount after setting to ${options.channelCount}`)
+            .beEqualTo(2);
 
         // Test channelCountMode.  A mode of "max" is illegal, but others are
         // ok.
@@ -154,6 +184,16 @@
             },
             'new PannerNode(c, ' + JSON.stringify(options) + ')')
             .throw(DOMException, 'NotSupportedError');
+        should(
+            () => {
+              node = new PannerNode(context);
+              node.channelCountMode = options.channelCountMode;
+            },
+            `node.channelCountMode = ${options.channelCountMode}`)
+            .throw(DOMException, "NotSupportedError");
+        should(node.channelCountMode,
+               `node.channelCountMode after setting to ${options.channelCountMode}`)
+            .beEqualTo("clamped-max");
 
         options = {channelCountMode: 'foobar'};
         should(
@@ -162,6 +202,16 @@
             },
             'new PannerNode(c, " + JSON.stringify(options) + ")')
             .throw(TypeError);
+        should(
+            () => {
+              node = new PannerNode(context);
+              node.channelCountMode = options.channelCountMode;
+            },
+            `node.channelCountMode = ${options.channelCountMode}`)
+            .notThrow(); // Invalid assignment to enum-valued attrs does not throw.
+        should(node.channelCountMode,
+               `node.channelCountMode after setting to ${options.channelCountMode}`)
+            .beEqualTo("clamped-max");
 
         // Test channelInterpretation.
         options = {channelInterpretation: 'speakers'};
@@ -200,6 +250,17 @@
             },
             'new PannerNode(c, ' + JSON.stringify(options) + ')')
             .throw(RangeError);
+        should(
+            () => {
+              node = new PannerNode(context);
+              node.maxDistance = options.maxDistance;
+            },
+            `node.maxDistance = ${options.maxDistance}`)
+            .throw(RangeError);
+        should(node.maxDistance,
+               `node.maxDistance after setting to ${options.maxDistance}`)
+            .beEqualTo(10000);
+
         options = {maxDistance: 100};
         should(
             () => {
@@ -218,6 +279,17 @@
             },
             'new PannerNode(c, ' + JSON.stringify(options) + ')')
             .throw(RangeError);
+        should(
+            () => {
+              node = new PannerNode(context);
+              node.rolloffFactor = options.rolloffFactor;
+            },
+            `node.rolloffFactor = ${options.rolloffFactor}`)
+            .throw(RangeError);
+        should(node.rolloffFactor,
+               `node.rolloffFactor after setting to ${options.rolloffFactor}`)
+            .beEqualTo(1);
+
         options = {rolloffFactor: 0};
         should(
             () => {
@@ -256,6 +328,17 @@
             },
             'new PannerNode(c, ' + JSON.stringify(options) + ')')
             .throw(DOMException, 'InvalidStateError');
+        should(
+            () => {
+              node = new PannerNode(context);
+              node.coneOuterGain = options.coneOuterGain;
+            },
+            `node.coneOuterGain = ${options.coneOuterGain}`)
+            .throw(DOMException, 'InvalidStateError');
+        should(node.coneOuterGain,
+               `node.coneOuterGain after setting to ${options.coneOuterGain}`)
+            .beEqualTo(0);
+
         options = {coneOuterGain: 1.1};
         should(
             () => {
@@ -263,6 +346,17 @@
             },
             'new PannerNode(c, ' + JSON.stringify(options) + ')')
             .throw(DOMException, 'InvalidStateError');
+        should(
+            () => {
+              node = new PannerNode(context);
+              node.coneOuterGain = options.coneOuterGain;
+            },
+            `node.coneOuterGain = ${options.coneOuterGain}`)
+            .throw(DOMException, 'InvalidStateError');
+        should(node.coneOuterGain,
+               `node.coneOuterGain after setting to ${options.coneOuterGain}`)
+            .beEqualTo(0);
+
         options = {coneOuterGain: 0.0};
         should(
             () => {
diff --git a/third_party/blink/web_tests/external/wpt/webxr/xrWebGLLayer_constructor.https-expected.txt b/third_party/blink/web_tests/external/wpt/webxr/xrWebGLLayer_constructor.https-expected.txt
deleted file mode 100644
index be0603a7..0000000
--- a/third_party/blink/web_tests/external/wpt/webxr/xrWebGLLayer_constructor.https-expected.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-This is a testharness.js-based test.
-FAIL Ensure that XRWebGLLayer's constructor throws appropriate errors promise_test: Unhandled rejection with value: object "TypeError: Cannot read property 'then' of undefined"
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/external/wpt/webxr/xrWebGLLayer_constructor.https.html b/third_party/blink/web_tests/external/wpt/webxr/xrWebGLLayer_constructor.https.html
index 7e57f42..0584da79 100644
--- a/third_party/blink/web_tests/external/wpt/webxr/xrWebGLLayer_constructor.https.html
+++ b/third_party/blink/web_tests/external/wpt/webxr/xrWebGLLayer_constructor.https.html
@@ -17,11 +17,8 @@
   let gl = webglCanvas.getContext('webgl', glAttributes);
   return navigator.xr.test.simulateDeviceConnection(TRACKED_IMMERSIVE_DEVICE)
     .then(() => {
-      let sessionPromise;
-      navigator.xr.test.simulateUserActivation(function() {
-        sessionPromise = navigator.xr.requestSession('inline');
-      });
-      return sessionPromise.then((session) => {
+      return navigator.xr.requestSession('inline')
+        .then((session) => {
           try {
             let webglLayerIncompatible = new XRWebGLLayer(session, gl);
           } catch (err) {
diff --git a/third_party/blink/web_tests/external/wpt/workers/semantics/interface-objects/003.any.js b/third_party/blink/web_tests/external/wpt/workers/semantics/interface-objects/003.any.js
new file mode 100644
index 0000000..1942a46
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/workers/semantics/interface-objects/003.any.js
@@ -0,0 +1,83 @@
+// META: global=!default,sharedworker
+
+var expected = [
+  // https://html.spec.whatwg.org/
+  "ApplicationCache",
+  "WorkerGlobalScope",
+  "SharedWorkerGlobalScope",
+  "Worker",
+  "SharedWorker",
+  "MessagePort",
+  "MessageEvent",
+  "WorkerNavigator",
+  "MessageChannel",
+  "WorkerLocation",
+  "ImageData",
+  "ImageBitmap",
+  "CanvasGradient",
+  "CanvasPattern",
+  "CanvasPath",
+  "Path2D",
+  "PromiseRejectionEvent",
+  "EventSource",
+  "WebSocket",
+  "CloseEvent",
+  "BroadcastChannel",
+  // https://tc39.github.io/ecma262/
+  "ArrayBuffer",
+  "Int8Array",
+  "Uint8Array",
+  "Uint8ClampedArray",
+  "Int16Array",
+  "Uint16Array",
+  "Int32Array",
+  "Uint32Array",
+  "Float32Array",
+  "Float64Array",
+  "DataView",
+  // https://xhr.spec.whatwg.org/
+  "XMLHttpRequestEventTarget",
+  "XMLHttpRequestUpload",
+  "XMLHttpRequest",
+  "ProgressEvent",
+  "FormData",
+  // https://url.spec.whatwg.org/
+  "URL",
+  "URLSearchParams",
+  // https://w3c.github.io/FileAPI/
+  "File",
+  "Blob",
+  "FileList",
+  "FileReader",
+  "FileReaderSync",
+  // https://dom.spec.whatwg.org/
+  "EventTarget",
+  "ErrorEvent",
+  "Event",
+  "CustomEvent",
+  // http://heycam.github.io/webidl/
+  "DOMException",
+  // https://streams.spec.whatwg.org/
+  "ReadableStream",
+  "WritableStream",
+  "ByteLengthQueuingStrategy",
+  "CountQueuingStrategy",
+  // http://w3c.github.io/IndexedDB/
+  "IDBRequest",
+  "IDBOpenDBRequest",
+  "IDBVersionChangeEvent",
+  "IDBFactory",
+  "IDBDatabase",
+  "IDBObjectStore",
+  "IDBIndex",
+  "IDBKeyRange",
+  "IDBCursor",
+  "IDBCursorWithValue",
+  "IDBTransaction",
+];
+
+for (var i = 0; i < unexpected.length; ++i) {
+  test(function() {
+    assert_true(unexpected[i] in self);
+  }, "The " + unexpected[i] + " interface object should not be exposed");
+}
diff --git a/third_party/blink/web_tests/external/wpt/workers/semantics/interface-objects/003.any.sharedworker-expected.txt b/third_party/blink/web_tests/external/wpt/workers/semantics/interface-objects/003.any.sharedworker-expected.txt
new file mode 100644
index 0000000..c20fb7c
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/workers/semantics/interface-objects/003.any.sharedworker-expected.txt
@@ -0,0 +1,4 @@
+This is a testharness.js-based test.
+FAIL 003 Uncaught ReferenceError: unexpected is not defined
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/external/wpt/workers/semantics/interface-objects/003.html b/third_party/blink/web_tests/external/wpt/workers/semantics/interface-objects/003.html
deleted file mode 100644
index 99e8b3a..0000000
--- a/third_party/blink/web_tests/external/wpt/workers/semantics/interface-objects/003.html
+++ /dev/null
@@ -1,19 +0,0 @@
-<!doctype html>
-<title>available interface objects in shared worker</title>
-<script src="/resources/testharness.js"></script>
-<script src="/resources/testharnessreport.js"></script>
-<div id=log></div>
-<script>
-setup(function() {
-  window.worker = new SharedWorker('003.js');
-  worker.port.onmessage = function(e) {
-    var result = e.data;
-    for (var i = 0; i < result.length; ++i) {
-      test(function() {
-        assert_true(result[i][1]);
-      }, "The " + result[i][0] + " interface object should be exposed");
-    }
-    done();
-  }
-}, {explicit_done: true});
-</script>
\ No newline at end of file
diff --git a/third_party/blink/web_tests/external/wpt/workers/semantics/interface-objects/003.js b/third_party/blink/web_tests/external/wpt/workers/semantics/interface-objects/003.js
deleted file mode 100644
index a91e7c3..0000000
--- a/third_party/blink/web_tests/external/wpt/workers/semantics/interface-objects/003.js
+++ /dev/null
@@ -1,82 +0,0 @@
-onconnect = function(e) {
-  var expected = [
-    // https://html.spec.whatwg.org/
-    "ApplicationCache",
-    "WorkerGlobalScope",
-    "SharedWorkerGlobalScope",
-    "Worker",
-    "SharedWorker",
-    "MessagePort",
-    "MessageEvent",
-    "WorkerNavigator",
-    "MessageChannel",
-    "WorkerLocation",
-    "ImageData",
-    "ImageBitmap",
-    "CanvasGradient",
-    "CanvasPattern",
-    "CanvasPath",
-    "Path2D",
-    "PromiseRejectionEvent",
-    "EventSource",
-    "WebSocket",
-    "CloseEvent",
-    "BroadcastChannel",
-    // https://tc39.github.io/ecma262/
-    "ArrayBuffer",
-    "Int8Array",
-    "Uint8Array",
-    "Uint8ClampedArray",
-    "Int16Array",
-    "Uint16Array",
-    "Int32Array",
-    "Uint32Array",
-    "Float32Array",
-    "Float64Array",
-    "DataView",
-    // https://xhr.spec.whatwg.org/
-    "XMLHttpRequestEventTarget",
-    "XMLHttpRequestUpload",
-    "XMLHttpRequest",
-    "ProgressEvent",
-    "FormData",
-    // https://url.spec.whatwg.org/
-    "URL",
-    "URLSearchParams",
-    // https://w3c.github.io/FileAPI/
-    "File",
-    "Blob",
-    "FileList",
-    "FileReader",
-    "FileReaderSync",
-    // https://dom.spec.whatwg.org/
-    "EventTarget",
-    "ErrorEvent",
-    "Event",
-    "CustomEvent",
-    // http://heycam.github.io/webidl/
-    "DOMException",
-    // https://streams.spec.whatwg.org/
-    "ReadableStream",
-    "WritableStream",
-    "ByteLengthQueuingStrategy",
-    "CountQueuingStrategy",
-    // http://w3c.github.io/IndexedDB/
-    "IDBRequest",
-    "IDBOpenDBRequest",
-    "IDBVersionChangeEvent",
-    "IDBFactory",
-    "IDBDatabase",
-    "IDBObjectStore",
-    "IDBIndex",
-    "IDBKeyRange",
-    "IDBCursor",
-    "IDBCursorWithValue",
-    "IDBTransaction",
-  ];
-  var result = [];
-  for (var i = 0; i < expected.length; ++i) {
-    result.push([expected[i], expected[i] in self]);
-  }
-  e.ports[0].postMessage(result);
-}
\ No newline at end of file
diff --git a/third_party/blink/web_tests/external/wpt/workers/semantics/interface-objects/004.any.js b/third_party/blink/web_tests/external/wpt/workers/semantics/interface-objects/004.any.js
new file mode 100644
index 0000000..358af74c
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/workers/semantics/interface-objects/004.any.js
@@ -0,0 +1,46 @@
+// META: global=!default,sharedworker
+
+var unexpected = [
+  // https://html.spec.whatwg.org/
+  "DedicatedWorkerGlobalScope",
+  "AbstractView",
+  "AbstractWorker",
+  "Location",
+  "Navigator",
+  "DOMImplementation",
+  "Audio",
+  "HTMLCanvasElement",
+  "Path",
+  "TextMetrics",
+  "CanvasProxy",
+  "CanvasRenderingContext2D",
+  "DrawingStyle",
+  "PopStateEvent",
+  "HashChangeEvent",
+  "PageTransitionEvent",
+  // https://streams.spec.whatwg.org/
+  "ReadableStreamDefaultReader",
+  "ReadableStreamBYOBReader",
+  "ReadableStreamDefaultController",
+  "ReadableByteStreamController",
+  "WritableStreamDefaultWriter",
+  "WritableStreamDefaultController",
+  // http://w3c.github.io/IndexedDB/
+  "IDBEnvironment",
+  // https://www.w3.org/TR/2010/NOTE-webdatabase-20101118/
+  "Database",
+  // https://w3c.github.io/uievents/
+  "UIEvent",
+  "FocusEvent",
+  "MouseEvent",
+  "WheelEvent",
+  "InputEvent",
+  "KeyboardEvent",
+  "CompositionEvent",
+];
+
+for (var i = 0; i < unexpected.length; ++i) {
+  test(function() {
+    assert_false(unexpected[i] in self);
+  }, "The " + unexpected[i] + " interface object should not be exposed");
+}
diff --git a/third_party/blink/web_tests/external/wpt/workers/semantics/interface-objects/004.any.sharedworker-expected.txt b/third_party/blink/web_tests/external/wpt/workers/semantics/interface-objects/004.any.sharedworker-expected.txt
new file mode 100644
index 0000000..d834acc
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/workers/semantics/interface-objects/004.any.sharedworker-expected.txt
@@ -0,0 +1,34 @@
+This is a testharness.js-based test.
+PASS The DedicatedWorkerGlobalScope interface object should not be exposed
+PASS The AbstractView interface object should not be exposed
+PASS The AbstractWorker interface object should not be exposed
+PASS The Location interface object should not be exposed
+PASS The Navigator interface object should not be exposed
+PASS The DOMImplementation interface object should not be exposed
+PASS The Audio interface object should not be exposed
+PASS The HTMLCanvasElement interface object should not be exposed
+PASS The Path interface object should not be exposed
+FAIL The TextMetrics interface object should not be exposed assert_false: expected false got true
+PASS The CanvasProxy interface object should not be exposed
+PASS The CanvasRenderingContext2D interface object should not be exposed
+PASS The DrawingStyle interface object should not be exposed
+PASS The PopStateEvent interface object should not be exposed
+PASS The HashChangeEvent interface object should not be exposed
+PASS The PageTransitionEvent interface object should not be exposed
+FAIL The ReadableStreamDefaultReader interface object should not be exposed assert_false: expected false got true
+PASS The ReadableStreamBYOBReader interface object should not be exposed
+PASS The ReadableStreamDefaultController interface object should not be exposed
+PASS The ReadableByteStreamController interface object should not be exposed
+FAIL The WritableStreamDefaultWriter interface object should not be exposed assert_false: expected false got true
+PASS The WritableStreamDefaultController interface object should not be exposed
+PASS The IDBEnvironment interface object should not be exposed
+PASS The Database interface object should not be exposed
+PASS The UIEvent interface object should not be exposed
+PASS The FocusEvent interface object should not be exposed
+PASS The MouseEvent interface object should not be exposed
+PASS The WheelEvent interface object should not be exposed
+PASS The InputEvent interface object should not be exposed
+PASS The KeyboardEvent interface object should not be exposed
+PASS The CompositionEvent interface object should not be exposed
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/external/wpt/workers/semantics/interface-objects/004.html b/third_party/blink/web_tests/external/wpt/workers/semantics/interface-objects/004.html
deleted file mode 100644
index b4a09c5..0000000
--- a/third_party/blink/web_tests/external/wpt/workers/semantics/interface-objects/004.html
+++ /dev/null
@@ -1,19 +0,0 @@
-<!doctype html>
-<title>unavailable interface objects in shared worker</title>
-<script src="/resources/testharness.js"></script>
-<script src="/resources/testharnessreport.js"></script>
-<div id=log></div>
-<script>
-setup(function() {
-  window.worker = new SharedWorker('004.js');
-  worker.port.onmessage = function(e) {
-    var result = e.data;
-    for (var i = 0; i < result.length; ++i) {
-      test(function() {
-        assert_false(result[i][1]);
-      }, "The " + result[i][0] + " interface object should not be exposed");
-    }
-    done();
-  }
-}, {explicit_done: true});
-</script>
\ No newline at end of file
diff --git a/third_party/blink/web_tests/external/wpt/workers/semantics/interface-objects/004.js b/third_party/blink/web_tests/external/wpt/workers/semantics/interface-objects/004.js
deleted file mode 100644
index 00e50d1..0000000
--- a/third_party/blink/web_tests/external/wpt/workers/semantics/interface-objects/004.js
+++ /dev/null
@@ -1,45 +0,0 @@
-onconnect = function(e) {
-  var unexpected = [
-    // https://html.spec.whatwg.org/
-    "DedicatedWorkerGlobalScope",
-    "AbstractView",
-    "AbstractWorker",
-    "Location",
-    "Navigator",
-    "DOMImplementation",
-    "Audio",
-    "HTMLCanvasElement",
-    "Path",
-    "TextMetrics",
-    "CanvasProxy",
-    "CanvasRenderingContext2D",
-    "DrawingStyle",
-    "PopStateEvent",
-    "HashChangeEvent",
-    "PageTransitionEvent",
-    // https://streams.spec.whatwg.org/
-    "ReadableStreamDefaultReader",
-    "ReadableStreamBYOBReader",
-    "ReadableStreamDefaultController",
-    "ReadableByteStreamController",
-    "WritableStreamDefaultWriter",
-    "WritableStreamDefaultController",
-    // http://w3c.github.io/IndexedDB/
-    "IDBEnvironment",
-    // https://www.w3.org/TR/2010/NOTE-webdatabase-20101118/
-    "Database",
-    // https://w3c.github.io/uievents/
-    "UIEvent",
-    "FocusEvent",
-    "MouseEvent",
-    "WheelEvent",
-    "InputEvent",
-    "KeyboardEvent",
-    "CompositionEvent",
-  ];
-  var result = [];
-  for (var i = 0; i < unexpected.length; ++i) {
-    result.push([unexpected[i], unexpected[i] in self]);
-  }
-  e.ports[0].postMessage(result);
-}
\ No newline at end of file
diff --git a/third_party/blink/web_tests/http/tests/serviceworker/webexposed/global-interface-listing-service-worker-expected.txt b/third_party/blink/web_tests/http/tests/serviceworker/webexposed/global-interface-listing-service-worker-expected.txt
index 4fcc8ea..b61487d 100644
--- a/third_party/blink/web_tests/http/tests/serviceworker/webexposed/global-interface-listing-service-worker-expected.txt
+++ b/third_party/blink/web_tests/http/tests/serviceworker/webexposed/global-interface-listing-service-worker-expected.txt
@@ -3679,7 +3679,6 @@
     getter self
     method atob
     method btoa
-    method cancelAnimationFrame
     method clearInterval
     method clearTimeout
     method constructor
@@ -3687,7 +3686,6 @@
     method fetch
     method importScripts
     method queueMicrotask
-    method requestAnimationFrame
     method setInterval
     method setTimeout
     setter onerror
diff --git a/third_party/blink/web_tests/inspector-protocol/memory/sampling-native-profile.js b/third_party/blink/web_tests/inspector-protocol/memory/sampling-native-profile.js
index e054cca..d433396 100644
--- a/third_party/blink/web_tests/inspector-protocol/memory/sampling-native-profile.js
+++ b/third_party/blink/web_tests/inspector-protocol/memory/sampling-native-profile.js
@@ -19,7 +19,8 @@
 
   const profile = message.result.profile;
   const foundTheSample = profile.samples.some(sample =>
-    sample.size >= 500 * 200 && sample.stack.some(frame => frame.includes('HTMLCanvasElement')));
+    sample.size >= 500 * 200 && sample.stack.some(frame =>
+      frame.includes('HTMLCanvasElement') || frame.includes('CanvasRenderingContext')));
   testRunner.log('Found sample: ' + foundTheSample);
   if (!foundTheSample)
     testRunner.log(profile);
diff --git a/third_party/blink/web_tests/inspector-protocol/memory/sampling-native-snapshot.js b/third_party/blink/web_tests/inspector-protocol/memory/sampling-native-snapshot.js
index a70e09dfc..e1e5644 100644
--- a/third_party/blink/web_tests/inspector-protocol/memory/sampling-native-snapshot.js
+++ b/third_party/blink/web_tests/inspector-protocol/memory/sampling-native-snapshot.js
@@ -16,7 +16,8 @@
 
   const profile = message.result.profile;
   const foundTheSample = profile.samples.some(sample =>
-    sample.size >= 640 * 1024 && sample.stack.some(frame => frame.includes('HTMLCanvasElement')));
+    sample.size >= 640 * 1024 && sample.stack.some(frame =>
+      frame.includes('HTMLCanvasElement') || frame.includes('CanvasRenderingContext')));
   testRunner.log('Found sample: ' + foundTheSample);
 
   testRunner.completeTest();
diff --git a/third_party/blink/web_tests/platform/win7/virtual/threaded/external/wpt/css/css-scroll-snap/scroll-margin-expected.txt b/third_party/blink/web_tests/platform/win7/virtual/threaded/external/wpt/css/css-scroll-snap/scroll-margin-expected.txt
deleted file mode 100644
index d3bfa9a..0000000
--- a/third_party/blink/web_tests/platform/win7/virtual/threaded/external/wpt/css/css-scroll-snap/scroll-margin-expected.txt
+++ /dev/null
@@ -1,5 +0,0 @@
-This is a testharness.js-based test.
-PASS Snaps to the positions adjusted by scroll-margin
-FAIL scroll-margin doesn't contribute to the snap position of the element if it's outside of the scroll port assert_equals: expected 0 but got 400
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/virtual/stable/http/tests/serviceworker/webexposed/global-interface-listing-service-worker-expected.txt b/third_party/blink/web_tests/virtual/stable/http/tests/serviceworker/webexposed/global-interface-listing-service-worker-expected.txt
index 4662fd8..12ee79b 100644
--- a/third_party/blink/web_tests/virtual/stable/http/tests/serviceworker/webexposed/global-interface-listing-service-worker-expected.txt
+++ b/third_party/blink/web_tests/virtual/stable/http/tests/serviceworker/webexposed/global-interface-listing-service-worker-expected.txt
@@ -2567,7 +2567,6 @@
     getter self
     method atob
     method btoa
-    method cancelAnimationFrame
     method clearInterval
     method clearTimeout
     method constructor
@@ -2575,7 +2574,6 @@
     method fetch
     method importScripts
     method queueMicrotask
-    method requestAnimationFrame
     method setInterval
     method setTimeout
     setter onerror
diff --git a/third_party/blink/web_tests/virtual/stable/webexposed/global-interface-listing-dedicated-worker-expected.txt b/third_party/blink/web_tests/virtual/stable/webexposed/global-interface-listing-dedicated-worker-expected.txt
index ffd58fa..5af578b 100644
--- a/third_party/blink/web_tests/virtual/stable/webexposed/global-interface-listing-dedicated-worker-expected.txt
+++ b/third_party/blink/web_tests/virtual/stable/webexposed/global-interface-listing-dedicated-worker-expected.txt
@@ -2591,7 +2591,6 @@
 [Worker]     getter self
 [Worker]     method atob
 [Worker]     method btoa
-[Worker]     method cancelAnimationFrame
 [Worker]     method clearInterval
 [Worker]     method clearTimeout
 [Worker]     method constructor
@@ -2599,7 +2598,6 @@
 [Worker]     method fetch
 [Worker]     method importScripts
 [Worker]     method queueMicrotask
-[Worker]     method requestAnimationFrame
 [Worker]     method setInterval
 [Worker]     method setTimeout
 [Worker]     setter onerror
@@ -2715,9 +2713,11 @@
 [Worker]     getter name
 [Worker]     getter onmessage
 [Worker]     getter onmessageerror
+[Worker]     method cancelAnimationFrame
 [Worker]     method close
 [Worker]     method gc
 [Worker]     method postMessage
+[Worker]     method requestAnimationFrame
 [Worker]     method webkitRequestFileSystem
 [Worker]     method webkitRequestFileSystemSync
 [Worker]     method webkitResolveLocalFileSystemSyncURL
diff --git a/third_party/blink/web_tests/virtual/stable/webexposed/global-interface-listing-shared-worker-expected.txt b/third_party/blink/web_tests/virtual/stable/webexposed/global-interface-listing-shared-worker-expected.txt
index 5ec6f8e..9a25a1e2 100644
--- a/third_party/blink/web_tests/virtual/stable/webexposed/global-interface-listing-shared-worker-expected.txt
+++ b/third_party/blink/web_tests/virtual/stable/webexposed/global-interface-listing-shared-worker-expected.txt
@@ -2478,7 +2478,6 @@
 [Worker]     getter self
 [Worker]     method atob
 [Worker]     method btoa
-[Worker]     method cancelAnimationFrame
 [Worker]     method clearInterval
 [Worker]     method clearTimeout
 [Worker]     method constructor
@@ -2486,7 +2485,6 @@
 [Worker]     method fetch
 [Worker]     method importScripts
 [Worker]     method queueMicrotask
-[Worker]     method requestAnimationFrame
 [Worker]     method setInterval
 [Worker]     method setTimeout
 [Worker]     setter onerror
diff --git a/third_party/blink/web_tests/webexposed/global-interface-listing-dedicated-worker-expected.txt b/third_party/blink/web_tests/webexposed/global-interface-listing-dedicated-worker-expected.txt
index d9fad66..b7b3f26 100644
--- a/third_party/blink/web_tests/webexposed/global-interface-listing-dedicated-worker-expected.txt
+++ b/third_party/blink/web_tests/webexposed/global-interface-listing-dedicated-worker-expected.txt
@@ -3731,7 +3731,6 @@
 [Worker]     getter self
 [Worker]     method atob
 [Worker]     method btoa
-[Worker]     method cancelAnimationFrame
 [Worker]     method clearInterval
 [Worker]     method clearTimeout
 [Worker]     method constructor
@@ -3739,7 +3738,6 @@
 [Worker]     method fetch
 [Worker]     method importScripts
 [Worker]     method queueMicrotask
-[Worker]     method requestAnimationFrame
 [Worker]     method setInterval
 [Worker]     method setTimeout
 [Worker]     setter onerror
@@ -3858,9 +3856,11 @@
 [Worker]     getter name
 [Worker]     getter onmessage
 [Worker]     getter onmessageerror
+[Worker]     method cancelAnimationFrame
 [Worker]     method close
 [Worker]     method gc
 [Worker]     method postMessage
+[Worker]     method requestAnimationFrame
 [Worker]     method webkitRequestFileSystem
 [Worker]     method webkitRequestFileSystemSync
 [Worker]     method webkitResolveLocalFileSystemSyncURL
diff --git a/third_party/blink/web_tests/webexposed/global-interface-listing-shared-worker-expected.txt b/third_party/blink/web_tests/webexposed/global-interface-listing-shared-worker-expected.txt
index 5def174..736f3811 100644
--- a/third_party/blink/web_tests/webexposed/global-interface-listing-shared-worker-expected.txt
+++ b/third_party/blink/web_tests/webexposed/global-interface-listing-shared-worker-expected.txt
@@ -3580,7 +3580,6 @@
 [Worker]     getter self
 [Worker]     method atob
 [Worker]     method btoa
-[Worker]     method cancelAnimationFrame
 [Worker]     method clearInterval
 [Worker]     method clearTimeout
 [Worker]     method constructor
@@ -3588,7 +3587,6 @@
 [Worker]     method fetch
 [Worker]     method importScripts
 [Worker]     method queueMicrotask
-[Worker]     method requestAnimationFrame
 [Worker]     method setInterval
 [Worker]     method setTimeout
 [Worker]     setter onerror
diff --git a/third_party/closure_compiler/externs/automation.js b/third_party/closure_compiler/externs/automation.js
index a0015f7..4e6c861 100644
--- a/third_party/closure_compiler/externs/automation.js
+++ b/third_party/closure_compiler/externs/automation.js
@@ -92,7 +92,11 @@
   ALERT: 'alert',
   ALERT_DIALOG: 'alertDialog',
   ANCHOR: 'anchor',
-  ANNOTATION: 'annotation',
+  ANNOTATION_ATTRIBUTION: 'annotationAttribution',
+  ANNOTATION_COMMENTARY: 'annotationCommentary',
+  ANNOTATION_PRESENCE: 'annotationPresence',
+  ANNOTATION_REVISION: 'annotationRevision',
+  ANNOTATION_SUGGESTION: 'annotationSuggestion',
   APPLICATION: 'application',
   ARTICLE: 'article',
   AUDIO: 'audio',
@@ -232,6 +236,7 @@
   ROW: 'row',
   ROW_HEADER: 'rowHeader',
   RUBY: 'ruby',
+  RUBY_ANNOTATION: 'rubyAnnotation',
   SCROLL_BAR: 'scrollBar',
   SCROLL_VIEW: 'scrollView',
   SEARCH: 'search',
diff --git a/third_party/libcxx-pretty-printers/OWNERS b/third_party/libcxx-pretty-printers/OWNERS
index 495b84f4..de81ae9 100644
--- a/third_party/libcxx-pretty-printers/OWNERS
+++ b/third_party/libcxx-pretty-printers/OWNERS
@@ -1,4 +1,3 @@
-dpranke@chromium.org
 leszeks@chromium.org
 thomasanderson@chromium.org
 # COMPONENT: Tools
diff --git a/third_party/metrics_proto/README.chromium b/third_party/metrics_proto/README.chromium
index 5c88f3b8..c953365 100644
--- a/third_party/metrics_proto/README.chromium
+++ b/third_party/metrics_proto/README.chromium
@@ -1,8 +1,8 @@
 Name: Metrics Protos
 Short Name: metrics_proto
 URL: This is the canonical public repository
-Version: 270770521
-Date: 2019/09/23 UTC
+Version: 270927279
+Date: 2019/09/24 UTC
 License: BSD
 Security Critical: Yes
 
diff --git a/third_party/metrics_proto/execution_context.proto b/third_party/metrics_proto/execution_context.proto
index 985a9ff..facdf5f 100644
--- a/third_party/metrics_proto/execution_context.proto
+++ b/third_party/metrics_proto/execution_context.proto
@@ -61,6 +61,12 @@
 
   SERVICE_WORKER_THREAD = 15;
 
+  // Thread pool thread (in renderer process).
+  THREAD_POOL_THREAD = 16;
+
+  // GPU memory thread (in renderer process).
+  GPU_MEMORY_THREAD = 17;
+
   // A Chrome thread not identified by any other enum. Defined for the benefit
   // of Chrome OS. Do not use for the Chrome sampling profiler; define a new
   // enum instead.
diff --git a/third_party/test_fonts/OWNERS b/third_party/test_fonts/OWNERS
index dff9f35a..35481d7 100644
--- a/third_party/test_fonts/OWNERS
+++ b/third_party/test_fonts/OWNERS
@@ -1,4 +1,3 @@
-dpranke@chromium.org
 drott@chromium.org
 thomasanderson@chromium.org
 # COMPONENT: Blink>Fonts
diff --git a/third_party/zlib/google/BUILD.gn b/third_party/zlib/google/BUILD.gn
index 0dee214..4024836 100644
--- a/third_party/zlib/google/BUILD.gn
+++ b/third_party/zlib/google/BUILD.gn
@@ -26,12 +26,23 @@
     sources = [
       "compression_utils.cc",
       "compression_utils.h",
-      "compression_utils_portable.cc",
-      "compression_utils_portable.h",
     ]
     deps = [
+      ":compression_utils_portable",
       "//base",
       "//third_party/zlib",
     ]
   }
 }
+
+# This allows other users of Chromium's zlib library, but don't use Chromium's
+# //base, to reuse some boilerplate code.
+static_library("compression_utils_portable") {
+  sources = [
+    "compression_utils_portable.cc",
+    "compression_utils_portable.h",
+  ]
+  deps = [
+    "//third_party/zlib",
+  ]
+}
diff --git a/tools/clang/blink_gc_plugin/process-graph.py b/tools/clang/blink_gc_plugin/process-graph.py
index fcbce952..865a2343 100755
--- a/tools/clang/blink_gc_plugin/process-graph.py
+++ b/tools/clang/blink_gc_plugin/process-graph.py
@@ -169,7 +169,7 @@
 
 def build_graph(filename):
   for decl in parse_file(filename):
-    if decl.has_key('name'):
+    if 'name' in decl:
       # Add/update a node entry
       name = decl['name']
       node = get_node(name)
diff --git a/tools/clang/traffic_annotation_extractor/traffic_annotation_extractor.cpp b/tools/clang/traffic_annotation_extractor/traffic_annotation_extractor.cpp
index b1e64db..5e67fa5 100644
--- a/tools/clang/traffic_annotation_extractor/traffic_annotation_extractor.cpp
+++ b/tools/clang/traffic_annotation_extractor/traffic_annotation_extractor.cpp
@@ -47,17 +47,6 @@
 struct Location {
   std::string file_path;
   int line_number = -1;
-
-  // Name of the function including this line. E.g., in the following code,
-  // |function_name| will be 'foo' for all |line_number| values 101-103.
-  //
-  // 100 void foo() {
-  // 101   NetworkTrafficAnnotationTag baz =
-  // 102       net::DefineNetworkTrafficAnnotation(...); }
-  // 103   bar(baz);
-  // 104 }
-  // If no function is found, 'Global Namespace' will be returned.
-  std::string function_name;
 };
 
 // An instance of a call to either of the 4 network traffic annotation
@@ -165,13 +154,6 @@
     location->line_number =
         result.SourceManager->getSpellingLineNumber(source_location);
 
-    const clang::FunctionDecl* ancestor =
-        result.Nodes.getNodeAs<clang::FunctionDecl>("function_context");
-    if (ancestor)
-      location->function_name = ancestor->getQualifiedNameAsString();
-    else
-      location->function_name = "Global Namespace";
-
     std::replace(location->file_path.begin(), location->file_path.end(), '\\',
                  '/');
 
@@ -196,45 +178,20 @@
     collector_->calls.push_back(instance);
   }
 
-  // Tests if the given function name belongs to the network traffic annotation
-  // API. These functions are all defined in
-  // 'net/traffic_annotation/network_traffic_annotation.h'.
-  bool IsAPIFunction(const std::string& function_name) {
-    return function_name == "net::NetworkTrafficAnnotationTag::NotReached" ||
-           function_name == "net::DefineNetworkTrafficAnnotation" ||
-           function_name == "net::DefinePartialNetworkTrafficAnnotation" ||
-           function_name == "net::CompleteNetworkTrafficAnnotation" ||
-           function_name == "net::BranchedCompleteNetworkTrafficAnnotation" ||
-           function_name ==
-               "net::MutableNetworkTrafficAnnotationTag::operator "
-               "NetworkTrafficAnnotationTag" ||
-           function_name ==
-               "net::MutablePartialNetworkTrafficAnnotationTag::operator "
-               "PartialNetworkTrafficAnnotationTag";
-  }
-
   // Stores an annotation constructor called with list expression.
   void AddConstructor(const clang::CXXConstructExpr* constructor_expr,
                       const MatchFinder::MatchResult& result) {
     Location instance;
-
     GetInstanceLocation(result, constructor_expr, &instance);
-    // Only report if the constructor is not in one of the API functions for
-    // network traffic annotations.
-    if (!IsAPIFunction(instance.function_name))
-      collector_->assignments.push_back(instance);
+    collector_->assignments.push_back(instance);
   }
 
   // Stores a value assignment to |unique_id_hash_code| of a mutable annotaton.
   void AddAssignment(const clang::MemberExpr* member_expr,
                      const MatchFinder::MatchResult& result) {
     Location instance;
-
     GetInstanceLocation(result, member_expr, &instance);
-    // Only report if the assignment is not in one of the API functions for
-    // network traffic annotations.
-    if (!IsAPIFunction(instance.function_name))
-      collector_->assignments.push_back(instance);
+    collector_->assignments.push_back(instance);
   }
 
   // Stores an annotation.
@@ -414,7 +371,6 @@
   for (const NetworkAnnotationInstance& instance : collector.annotations) {
     llvm::outs() << "==== NEW ANNOTATION ====\n";
     llvm::outs() << instance.location.file_path << "\n";
-    llvm::outs() << instance.location.function_name << "\n";
     llvm::outs() << instance.location.line_number << "\n";
     llvm::outs() << instance.GetTypeName() << "\n";
     llvm::outs() << instance.annotation.unique_id << "\n";
@@ -427,7 +383,6 @@
   for (const CallInstance& instance : collector.calls) {
     llvm::outs() << "==== NEW CALL ====\n";
     llvm::outs() << instance.location.file_path << "\n";
-    llvm::outs() << instance.location.function_name << "\n";
     llvm::outs() << instance.location.line_number << "\n";
     llvm::outs() << instance.called_function_name << "\n";
     llvm::outs() << instance.has_annotation << "\n";
@@ -438,7 +393,6 @@
   for (const Location& instance : collector.assignments) {
     llvm::outs() << "==== NEW ASSIGNMENT ====\n";
     llvm::outs() << instance.file_path << "\n";
-    llvm::outs() << instance.function_name << "\n";
     llvm::outs() << instance.line_number << "\n";
     llvm::outs() << "==== ASSIGNMENT ENDS ====\n";
   }
diff --git a/tools/mb/mb_config.pyl b/tools/mb/mb_config.pyl
index f79e965..e977bca 100644
--- a/tools/mb/mb_config.pyl
+++ b/tools/mb/mb_config.pyl
@@ -1278,23 +1278,23 @@
     ],
 
     'chromeos_with_codecs_debug_bot': [
-      'chromeos_with_codecs', 'debug_bot', 'use_vaapi',
+      'chromeos_with_codecs', 'debug_bot',
     ],
 
     'chromeos_with_codecs_release_bot': [
-      'chromeos_with_codecs', 'release_bot', 'use_vaapi',
+      'chromeos_with_codecs', 'release_bot',
     ],
 
     'chromeos_with_codecs_release_bot_coverage': [
-      'chromeos_with_codecs', 'release_bot', 'use_vaapi', 'use_clang_coverage',
+      'chromeos_with_codecs', 'release_bot', 'use_clang_coverage',
     ],
 
     'chromeos_with_codecs_release_trybot': [
-      'chromeos_with_codecs', 'release_trybot', 'use_vaapi', 'no_symbols',
+      'chromeos_with_codecs', 'release_trybot', 'no_symbols',
     ],
 
     'chromeos_with_codecs_release_trybot_code_coverage': [
-      'chromeos_with_codecs', 'release_trybot', 'use_vaapi', 'no_symbols',
+      'chromeos_with_codecs', 'release_trybot', 'no_symbols',
       'use_clang_coverage', 'partial_code_coverage_instrumentation',
     ],
 
@@ -2467,10 +2467,6 @@
       'gn_args': 'use_dummy_lastchange=true',
     },
 
-    'use_vaapi': {
-      'gn_args': 'use_vaapi=true',
-    },
-
     'v8_simulate_arm': {
       'gn_args': 'target_cpu="x86" v8_target_cpu="arm"',
     },
diff --git a/tools/metrics/histograms/enums.xml b/tools/metrics/histograms/enums.xml
index d4ddab1..4b404954 100644
--- a/tools/metrics/histograms/enums.xml
+++ b/tools/metrics/histograms/enums.xml
@@ -18121,6 +18121,7 @@
   <int value="605" label="DeviceLoginSpokenFeedbackEnabled"/>
   <int value="606" label="DeviceLoginScreenHighContrastEnabled"/>
   <int value="607" label="DeviceLoginScreenVirtualKeyboardEnabled"/>
+  <int value="608" label="CloudExtensionRequestEnabled"/>
 </enum>
 
 <enum name="EnterprisePolicyInvalidations">
diff --git a/tools/metrics/histograms/histograms.xml b/tools/metrics/histograms/histograms.xml
index 1b457dd0..222d331 100644
--- a/tools/metrics/histograms/histograms.xml
+++ b/tools/metrics/histograms/histograms.xml
@@ -442,7 +442,7 @@
 </histogram>
 
 <histogram name="Accessibility.Mac.DifferentiateWithoutColor"
-    enum="BooleanEnabled" expires_after="2019-11-02">
+    enum="BooleanEnabled" expires_after="2020-11-02">
   <owner>ellyjones@chromium.org</owner>
   <summary>
     Whether the &quot;differentiate without color&quot; Mac system setting is
@@ -451,7 +451,7 @@
 </histogram>
 
 <histogram name="Accessibility.Mac.FullKeyboardAccessEnabled"
-    enum="BooleanEnabled" expires_after="2019-11-02">
+    enum="BooleanEnabled" expires_after="2020-11-02">
   <owner>ellyjones@chromium.org</owner>
   <summary>
     Whether the &quot;full keyboard access&quot; Mac system setting is enabled.
@@ -460,7 +460,7 @@
 </histogram>
 
 <histogram name="Accessibility.Mac.IncreaseContrast" enum="BooleanEnabled"
-    expires_after="2019-11-02">
+    expires_after="2020-11-02">
   <owner>ellyjones@chromium.org</owner>
   <summary>
     Whether the &quot;increase contrast&quot; Mac system setting is enabled.
@@ -481,7 +481,7 @@
 </histogram>
 
 <histogram name="Accessibility.Mac.ReduceTransparency" enum="BooleanEnabled"
-    expires_after="2019-11-02">
+    expires_after="2020-11-02">
   <owner>ellyjones@chromium.org</owner>
   <summary>
     Whether the &quot;reduce transparency&quot; Mac system setting is enabled.
@@ -1745,7 +1745,7 @@
 </histogram>
 
 <histogram name="Android.BackgroundTaskScheduler.TaskLoadedNative"
-    enum="BackgroundTaskId" expires_after="2020-01-20">
+    enum="BackgroundTaskId" expires_after="2020-03-22">
   <owner>fgorski@chromium.org</owner>
   <owner>nyquist@chromium.org</owner>
   <summary>Records that a specific background task has loaded native.</summary>
@@ -1791,7 +1791,7 @@
 </histogram>
 
 <histogram name="Android.BackgroundTaskScheduler.TaskStarted"
-    enum="BackgroundTaskId" expires_after="2020-01-20">
+    enum="BackgroundTaskId" expires_after="2020-03-22">
   <owner>fgorski@chromium.org</owner>
   <owner>nyquist@chromium.org</owner>
   <summary>Records that a specific background task has been started.</summary>
@@ -2352,7 +2352,7 @@
 </histogram>
 
 <histogram name="Android.DownloadManager.List.View.Action"
-    enum="Android.DownloadManager.List.View.Actions" expires_after="2020-01-20">
+    enum="Android.DownloadManager.List.View.Actions" expires_after="2020-03-22">
   <owner>dtrainor@chromium.org</owner>
   <owner>clank-downloads@google.com</owner>
   <summary>The count of Download Home list entry actions taken.</summary>
@@ -3213,7 +3213,7 @@
 </histogram>
 
 <histogram name="Android.NTP.Impression" enum="NTPImpressionType"
-    expires_after="2020-01-20">
+    expires_after="2020-03-22">
   <owner>finkm@chromium.org</owner>
   <summary>
     Counts impressions of the NTP on Android. It also counts potential
@@ -3249,7 +3249,7 @@
 </histogram>
 
 <histogram name="Android.OmniboxFocusReason" enum="OmniboxFocusReason"
-    expires_after="2020-01-20">
+    expires_after="2020-03-22">
   <owner>mdjones@chromium.org</owner>
   <owner>twellington@chromium.org</owner>
   <owner>amaralp@chromium.org</owner>
@@ -4336,7 +4336,7 @@
 </histogram>
 
 <histogram name="AndroidSms.PWAInstallationResult"
-    enum="WebAppInstallResultCode" expires_after="2020-01-20">
+    enum="WebAppInstallResultCode" expires_after="2020-03-22">
   <owner>azeemarshad@chromium.org</owner>
   <summary>
     Records installation result code for every installation attempt for Android
@@ -5073,7 +5073,7 @@
 </histogram>
 
 <histogram name="Apps.AppList.ZeroStateSuggestionOpenType"
-    enum="AppListSearchResult" expires_after="2020-01-20">
+    enum="AppListSearchResult" expires_after="2020-03-22">
   <owner>jennyz@chromium.org</owner>
   <owner>newcomer@chromium.org</owner>
   <summary>
@@ -5146,7 +5146,8 @@
   </summary>
 </histogram>
 
-<histogram name="Apps.AppListCreationTime" units="ms">
+<histogram name="Apps.AppListCreationTime" units="ms"
+    expires_after="2020-03-22">
   <owner>calamity@chromium.org</owner>
   <summary>
     The amount of time it takes to build the app list UI. This is logged each
@@ -5167,7 +5168,7 @@
 </histogram>
 
 <histogram name="Apps.AppListFolder.ShowHide.AnimationSmoothness" units="%"
-    expires_after="2020-01-20">
+    expires_after="2020-03-22">
   <owner>wutao@chromium.org</owner>
   <summary>
     Relative smoothness of animations of showing and hiding app list folder.
@@ -5306,7 +5307,7 @@
 </histogram>
 
 <histogram name="Apps.AppListPlayStoreSearchAppsDisplayed" units="apss"
-    expires_after="2020-01-20">
+    expires_after="2020-03-22">
   <owner>jennyz@chromium.org</owner>
   <owner>newcomer@chromium.org</owner>
   <summary>
@@ -5356,7 +5357,7 @@
 </histogram>
 
 <histogram name="Apps.AppListResultLaunchIndexAndQueryLength" units="units"
-    expires_after="2020-01-20">
+    expires_after="2020-03-22">
   <owner>tby@chromium.org</owner>
   <owner>jiameng@chromium.org</owner>
   <summary>
@@ -5367,7 +5368,7 @@
 </histogram>
 
 <histogram name="Apps.AppListSearchAbandonQueryLength" units="characters"
-    expires_after="2020-01-20">
+    expires_after="2020-03-22">
   <owner>jennyz@chromium.org</owner>
   <owner>jiameng@chromium.org</owner>
   <summary>
@@ -5413,7 +5414,7 @@
 </histogram>
 
 <histogram name="Apps.AppListSearchQueryLength" units="characters"
-    expires_after="2020-01-20">
+    expires_after="2020-03-22">
 <!-- Name completed by histogram_suffixes name="TabletOrClamshellMode" -->
 
   <owner>calamity@chromium.org</owner>
@@ -5424,7 +5425,7 @@
 </histogram>
 
 <histogram name="Apps.AppListSearchResultDistanceFromOrigin" units="keystrokes"
-    expires_after="2020-01-20">
+    expires_after="2020-03-22">
   <owner>calamity@chromium.org</owner>
   <summary>
     The minimum number of arrow keys a user would need to press to navigate to
@@ -5531,7 +5532,7 @@
 </histogram>
 
 <histogram name="Apps.AppListSuggestedChipLaunched" units="indices"
-    expires_after="2020-01-20">
+    expires_after="2020-03-22">
   <owner>newcomer@chromium.org</owner>
   <owner>charleszhao@chromium.org</owner>
   <summary>
@@ -5554,7 +5555,7 @@
 </histogram>
 
 <histogram name="Apps.AppListTileLaunchIndexAndQueryLength" units="units"
-    expires_after="2020-01-20">
+    expires_after="2020-03-22">
   <owner>tby@chromium.org</owner>
   <owner>jiameng@chromium.org</owner>
   <summary>
@@ -6031,7 +6032,7 @@
 </histogram>
 
 <histogram name="Apps.StateTransition.AnimationSmoothness" units="%"
-    expires_after="2020-01-20">
+    expires_after="2020-03-22">
 <!-- Name completed by histogram_suffixes
      name="TabletOrClamshellMode" and
      name="EnterOrExitOverview" and
@@ -6257,7 +6258,7 @@
 </histogram>
 
 <histogram name="Arc.CustomTabs.SessionEndReason"
-    enum="ArcCustomTabsSessionEndReason" expires_after="2020-01-20">
+    enum="ArcCustomTabsSessionEndReason" expires_after="2020-03-22">
   <owner>hashimoto@google.com</owner>
   <owner>shihuis@google.com</owner>
   <summary>
@@ -6524,7 +6525,7 @@
   </summary>
 </histogram>
 
-<histogram name="Arc.OOMKills.Count" units="kills" expires_after="2020-01-20">
+<histogram name="Arc.OOMKills.Count" units="kills" expires_after="2020-03-22">
   <owner>elijahtaylor@google.com</owner>
   <owner>shihuis@google.com</owner>
   <summary>Cumulative count of OOM kills in one user session.</summary>
@@ -6584,7 +6585,7 @@
 </histogram>
 
 <histogram name="Arc.OptInSilentAuthCode.SecondaryAccount"
-    enum="ArcOptInSilentAuthCode" expires_after="2020-01-20">
+    enum="ArcOptInSilentAuthCode" expires_after="2020-03-22">
   <owner>khmel@google.com</owner>
   <summary>
     Arc Silent Auth Code status. This status is set during the minting of an
@@ -6615,7 +6616,7 @@
 </histogram>
 
 <histogram name="Arc.PlayStoreSearch.QueryTime" units="ms"
-    expires_after="2020-01-20">
+    expires_after="2020-03-22">
   <owner>hejq@chromium.org</owner>
   <summary>
     Time between sending an Play Store app discovery request and the storing
@@ -7294,7 +7295,7 @@
 </histogram>
 
 <histogram name="Ash.Login.Lock.AuthMethod.Used.ClamShellMode"
-    enum="AuthMethod" expires_after="2020-01-20">
+    enum="AuthMethod" expires_after="2020-03-22">
   <owner>jdufault@google.com</owner>
   <summary>
     The usage of different auth methods (PIN / Password / Smartlock /
@@ -7303,7 +7304,7 @@
 </histogram>
 
 <histogram name="Ash.Login.Lock.AuthMethod.Used.TabletMode" enum="AuthMethod"
-    expires_after="2020-01-20">
+    expires_after="2020-03-22">
   <owner>jdufault@google.com</owner>
   <summary>
     The usage of different auth methods (PIN / Password / Smartlock /
@@ -7350,7 +7351,7 @@
 </histogram>
 
 <histogram name="Ash.NightLight.DisplayCrtcCtmSupport"
-    enum="AshNightLightDisplayCrtcCtmSupportType" expires_after="2020-01-20">
+    enum="AshNightLightDisplayCrtcCtmSupportType" expires_after="2020-03-22">
   <owner>afakhry@chromium.org</owner>
   <summary>
     The type of CRTC color transorm matrix support for the currently connected
@@ -7361,7 +7362,7 @@
 </histogram>
 
 <histogram name="Ash.NightLight.ScheduleType" enum="AshNightLightScheduleType"
-    expires_after="2020-01-20">
+    expires_after="2020-03-22">
   <owner>afakhry@chromium.org</owner>
   <summary>
     The selected Night Light schedule type. Emitted when the user changes the
@@ -7380,7 +7381,7 @@
 </histogram>
 
 <histogram name="Ash.NumberOfVisibleWindowsInPrimaryDisplay" units="Windows"
-    expires_after="2020-01-20">
+    expires_after="2020-03-22">
   <owner>jamescook@chromium.org</owner>
   <summary>
     An upper bound on the number of windows visible to the user on the primary
@@ -7552,7 +7553,7 @@
 </histogram>
 
 <histogram name="Ash.Rotation.AnimationSmoothness" units="%"
-    expires_after="2020-01-20">
+    expires_after="2020-03-22">
   <owner>wutao@chromium.org</owner>
   <summary>
     Relative smoothness of animations when rotating screen. 100% represents
@@ -8052,13 +8053,13 @@
 </histogram>
 
 <histogram name="Ash.TouchView.TouchViewActivePercentage" units="%"
-    expires_after="2020-01-20">
+    expires_after="2020-03-22">
   <owner>girard@chromium.org</owner>
   <summary>The proportion of time spent in TouchView during a session.</summary>
 </histogram>
 
 <histogram name="Ash.TouchView.TouchViewActiveTotal" units="minutes"
-    expires_after="2020-01-20">
+    expires_after="2020-03-22">
   <owner>girard@chromium.org</owner>
   <summary>The total time that TouchView is active during a session.</summary>
 </histogram>
@@ -8209,7 +8210,7 @@
 </histogram>
 
 <histogram name="Ash.Window.AnimationSmoothness.CrossFade" units="%"
-    expires_after="2020-01-20">
+    expires_after="2020-03-22">
   <owner>wutao@chromium.org</owner>
   <summary>
     Relative smoothness of cross fade animation when setting window bounds. 100%
@@ -8221,7 +8222,7 @@
 </histogram>
 
 <histogram name="Ash.Window.AnimationSmoothness.Hide" units="%"
-    expires_after="2020-01-20">
+    expires_after="2020-03-22">
   <owner>wutao@chromium.org</owner>
   <summary>
     Relative smoothness of hiding window animation. 100% represents ideally
@@ -8481,7 +8482,7 @@
 </histogram>
 
 <histogram name="Assistant.ContainerView.Resize.AnimationSmoothness" units="%"
-    expires_after="2020-01-20">
+    expires_after="2020-03-22">
   <owner>wutao@chromium.org</owner>
   <summary>
     Relative animation smoothness of resizing assistant container window. 100%
@@ -8701,7 +8702,7 @@
 </histogram>
 
 <histogram name="Assistant.QuerySource" enum="AssistantQuerySource"
-    expires_after="2019-11-30">
+    expires_after="2020-03-22">
   <owner>xiaohuic@chromium.org</owner>
   <owner>meilinw@chromium.org</owner>
   <summary>
@@ -12641,7 +12642,7 @@
 </histogram>
 
 <histogram name="AutoScreenBrightness.DailyUserAdjustment.Atlas" units="count"
-    expires_after="2020-01-20">
+    expires_after="2020-03-22">
   <owner>jiameng@chromium.org</owner>
   <summary>
     Number of times that a user has made brightness adjustments on an Atlas
@@ -14928,7 +14929,7 @@
 </histogram>
 
 <histogram name="Blink.HandleInputEvents.UpdateTime" units="microseconds"
-    expires_after="2020-01-20">
+    expires_after="2020-03-22">
   <owner>paint-dev@chromium.org</owner>
   <summary>
     Time spent processing rAF-aligned input during a main frame update.
@@ -16047,7 +16048,7 @@
 </histogram>
 
 <histogram name="Blink.UseCounter.Extensions.Features" enum="FeatureObserver"
-    expires_after="2020-01-20">
+    expires_after="2020-03-22">
   <owner>loonybear@chromium.org</owner>
   <summary>
     Count of how many page loads use various features for pages with a
@@ -17836,7 +17837,7 @@
 </histogram>
 
 <histogram name="Bookmarks.Count.OnProfileLoad" units="bookmarks"
-    expires_after="2020-01-20">
+    expires_after="2020-03-22">
   <owner>supertri@chromium.org</owner>
   <owner>isherman@chromium.org</owner>
   <owner>aidanday@google.com</owner>
@@ -17850,7 +17851,7 @@
 </histogram>
 
 <histogram name="Bookmarks.Count.OnProfileLoad.DuplicateUrl" units="bookmarks"
-    expires_after="2020-01-20">
+    expires_after="2020-03-22">
   <owner>mamir@chromium.org</owner>
   <owner>mastiz@chromium.org</owner>
   <summary>
@@ -17862,7 +17863,7 @@
 </histogram>
 
 <histogram name="Bookmarks.Count.OnProfileLoad.EmptyTitle" units="bookmarks"
-    expires_after="2020-01-20">
+    expires_after="2020-03-22">
   <owner>mamir@chromium.org</owner>
   <owner>mastiz@chromium.org</owner>
   <summary>
@@ -18110,7 +18111,7 @@
 </histogram>
 
 <histogram base="true" name="Browser.Tabs.TotalSwitchDuration" units="ms"
-    expires_after="2020-01-20">
+    expires_after="2020-03-22">
 <!-- Name completed by histogram_suffixes name="TabSwitchingType" -->
 
   <owner>ejoe@google.com</owner>
@@ -19687,7 +19688,7 @@
 </histogram>
 
 <histogram name="ChildProcess.Launched.UtilityProcessHash"
-    enum="UtilityProcessNameHash" expires_after="2020-01-20">
+    enum="UtilityProcessNameHash" expires_after="2020-03-22">
   <owner>wfh@chromium.org</owner>
   <summary>
     Count of child utility process launches, bucketed by the hash of their
@@ -23371,7 +23372,7 @@
 </histogram>
 
 <histogram name="ContentSuggestions.Feed.DisplayStatusOnOpen"
-    enum="ContentSuggestionsDisplayStatus" expires_after="2020-01-20">
+    enum="ContentSuggestionsDisplayStatus" expires_after="2020-03-22">
   <owner>gangwu@chromium.org</owner>
   <owner>fgorski@chromium.org</owner>
   <summary>
@@ -25324,7 +25325,7 @@
 </histogram>
 
 <histogram name="Cras.MissedCallbackFrequencyInput" units="count"
-    expires_after="2020-01-17">
+    expires_after="2020-03-22">
   <owner>yuhsuan@chromium.org</owner>
   <owner>chromeos-audio@google.com</owner>
   <summary>
@@ -26686,7 +26687,7 @@
 </histogram>
 
 <histogram name="Cryptohome.Errors" enum="CryptohomeError"
-    expires_after="2020-01-20">
+    expires_after="2020-03-22">
   <owner>apronin@chromium.org</owner>
   <owner>cros-hwsec+uma@chromium.org</owner>
   <summary>Cryptohome errors.</summary>
@@ -26943,7 +26944,7 @@
   </summary>
 </histogram>
 
-<histogram name="CryptohomeClient" units="ms" expires_after="2020-01-20">
+<histogram name="CryptohomeClient" units="ms" expires_after="2020-03-22">
   <owner>zuan@chromium.org</owner>
   <owner>cros-hwsec+uma@chromium.org</owner>
   <summary>
@@ -26966,7 +26967,7 @@
 </histogram>
 
 <histogram name="CustomTabs.ClientAppId" enum="ClientAppId"
-    expires_after="2020-01-20">
+    expires_after="2020-03-22">
   <owner>yusufo@chromium.org</owner>
   <summary>
     Android: AppId declared by the launching application in EXTRA_APPLICATION_ID
@@ -33154,7 +33155,7 @@
 </histogram>
 
 <histogram base="true" name="Download.Counts" enum="DownloadCountType"
-    expires_after="2020-01-20">
+    expires_after="2020-03-22">
 <!-- Name completed by histogram_suffixes name="DownloadSource" -->
 
   <owner>xingliu@chromium.org</owner>
@@ -33754,7 +33755,7 @@
 </histogram>
 
 <histogram name="Download.IOSDownloadFileResult" enum="DownloadFileResult"
-    expires_after="2020-01-15">
+    expires_after="2020-03-22">
   <owner>eugenebut@chromium.org</owner>
   <summary>Result when a user attempts to download a file on iOS.</summary>
 </histogram>
@@ -36518,7 +36519,7 @@
 </histogram>
 
 <histogram name="Enterprise.Policies" enum="EnterprisePolicies"
-    expires_after="2020-01-20">
+    expires_after="2020-03-22">
   <owner>mnissler@chromium.org</owner>
   <summary>
     A set of enterprise policy rules that are in use. This is recorded every 24
@@ -37205,7 +37206,7 @@
 </histogram>
 
 <histogram name="Event.AggregatedLatency.Renderer2" units="microseconds"
-    expires_after="2020-01-20">
+    expires_after="2020-03-22">
   <owner>tdresser@chromium.org</owner>
   <summary>
     Time between initiation of any input event and the renderer receiving and
@@ -38387,7 +38388,7 @@
 </histogram>
 
 <histogram name="Event.Latency.EndToEnd.KeyPress" units="microseconds"
-    expires_after="2020-01-20">
+    expires_after="2020-03-22">
   <owner>tdresser@chromium.org</owner>
   <owner>input-dev@chromium.org</owner>
   <summary>
@@ -38437,7 +38438,7 @@
 </histogram>
 
 <histogram name="Event.Latency.HitTest" units="microseconds"
-    expires_after="2020-01-20">
+    expires_after="2020-03-22">
   <owner>paint-dev@chromium.org</owner>
   <owner>schenney@chromium.org</owner>
   <summary>
@@ -38456,7 +38457,7 @@
 </histogram>
 
 <histogram name="Event.Latency.HitTestRecursive" units="microseconds"
-    expires_after="2020-01-20">
+    expires_after="2020-03-22">
   <owner>paint-dev@chromium.org</owner>
   <owner>schenney@chromium.org</owner>
   <summary>
@@ -38836,7 +38837,7 @@
 </histogram>
 
 <histogram name="Event.Latency.ScrollBegin.TimeToScrollUpdateSwapBegin2"
-    units="microseconds" expires_after="2020-01-20">
+    units="microseconds" expires_after="2020-03-22">
   <owner>nzolghadr@chromium.org</owner>
   <summary>
     Time between initial creation of a wheel/touch event and start of the frame
@@ -39042,7 +39043,7 @@
 
 <histogram
     name="Event.Latency.ScrollBegin.Wheel.BrowserNotifiedToBeforeGpuSwap2"
-    units="microseconds" expires_after="2020-01-20">
+    units="microseconds" expires_after="2020-03-22">
   <owner>tdresser@chromium.org</owner>
   <summary>
     Time between the browser receives the notification of the first ScrollUpdate
@@ -39486,7 +39487,7 @@
 </histogram>
 
 <histogram name="Event.Latency.ScrollUpdate.TimeToScrollUpdateSwapBegin2"
-    units="microseconds" expires_after="2020-01-20">
+    units="microseconds" expires_after="2020-03-22">
   <owner>nzolghadr@chromium.org</owner>
   <summary>
     Time between initial creation of a wheel/touch event and start of the frame
@@ -39499,7 +39500,7 @@
 </histogram>
 
 <histogram name="Event.Latency.ScrollUpdate.Touch.AverageLag" units="pixels"
-    expires_after="2020-01-20">
+    expires_after="2020-03-22">
   <owner>eirage@chromium.org</owner>
   <owner>nzolghadr@chromium.org</owner>
   <summary>
@@ -39614,7 +39615,7 @@
 </histogram>
 
 <histogram name="Event.Latency.ScrollUpdate.Touch.RAFTimeToFrameSwapEnd"
-    units="microseconds" expires_after="2020-01-20">
+    units="microseconds" expires_after="2020-03-22">
   <owner>eirage@chromium.org</owner>
   <owner>axantoine@google.com</owner>
   <summary>
@@ -40137,7 +40138,7 @@
 </histogram>
 
 <histogram name="Event.PassiveListeners" enum="EventResultType"
-    expires_after="2020-01-20">
+    expires_after="2020-03-22">
   <owner>dtapuska@chromium.org</owner>
   <summary>
     The result of handling of MouseWheel, TouchStart, TouchMove, TouchEnd events
@@ -40335,7 +40336,7 @@
 </histogram>
 
 <histogram name="Event.Touch.FilteredAtPassthroughQueue"
-    enum="EventPreFilterResult" expires_after="2020-01-20">
+    enum="EventPreFilterResult" expires_after="2020-03-22">
   <owner>charliea@chromium.org</owner>
   <owner>nzolghadr@chromium.org</owner>
   <summary>
@@ -40938,7 +40939,7 @@
 </histogram>
 
 <histogram name="ExploreSites.CatalogUpdateRequestSource"
-    enum="ExploreSitesCatalogUpdateRequestSource" expires_after="2019-12-15">
+    enum="ExploreSitesCatalogUpdateRequestSource" expires_after="2020-03-22">
   <owner>dewittj@chromium.org</owner>
   <owner>petewil@chromium.org</owner>
   <summary>
@@ -50661,7 +50662,7 @@
 </histogram>
 
 <histogram name="GPU.GPUProcessTerminationStatus2" enum="GpuTerminationStatus"
-    expires_after="2020-01-20">
+    expires_after="2020-03-22">
   <owner>vmiura@chromium.org</owner>
   <summary>
     Counts for each time the GPU Process Host detects the process dies.
@@ -57243,7 +57244,7 @@
 </histogram>
 
 <histogram name="Launch.FlagsAtStartup" enum="LoginCustomFlags"
-    expires_after="2020-01-20">
+    expires_after="2020-03-22">
   <owner>asvitkine@chromium.org</owner>
   <summary>
     Logs which Chrome flags from about:flags were active on start up. Note that
@@ -57261,7 +57262,7 @@
 </histogram>
 
 <histogram name="Launch.HomeScreenSource" enum="LaunchFromHomeScreenSource"
-    expires_after="2020-01-20">
+    expires_after="2020-03-22">
   <owner>dominickn@chromium.org</owner>
   <summary>
     Records the source of an Android homescreen launch intent used to launch
@@ -90382,7 +90383,7 @@
 </histogram>
 
 <histogram name="NQE.MainFrame.EffectiveConnectionType"
-    enum="NQEEffectiveConnectionType" expires_after="2020-01-20">
+    enum="NQEEffectiveConnectionType" expires_after="2020-03-22">
   <owner>tbansal@chromium.org</owner>
   <owner>bengr@chromium.org</owner>
   <summary>
@@ -93471,6 +93472,36 @@
   </summary>
 </histogram>
 
+<histogram name="Omnibox.MatchStability.AsyncMatchChange" units="position"
+    expires_after="2020-03-01">
+  <owner>tommycli@chromium.org</owner>
+  <owner>jdonnelly@chromium.org</owner>
+  <summary>
+    This tracks how unstable our matches are due to asynchronous updates.
+    Whenever a dropdown position gets asynchronously set to a new match that's
+    different from the old match, this histogram logs the index.
+
+    We primarily care the default match, which is logged at index 0. All indices
+    matter though, so we track the other indices too. When the number of matches
+    changes asynchronously, matches that are removed are considered a
+    &quot;change&quot; and logged here. Matches that are appended are less
+    disruptive to the user, and are not logged.
+
+    This is because this metric is primarily interested in how many times the
+    user could be looking at a match with the intention of selecting it, and
+    then it surprisingly goes away.
+
+    Note that merely adding a new asynchronous provider will increase these
+    counts. That's intentional. Swapping matches out annoys users, and to avoid
+    this, we should do some things like: caching, update coaslescing, update
+    deferral until next keystroke, provider timeouts, etc.
+
+    This metric is designed to be normalized by the True count of
+    Omnibox.Start.WantAsyncMatches, which will yield the number of times the
+    match changes asynchronously per keystroke or other user gesture.
+  </summary>
+</histogram>
+
 <histogram name="Omnibox.NumEvents" enum="UsedOmnibox">
   <owner>mpearson@chromium.org</owner>
   <owner>jdonnelly@chromium.org</owner>
@@ -93843,6 +93874,23 @@
   </summary>
 </histogram>
 
+<histogram name="Omnibox.Start.WantAsyncMatches" enum="Boolean"
+    expires_after="2020-03-01">
+  <owner>tommycli@chromium.org</owner>
+  <owner>jdonnelly@chromium.org</owner>
+  <summary>
+    Whether asynchronous matches are requested. Recorded every time
+    AutocompleteController::Start() is called.
+
+    False counts roughly correspond to non-interactive invocations of
+    AutocompleteController - like when we are classifying user text as a Search
+    or URL.
+
+    True counts roughly correspond to keystrokes or on-focus events, and can be
+    used as a normalizer for per-user-gesture metrics.
+  </summary>
+</histogram>
+
 <histogram name="Omnibox.SuggestionUsed.AnswerInSuggest"
     enum="SuggestionAnswerOptionalType" expires_after="M83">
   <owner>chrome-android-omnibox-team@google.com</owner>
@@ -96971,7 +97019,7 @@
 
 <histogram
     name="PageLoad.DocumentTiming.NavigationToDOMContentLoadedEventFired"
-    units="ms" expires_after="2020-01-20">
+    units="ms" expires_after="2020-03-22">
   <owner>bmcquade@chromium.org</owner>
   <owner>csharrison@chromium.org</owner>
   <summary>
@@ -96980,7 +97028,8 @@
   </summary>
 </histogram>
 
-<histogram name="PageLoad.DocumentTiming.NavigationToFirstLayout" units="ms">
+<histogram name="PageLoad.DocumentTiming.NavigationToFirstLayout" units="ms"
+    expires_after="2020-03-22">
   <owner>bmcquade@chromium.org</owner>
   <owner>csharrison@chromium.org</owner>
   <summary>
@@ -96990,7 +97039,7 @@
 </histogram>
 
 <histogram name="PageLoad.DocumentTiming.NavigationToLoadEventFired" units="ms"
-    expires_after="2020-01-20">
+    expires_after="2020-03-22">
   <owner>bmcquade@chromium.org</owner>
   <owner>csharrison@chromium.org</owner>
   <summary>
@@ -97062,7 +97111,7 @@
 </histogram>
 
 <histogram base="true" name="PageLoad.Experimental.AbortTiming.Background"
-    units="ms" expires_after="2020-01-20">
+    units="ms" expires_after="2020-03-22">
   <owner>bmcquade@chromium.org</owner>
   <owner>csharrison@chromium.org</owner>
   <summary>
@@ -97086,7 +97135,7 @@
 </histogram>
 
 <histogram base="true" name="PageLoad.Experimental.AbortTiming.Close"
-    units="ms" expires_after="2020-01-20">
+    units="ms" expires_after="2020-03-22">
   <owner>csharrison@chromium.org</owner>
   <summary>
     This metric is still experimental and not yet ready to be relied upon.
@@ -97097,7 +97146,7 @@
 
 <histogram base="true"
     name="PageLoad.Experimental.AbortTiming.ForwardBackNavigation" units="ms"
-    expires_after="2020-01-20">
+    expires_after="2020-03-22">
   <owner>csharrison@chromium.org</owner>
   <summary>
     This metric is still experimental and not yet ready to be relied upon.
@@ -97107,7 +97156,7 @@
 </histogram>
 
 <histogram base="true" name="PageLoad.Experimental.AbortTiming.NewNavigation"
-    units="ms" expires_after="2020-01-20">
+    units="ms" expires_after="2020-03-22">
   <owner>csharrison@chromium.org</owner>
   <summary>
     This metric is still experimental and not yet ready to be relied upon.
@@ -97127,7 +97176,7 @@
 </histogram>
 
 <histogram base="true" name="PageLoad.Experimental.AbortTiming.Reload"
-    units="ms" expires_after="2020-01-20">
+    units="ms" expires_after="2020-03-22">
   <owner>csharrison@chromium.org</owner>
   <summary>
     This metric is still experimental and not yet ready to be relied upon.
@@ -97136,7 +97185,8 @@
   </summary>
 </histogram>
 
-<histogram base="true" name="PageLoad.Experimental.AbortTiming.Stop" units="ms">
+<histogram base="true" name="PageLoad.Experimental.AbortTiming.Stop" units="ms"
+    expires_after="2020-03-22">
   <owner>csharrison@chromium.org</owner>
   <summary>
     This metric is still experimental and not yet ready to be relied upon.
@@ -97169,7 +97219,8 @@
   </summary>
 </histogram>
 
-<histogram name="PageLoad.Experimental.Bytes.Network" units="KB">
+<histogram name="PageLoad.Experimental.Bytes.Network" units="KB"
+    expires_after="2020-03-22">
   <owner>jkarlin@chromium.org</owner>
   <summary>
     The number of prefiltered (e.g., compressed) response body KiloBytes loaded
@@ -97180,7 +97231,7 @@
 </histogram>
 
 <histogram name="PageLoad.Experimental.Bytes.NetworkIncludingHeaders"
-    units="KB">
+    units="KB" expires_after="2020-03-22">
   <owner>jkarlin@chromium.org</owner>
   <summary>
     The number of prefiltered (e.g., compressed) KiloBytes loaded over the
@@ -97380,7 +97431,7 @@
 </histogram>
 
 <histogram name="PageLoad.Experimental.NavigationToInteractive" units="ms"
-    expires_after="2020-01-20">
+    expires_after="2020-03-22">
   <owner>dproy@chromium.org</owner>
   <owner>tdresser@chromium.org</owner>
   <summary>
@@ -97499,7 +97550,7 @@
 
 <histogram
     name="PageLoad.Experimental.PaintTiming.ForegroundToFirstMeaningfulPaint"
-    units="ms">
+    units="ms" expires_after="2020-03-22">
   <owner>ksakamoto@chromium.org</owner>
   <summary>
     Measures the time from a background tab being switched to the foreground to
@@ -97587,7 +97638,7 @@
 
 <histogram
     name="PageLoad.Experimental.PaintTiming.NavigationToFirstMeaningfulPaint"
-    units="ms" expires_after="2020-01-20">
+    units="ms" expires_after="2020-03-22">
   <owner>ksakamoto@chromium.org</owner>
   <owner>speed-metrics-dev@chromium.org</owner>
   <summary>
@@ -97695,7 +97746,7 @@
 
 <histogram
     name="PageLoad.Experimental.PaintTiming.ParseStartToFirstMeaningfulPaint"
-    units="ms" expires_after="2020-01-20">
+    units="ms" expires_after="2020-03-22">
   <owner>ksakamoto@chromium.org</owner>
   <summary>
     Measures the time from when the HTML parser started, to the first meaningful
@@ -97760,7 +97811,7 @@
 </histogram>
 
 <histogram name="PageLoad.Experimental.TimeToInteractiveStatus"
-    enum="TimeToInteractiveStatus" expires_after="2020-01-20">
+    enum="TimeToInteractiveStatus" expires_after="2020-03-22">
   <owner>tdresser@chromium.org</owner>
   <owner>dproy@chromium.org</owner>
   <summary>
@@ -97978,7 +98029,7 @@
 </histogram>
 
 <histogram name="PageLoad.InputTiming.NavigationToFirstScroll.AfterPaint"
-    units="ms">
+    units="ms" expires_after="2020-03-22">
   <owner>tdresser@chromium.org</owner>
   <summary>
     Measures the time to first scroll input after the first paint.
@@ -98334,7 +98385,7 @@
 </histogram>
 
 <histogram name="PageLoad.Internal.ClientRedirect.NavigationWithoutPaint"
-    enum="Boolean">
+    enum="Boolean" expires_after="2020-03-22">
   <owner>bmcquade@chromium.org</owner>
   <summary>
     Counts how often a client-side redirect was initiated from a page that did
@@ -98376,7 +98427,8 @@
   </summary>
 </histogram>
 
-<histogram name="PageLoad.Internal.ErrorCode" enum="InternalErrorLoadEvent">
+<histogram name="PageLoad.Internal.ErrorCode" enum="InternalErrorLoadEvent"
+    expires_after="2020-03-22">
   <owner>csharrison@chromium.org</owner>
   <owner>bmcquade@chromium.org</owner>
   <summary>
@@ -98398,7 +98450,7 @@
 </histogram>
 
 <histogram name="PageLoad.Internal.NavigationStartedInForeground"
-    enum="BooleanForeground" expires_after="2020-01-20">
+    enum="BooleanForeground" expires_after="2020-03-22">
   <owner>bmcquade@chromium.org</owner>
   <summary>Whether a navigation started in the foreground.</summary>
 </histogram>
@@ -98432,7 +98484,7 @@
 </histogram>
 
 <histogram name="PageLoad.Internal.PageLoadTimingStatus"
-    enum="PageLoadTimingStatus">
+    enum="PageLoadTimingStatus" expires_after="2020-03-22">
   <owner>bmcquade@chromium.org</owner>
   <summary>
     The status of PageLoadTiming structs received from the render process over
@@ -98616,7 +98668,7 @@
 </histogram>
 
 <histogram name="PageLoad.Internal.Renderer.PresentationTime.DeltaFromSwapTime"
-    units="ms">
+    units="ms" expires_after="2020-03-22">
   <owner>sadrul@chromium.org</owner>
   <summary>
     'Swap time' is the timestamp of the renderer submitting a CompositorFrame,
@@ -98632,7 +98684,7 @@
 </histogram>
 
 <histogram name="PageLoad.Internal.Renderer.PresentationTime.Valid"
-    enum="Boolean">
+    enum="Boolean" expires_after="2020-03-22">
   <owner>sadrul@chromium.org</owner>
   <summary>
     This boolean keeps track of whether a valid presentation-timestamp was
@@ -98643,7 +98695,7 @@
 </histogram>
 
 <histogram name="PageLoad.LayoutInstability.CumulativeShiftScore"
-    units="scorex10">
+    units="scorex10" expires_after="2020-03-22">
   <owner>bmcquade@chromium.org</owner>
   <owner>skobes@chromium.org</owner>
   <summary>
@@ -98655,7 +98707,7 @@
 </histogram>
 
 <histogram name="PageLoad.LayoutInstability.CumulativeShiftScore.MainFrame"
-    units="scorex10">
+    units="scorex10" expires_after="2020-03-22">
   <owner>bmcquade@chromium.org</owner>
   <owner>skobes@chromium.org</owner>
   <summary>
@@ -98674,7 +98726,8 @@
   </summary>
 </histogram>
 
-<histogram name="PageLoad.PageTiming.ForegroundDuration" units="ms">
+<histogram name="PageLoad.PageTiming.ForegroundDuration" units="ms"
+    expires_after="2020-03-22">
   <owner>bmcquade@chromium.org</owner>
   <summary>
     For page loads that start in the foreground, measures the duration of time
@@ -98710,7 +98763,8 @@
   </summary>
 </histogram>
 
-<histogram name="PageLoad.PageTiming.NavigationToFirstForeground" units="ms">
+<histogram name="PageLoad.PageTiming.NavigationToFirstForeground" units="ms"
+    expires_after="2020-03-22">
   <owner>bmcquade@chromium.org</owner>
   <owner>csharrison@chromium.org</owner>
   <summary>
@@ -98789,7 +98843,7 @@
 </histogram>
 
 <histogram name="PageLoad.PaintTiming.NavigationToLargestContentfulPaint"
-    units="ms">
+    units="ms" expires_after="2020-03-22">
   <owner>maxlg@chromium.org</owner>
   <owner>speed-metrics-dev@chromium.org</owner>
   <summary>
@@ -98803,7 +98857,7 @@
 
 <histogram
     name="PageLoad.PaintTiming.NavigationToLargestContentfulPaint.MainFrame"
-    units="ms">
+    units="ms" expires_after="2020-03-22">
   <owner>maxlg@chromium.org</owner>
   <owner>speed-metrics-dev@chromium.org</owner>
   <summary>
@@ -98827,7 +98881,7 @@
 </histogram>
 
 <histogram name="PageLoad.ParseTiming.NavigationToParseStart" units="ms"
-    expires_after="2020-01-20">
+    expires_after="2020-03-22">
   <owner>bmcquade@chromium.org</owner>
   <owner>csharrison@chromium.org</owner>
   <summary>
@@ -98858,7 +98912,7 @@
 </histogram>
 
 <histogram name="PageLoad.ParseTiming.ParseBlockedOnScriptLoad" units="ms"
-    expires_after="2020-01-20">
+    expires_after="2020-03-22">
   <owner>bmcquade@chromium.org</owner>
   <owner>csharrison@chromium.org</owner>
   <summary>
@@ -99348,7 +99402,7 @@
 </histogram>
 
 <histogram name="PageSerialization.MhtmlLoading.LoadResult"
-    enum="MhtmlLoadResult">
+    enum="MhtmlLoadResult" expires_after="2020-03-22">
   <owner>iwells@chromium.org</owner>
   <owner>carlosk@chromium.org</owner>
   <summary>Reports the result of an attempt to load an MHTML archive.</summary>
@@ -99662,14 +99716,15 @@
 </histogram>
 
 <histogram name="PasswordBubble.DisplayDisposition"
-    enum="PasswordBubbleDisplayDisposition">
+    enum="PasswordBubbleDisplayDisposition" expires_after="2020-03-22">
   <owner>vasilii@chromium.org</owner>
   <summary>
     When the password management bubble opened, what state was it in?
   </summary>
 </histogram>
 
-<histogram name="PasswordGeneration.Event" enum="PasswordGenerationEvent">
+<histogram name="PasswordGeneration.Event" enum="PasswordGenerationEvent"
+    expires_after="2020-03-22">
   <owner>dvadym@chromium.org</owner>
   <owner>kolos@chromium.org</owner>
   <owner>vasilii@chromium.org</owner>
@@ -99686,7 +99741,7 @@
 </histogram>
 
 <histogram name="PasswordGeneration.GeneratedPasswordWasEdited"
-    enum="BooleanGeneratedPasswordWasEdited">
+    enum="BooleanGeneratedPasswordWasEdited" expires_after="2020-03-22">
   <owner>kolos@chromium.org</owner>
   <summary>
     Measures the frequency of user editing of generated passwords. Uploaded once
@@ -99724,7 +99779,7 @@
 </histogram>
 
 <histogram name="PasswordGeneration.SubmissionEvent"
-    enum="PasswordSubmissionEvent">
+    enum="PasswordSubmissionEvent" expires_after="2020-03-22">
   <owner>dvadym@chromium.org</owner>
   <owner>kolos@chromium.org</owner>
   <summary>
@@ -99748,7 +99803,7 @@
 </histogram>
 
 <histogram name="PasswordGeneration.UserDecision"
-    enum="PasswordGenerationUserEvent">
+    enum="PasswordGenerationUserEvent" expires_after="2020-03-22">
   <owner>ioanap@chromium.org</owner>
   <owner>vasilii@chromium.org</owner>
   <summary>
@@ -99877,7 +99932,8 @@
   </summary>
 </histogram>
 
-<histogram name="PasswordManager.AccountsPerSite" units="units">
+<histogram name="PasswordManager.AccountsPerSite" units="units"
+    expires_after="2020-03-22">
   <owner>dvadym@chromium.org</owner>
   <owner>vasilii@chromium.org</owner>
   <summary>
@@ -99921,7 +99977,7 @@
 </histogram>
 
 <histogram name="PasswordManager.ActionsTakenOnNonSecureForm"
-    enum="PasswordManagerActionsTakenV3">
+    enum="PasswordManagerActionsTakenV3" expires_after="2020-03-22">
   <owner>estark@chromium.org</owner>
   <summary>
     Stats documenting how we handle every form containing a password on a
@@ -99930,7 +99986,7 @@
 </histogram>
 
 <histogram name="PasswordManager.ActionsTakenV3"
-    enum="PasswordManagerActionsTakenV3">
+    enum="PasswordManagerActionsTakenV3" expires_after="2020-03-22">
   <owner>dvadym@chromium.org</owner>
   <owner>vasilii@chromium.org</owner>
   <summary>
@@ -100131,7 +100187,7 @@
 </histogram>
 
 <histogram name="PasswordManager.ApplySyncChanges.AddLoginSyncError"
-    enum="PasswordAddLoginSyncError" expires_after="2020-01-20">
+    enum="PasswordAddLoginSyncError" expires_after="2020-03-22">
   <owner>mamir@chromium.org</owner>
   <owner>mastiz@chromium.org</owner>
   <summary>
@@ -100153,7 +100209,7 @@
 </histogram>
 
 <histogram name="PasswordManager.ApplySyncChangesState"
-    enum="PasswordApplySyncChangesState" expires_after="2020-01-20">
+    enum="PasswordApplySyncChangesState" expires_after="2020-03-22">
   <owner>mamir@chromium.org</owner>
   <owner>mastiz@chromium.org</owner>
   <summary>
@@ -101225,7 +101281,7 @@
 </histogram>
 
 <histogram name="PasswordManager.MergeSyncData.AddLoginSyncError"
-    enum="PasswordAddLoginSyncError" expires_after="2020-01-20">
+    enum="PasswordAddLoginSyncError" expires_after="2020-03-22">
   <owner>mamir@chromium.org</owner>
   <owner>mastiz@chromium.org</owner>
   <summary>
@@ -101258,7 +101314,7 @@
 </histogram>
 
 <histogram name="PasswordManager.NewlySavedPasswordIsGenerated"
-    enum="BooleanNewlySavedPasswordIsGenerated">
+    enum="BooleanNewlySavedPasswordIsGenerated" expires_after="2020-03-22">
   <owner>nepper@chromium.org</owner>
   <owner>battre@chromium.org</owner>
   <owner>kolos@chromium.org</owner>
@@ -101387,7 +101443,7 @@
 </histogram>
 
 <histogram name="PasswordManager.PasswordDropdownItemSelected"
-    enum="PasswordDropdownSelectedOption" expires_after="2019-12-01">
+    enum="PasswordDropdownSelectedOption" expires_after="2020-03-22">
   <owner>dvadym@chromium.org</owner>
   <owner>vasilii@chromium.org</owner>
   <summary>
@@ -101396,7 +101452,7 @@
 </histogram>
 
 <histogram name="PasswordManager.PasswordDropdownShown"
-    enum="PasswordDropdownState" expires_after="2019-12-01">
+    enum="PasswordDropdownState" expires_after="2020-03-22">
   <owner>dvadym@chromium.org</owner>
   <owner>vasilii@chromium.org</owner>
   <summary>Logs the state of the password dropdown when it's shown.</summary>
@@ -101490,7 +101546,7 @@
 </histogram>
 
 <histogram name="PasswordManager.PasswordSavedWithManualFallback"
-    enum="BooleanPasswordSavedWithFallback">
+    enum="BooleanPasswordSavedWithFallback" expires_after="2020-03-22">
   <owner>kolos@chromium.org</owner>
   <summary>
     Measures whether users save passwords with automatic prompt or manual
@@ -101507,7 +101563,7 @@
 </histogram>
 
 <histogram name="PasswordManager.PasswordSyncState" enum="PasswordSyncState"
-    expires_after="2020-01-20">
+    expires_after="2020-03-22">
   <owner>dvadym@chromium.org</owner>
   <owner>vasilii@chromium.org</owner>
   <summary>
@@ -101613,7 +101669,7 @@
 </histogram>
 
 <histogram name="PasswordManager.RequirementsSpecFetcher.NetErrorCode"
-    enum="NetErrorCodes">
+    enum="NetErrorCodes" expires_after="2020-03-22">
   <owner>battre@chromium.org</owner>
   <summary>
     Network error code of fetching a password requirements file.
@@ -101704,7 +101760,7 @@
 </histogram>
 
 <histogram name="PasswordManager.SaveUIDismissalReason"
-    enum="PasswordManagerUIDismissalReason" expires_after="2020-01-20">
+    enum="PasswordManagerUIDismissalReason" expires_after="2020-03-22">
   <owner>vasilii@chromium.org</owner>
   <summary>Why was the save password UI (bubble or infobar) closed?</summary>
 </histogram>
@@ -102119,7 +102175,7 @@
 </histogram>
 
 <histogram name="PasswordManager.SyncPasswordHashChange"
-    enum="GaiaPasswordHashChange">
+    enum="GaiaPasswordHashChange" expires_after="2020-03-22">
   <owner>dvadym@chromium.org</owner>
   <owner>chrome-safebrowsing-alerts@google.com</owner>
   <summary>
@@ -102189,7 +102245,7 @@
 </histogram>
 
 <histogram base="true" name="PasswordManager.TotalAccountsHiRes.ByType"
-    units="units">
+    units="units" expires_after="2020-03-22">
   <owner>battre@chromium.org</owner>
   <owner>vasilii@chromium.org</owner>
   <summary>
@@ -102200,7 +102256,7 @@
 </histogram>
 
 <histogram base="true" name="PasswordManager.TotalAccountsHiRes.WithScheme"
-    units="accounts">
+    units="accounts" expires_after="2020-03-22">
   <owner>engedy@chromium.org</owner>
   <owner>vasilii@chromium.org</owner>
   <summary>
@@ -102213,7 +102269,7 @@
 </histogram>
 
 <histogram name="PasswordManager.UIDismissalReason"
-    enum="PasswordManagerUIDismissalReason">
+    enum="PasswordManagerUIDismissalReason" expires_after="2020-03-22">
   <owner>vasilii@chromium.org</owner>
   <summary>
     Why was the password manager's UI (bubble or infobar) closed? Save and
@@ -102241,7 +102297,7 @@
 </histogram>
 
 <histogram name="PasswordManager.UpdateUIDismissalReason"
-    enum="PasswordManagerUIDismissalReason">
+    enum="PasswordManagerUIDismissalReason" expires_after="2020-03-22">
   <owner>vasilii@chromium.org</owner>
   <summary>Why was the update password UI (bubble or infobar) closed?</summary>
 </histogram>
@@ -102440,7 +102496,7 @@
 </histogram>
 
 <histogram name="PasswordProtection.PasswordProtectionResponseOrErrorCode"
-    enum="CombinedHttpResponseAndNetErrorCode">
+    enum="CombinedHttpResponseAndNetErrorCode" expires_after="2020-03-22">
   <owner>vakh@chromium.org</owner>
   <owner>chrome-safebrowsing-alerts@google.com</owner>
   <summary>
@@ -102462,7 +102518,7 @@
 </histogram>
 
 <histogram name="PasswordProtection.PasswordReuseSyncAccountType"
-    enum="PasswordProtectionSyncAccountType">
+    enum="PasswordProtectionSyncAccountType" expires_after="2020-03-22">
   <owner>vakh@chromium.org</owner>
   <owner>chrome-safebrowsing-alerts@google.com</owner>
   <summary>
@@ -102485,7 +102541,8 @@
   </summary>
 </histogram>
 
-<histogram name="PasswordProtection.RequestNetworkDuration" units="ms">
+<histogram name="PasswordProtection.RequestNetworkDuration" units="ms"
+    expires_after="2020-03-22">
   <owner>vakh@chromium.org</owner>
   <owner>chrome-safebrowsing-alerts@google.com</owner>
   <summary>
@@ -102495,7 +102552,7 @@
 </histogram>
 
 <histogram base="true" name="PasswordProtection.RequestOutcome"
-    enum="PasswordProtectionRequestOutcome">
+    enum="PasswordProtectionRequestOutcome" expires_after="2020-03-22">
   <owner>vakh@chromium.org</owner>
   <owner>chrome-safebrowsing-alerts@google.com</owner>
   <summary>
@@ -102530,7 +102587,8 @@
   </summary>
 </histogram>
 
-<histogram name="PasswordProtection.Verdict" enum="PasswordProtectionVerdict">
+<histogram name="PasswordProtection.Verdict" enum="PasswordProtectionVerdict"
+    expires_after="2020-03-22">
   <owner>vakh@chromium.org</owner>
   <owner>chrome-safebrowsing-alerts@google.com</owner>
   <summary>
@@ -102637,7 +102695,7 @@
 </histogram>
 
 <histogram name="PaymentRequest.CheckoutFunnel.NoShow"
-    enum="PaymentRequestNoShowReason" expires_after="2020-01-20">
+    enum="PaymentRequestNoShowReason" expires_after="2020-03-22">
   <owner>sebsg@chromium.org</owner>
   <summary>
     The reason that leads to the Payment Request not being shown to the user.
@@ -102689,7 +102747,8 @@
   </summary>
 </histogram>
 
-<histogram name="PaymentRequest.Events" units="bitfield value">
+<histogram name="PaymentRequest.Events" units="bitfield value"
+    expires_after="2020-03-22">
   <owner>sebsg@chromium.org</owner>
   <summary>
     A bitfield representing the different events that happened during the
@@ -102697,7 +102756,8 @@
   </summary>
 </histogram>
 
-<histogram name="PaymentRequest.JourneyLoggerHasRecorded" enum="Boolean">
+<histogram name="PaymentRequest.JourneyLoggerHasRecorded" enum="Boolean"
+    expires_after="2020-03-22">
   <owner>sahel@chromium.org</owner>
   <summary>
     Whether a journey logger has recorded an events bit field or not.
@@ -102713,7 +102773,7 @@
 </histogram>
 
 <histogram name="PaymentRequest.MissingContactFields"
-    enum="PaymentRequestMissingContactFields">
+    enum="PaymentRequestMissingContactFields" expires_after="2020-03-22">
   <owner>sahel@chromium.org</owner>
   <summary>
     A bitfield representing different missing fields of the contact section in
@@ -102725,7 +102785,7 @@
 </histogram>
 
 <histogram name="PaymentRequest.MissingPaymentFields"
-    enum="PaymentRequestMissingPaymentFields">
+    enum="PaymentRequestMissingPaymentFields" expires_after="2020-03-22">
   <owner>sahel@chromium.org</owner>
   <summary>
     A bitfield representing different missing fields of the payment info section
@@ -102737,7 +102797,7 @@
 </histogram>
 
 <histogram name="PaymentRequest.MissingShippingFields"
-    enum="PaymentRequestMissingShippingFields">
+    enum="PaymentRequestMissingShippingFields" expires_after="2020-03-22">
   <owner>sahel@chromium.org</owner>
   <summary>
     A bitfield representing different missing fields of the shipping section in
@@ -102748,28 +102808,32 @@
   </summary>
 </histogram>
 
-<histogram name="PaymentRequest.NumberOfSelectionAdds" units="units">
+<histogram name="PaymentRequest.NumberOfSelectionAdds" units="units"
+    expires_after="2020-03-22">
   <owner>sebsg@chromium.org</owner>
   <summary>
     The number of times the user added an entry during a Payment Request.
   </summary>
 </histogram>
 
-<histogram name="PaymentRequest.NumberOfSelectionChanges" units="units">
+<histogram name="PaymentRequest.NumberOfSelectionChanges" units="units"
+    expires_after="2020-03-22">
   <owner>sebsg@chromium.org</owner>
   <summary>
     The number of times the user changed an entry during a Payment Request.
   </summary>
 </histogram>
 
-<histogram name="PaymentRequest.NumberOfSelectionEdits" units="units">
+<histogram name="PaymentRequest.NumberOfSelectionEdits" units="units"
+    expires_after="2020-03-22">
   <owner>sebsg@chromium.org</owner>
   <summary>
     The number of times the user edited an entry during a Payment Request.
   </summary>
 </histogram>
 
-<histogram name="PaymentRequest.NumberOfSuggestionsShown" units="units">
+<histogram name="PaymentRequest.NumberOfSuggestionsShown" units="units"
+    expires_after="2020-03-22">
   <owner>sebsg@chromium.org</owner>
   <summary>
     The number of suggestions shown to the user during a payment request.
@@ -102805,14 +102869,16 @@
   <summary>True when a service worker times out 5 mins after request.</summary>
 </histogram>
 
-<histogram name="PaymentRequest.TimeToCheckout.Completed" units="ms">
+<histogram name="PaymentRequest.TimeToCheckout.Completed" units="ms"
+    expires_after="2020-03-22">
   <owner>sahel@chromium.org</owner>
   <summary>
     Records the time between a payment request .show() and its completion.
   </summary>
 </histogram>
 
-<histogram name="PaymentRequest.TimeToCheckout.Completed.Shown" units="ms">
+<histogram name="PaymentRequest.TimeToCheckout.Completed.Shown" units="ms"
+    expires_after="2020-03-22">
 <!-- Name completed by histogram_suffixes name="PaymentRequestCompletedInstrument" -->
 
   <owner>sahel@chromium.org</owner>
@@ -102823,7 +102889,7 @@
 </histogram>
 
 <histogram name="PaymentRequest.TimeToCheckout.Completed.SkippedShow"
-    units="ms">
+    units="ms" expires_after="2020-03-22">
 <!-- Name completed by histogram_suffixes name="PaymentRequestCompletedInstrument" -->
 
   <owner>sahel@chromium.org</owner>
@@ -102833,7 +102899,8 @@
   </summary>
 </histogram>
 
-<histogram name="PaymentRequest.TimeToCheckout.OtherAborted" units="ms">
+<histogram name="PaymentRequest.TimeToCheckout.OtherAborted" units="ms"
+    expires_after="2020-03-22">
   <owner>sahel@chromium.org</owner>
   <summary>
     Records the time between a payment request .show() and its termination by
@@ -102841,7 +102908,8 @@
   </summary>
 </histogram>
 
-<histogram name="PaymentRequest.TimeToCheckout.UserAborted" units="ms">
+<histogram name="PaymentRequest.TimeToCheckout.UserAborted" units="ms"
+    expires_after="2020-03-22">
 <!-- Name completed by histogram_suffixes name="PaymentRequestPaymentSheetShowStatus" -->
 
   <owner>sahel@chromium.org</owner>
@@ -102852,7 +102920,7 @@
 </histogram>
 
 <histogram name="PaymentRequest.TransactionAmount.Completed"
-    enum="PaymentRequestTransactionSize">
+    enum="PaymentRequestTransactionSize" expires_after="2020-03-22">
   <owner>sahel@chromium.org</owner>
   <summary>
     Records the transaction amounts completed using payment request API after
@@ -102861,7 +102929,7 @@
 </histogram>
 
 <histogram name="PaymentRequest.TransactionAmount.Triggered"
-    enum="PaymentRequestTransactionSize">
+    enum="PaymentRequestTransactionSize" expires_after="2020-03-22">
   <owner>sahel@chromium.org</owner>
   <summary>
     Records the transaction amounts triggered using payment request API after
@@ -102946,7 +103014,7 @@
 </histogram>
 
 <histogram name="PDF.Actions" enum="ChromePDFViewerActions"
-    expires_after="2020-01-20">
+    expires_after="2020-03-22">
   <owner>hnakashima@chromium.org</owner>
   <summary>
     Tracks user actions in the PDF viewer. Logged when the document is opened
@@ -107690,7 +107758,7 @@
 </histogram>
 
 <histogram name="Plugin.Flash.TinyContentSize" enum="FlashTinyContentSize"
-    expires_after="2020-01-20">
+    expires_after="2020-03-22">
   <owner>tommycli@chromium.org</owner>
   <summary>
     Collects the sizes of all loaded Flash plugin instances. This is for
@@ -107717,7 +107785,7 @@
 </histogram>
 
 <histogram name="Plugin.FlashUsage" enum="FlashUsage"
-    expires_after="2020-01-20">
+    expires_after="2020-03-22">
   <owner>yzshen@chromium.org</owner>
   <owner>thestig@chromium.org</owner>
   <summary>Collects Flash usage data.</summary>
@@ -107985,7 +108053,7 @@
 </histogram>
 
 <histogram name="Power.BatteryChargeHealth" units="%"
-    expires_after="2020-01-20">
+    expires_after="2020-03-22">
   <owner>tbroch@chromium.org</owner>
   <summary>
     Chrome OS battery charge health percentage. Sampled once when device starts
@@ -108017,7 +108085,7 @@
 </histogram>
 
 <histogram name="Power.BatteryDischargeRate" units="mW"
-    expires_after="2020-01-20">
+    expires_after="2020-03-22">
   <owner>tbroch@chromium.org</owner>
   <summary>
     Chrome OS battery discharge rate in mW sampled every 30 seconds while the
@@ -108083,7 +108151,7 @@
 </histogram>
 
 <histogram name="Power.BatteryDischargeRateWhileSuspended" units="mW"
-    expires_after="2020-01-20">
+    expires_after="2020-03-22">
   <owner>tbroch@chromium.org</owner>
   <summary>
     Chrome OS battery discharge rate in mW while the system was suspended,
@@ -108103,7 +108171,7 @@
   </summary>
 </histogram>
 
-<histogram name="Power.BatteryPercentDrop" units="%" expires_after="2020-01-20">
+<histogram name="Power.BatteryPercentDrop" units="%" expires_after="2020-03-22">
   <owner>ryansturm@chromium.org</owner>
   <owner>tbansal@chromium.org</owner>
   <summary>
@@ -108167,7 +108235,7 @@
 </histogram>
 
 <histogram name="Power.BatteryRemainingWhenChargeStarts" units="%"
-    expires_after="2020-01-20">
+    expires_after="2020-03-22">
   <owner>tbroch@chromium.org</owner>
   <summary>
     Chrome OS remaining battery charge as percent of the maximum battery charge,
@@ -108247,7 +108315,7 @@
 </histogram>
 
 <histogram name="Power.ConnectedChargingPorts"
-    enum="PowerConnectedChargingPorts" expires_after="2020-01-20">
+    enum="PowerConnectedChargingPorts" expires_after="2020-03-22">
   <owner>bleung@chromium.org</owner>
   <owner>tbroch@chromium.org</owner>
   <summary>
@@ -108414,7 +108482,7 @@
 </histogram>
 
 <histogram name="Power.IdleSuspendCountDaily" units="count"
-    expires_after="2020-01-20">
+    expires_after="2020-03-22">
   <owner>tbroch@chromium.org</owner>
   <owner>jiameng@chromium.org</owner>
   <summary>
@@ -108512,7 +108580,7 @@
 </histogram>
 
 <histogram name="Power.KernelSuspendTimeOnBattery" units="ms"
-    expires_after="2020-01-20">
+    expires_after="2020-03-22">
   <owner>tbroch@chromium.org</owner>
   <summary>
     The time that the kernel took to suspend-to-RAM the Chrome OS device when
@@ -108540,7 +108608,7 @@
 </histogram>
 
 <histogram name="Power.LidClosedSuspendCountDaily" units="count"
-    expires_after="2020-01-20">
+    expires_after="2020-03-22">
   <owner>tbroch@chromium.org</owner>
   <summary>
     Number of times that that the system has suspended in response to its lid
@@ -108733,7 +108801,7 @@
 </histogram>
 
 <histogram name="Power.PowerSupplyMaxPower" units="W"
-    expires_after="2020-01-20">
+    expires_after="2020-03-22">
   <owner>bleung@chromium.org</owner>
   <owner>tbroch@chromium.org</owner>
   <summary>
@@ -108785,7 +108853,7 @@
 </histogram>
 
 <histogram name="Power.SuspendAttempt" enum="SuspendAttempt"
-    expires_after="2020-01-20">
+    expires_after="2020-03-22">
   <owner>tbroch@chromium.org</owner>
   <summary>
     The number of suspend attempts on Chrome OS. Samples are reported before
@@ -108795,7 +108863,7 @@
 </histogram>
 
 <histogram name="Power.SuspendAttemptsBeforeCancel" units="units"
-    expires_after="2020-01-20">
+    expires_after="2020-03-22">
   <owner>tbroch@chromium.org</owner>
   <summary>
     The number of suspend attempts performed for a single suspend request (e.g.
@@ -108806,7 +108874,7 @@
 </histogram>
 
 <histogram name="Power.SuspendAttemptsBeforeSuccess" units="units"
-    expires_after="2020-01-20">
+    expires_after="2020-03-22">
   <owner>tbroch@chromium.org</owner>
   <summary>
     The number of suspend attempts performed for a single suspend request (e.g.
@@ -108816,7 +108884,7 @@
 </histogram>
 
 <histogram name="Power.SuspendResult" enum="SuspendResult"
-    expires_after="2020-01-20">
+    expires_after="2020-03-22">
   <owner>tbroch@chromium.org</owner>
   <summary>
     The results of suspend attempts on Chrome OS. Samples are reported after
@@ -108860,7 +108928,7 @@
 </histogram>
 
 <histogram name="Power.TimeInSuspendAtBoot" units="minutes"
-    expires_after="2020-01-20">
+    expires_after="2020-03-22">
   <owner>tbroch@chromium.org</owner>
   <summary>
     Chrome OS time in minutes spent in suspend-to-RAM mode sampled at boot
@@ -108869,7 +108937,7 @@
 </histogram>
 
 <histogram name="Power.TimeInSuspendAtResume" units="minutes"
-    expires_after="2020-01-20">
+    expires_after="2020-03-22">
   <owner>tbroch@chromium.org</owner>
   <summary>
     Chrome OS time in minutes spent in suspend-to-RAM mode sampled at resume.
@@ -110806,7 +110874,7 @@
 </histogram>
 
 <histogram name="Previews.DeferAllScript.DenyListMatch" enum="BooleanDetected"
-    expires_after="2020-01-20">
+    expires_after="2020-03-22">
   <owner>tbansal@chromium.org</owner>
   <owner>dougarnett@chromium.org</owner>
   <summary>
@@ -110816,7 +110884,7 @@
 </histogram>
 
 <histogram name="Previews.DeferAllScript.RedirectLoopDetectedUsingCache"
-    enum="BooleanDetected" expires_after="2020-01-20">
+    enum="BooleanDetected" expires_after="2020-03-22">
   <owner>tbansal@chromium.org</owner>
   <owner>dougarnett@chromium.org</owner>
   <summary>
@@ -110826,7 +110894,7 @@
 </histogram>
 
 <histogram name="Previews.EligibilityReason" enum="PreviewsEligibilityReason"
-    expires_after="2020-01-20">
+    expires_after="2020-03-22">
   <owner>ryansturm@chromium.org</owner>
   <summary>
     When evaluating whether to show a user a preview, the preview might be
@@ -110990,9 +111058,9 @@
 </histogram>
 
 <histogram name="Previews.Offline.CommittedErrorPage" enum="BooleanError"
-    expires_after="M80">
-  <owner>harrisonsean@chromium.org</owner>
+    expires_after="M90">
   <owner>robertogden@chromium.org</owner>
+  <owner>tbansal@chromium.org</owner>
   <summary>
     Whether or not an offline preview that was committed showed an error page.
     This metric is recorded every time an offline preview is committed.
@@ -111024,7 +111092,7 @@
 </histogram>
 
 <histogram name="Previews.OmniboxAction" enum="PreviewsUserOmniboxAction"
-    expires_after="2020-01-20">
+    expires_after="2020-03-22">
   <owner>robertogden@chromium.org</owner>
   <summary>User interactions with the Previews Android Omnibox UI.</summary>
 </histogram>
@@ -111174,7 +111242,7 @@
 </histogram>
 
 <histogram name="Previews.OptOut.UserOptedOut" enum="PreviewsUserOptedOut"
-    expires_after="2020-01-20">
+    expires_after="2020-03-22">
   <owner>ryansturm@chromium.org</owner>
   <summary>
     Whether the user chose to reload the original page when shown a preview.
@@ -111189,7 +111257,7 @@
 </histogram>
 
 <histogram name="Previews.PageEndReason" enum="PageEndReason"
-    expires_after="2020-01-20">
+    expires_after="2020-03-22">
   <owner>robertogden@chromium.org</owner>
   <summary>Records why the page load ended on a given preview type.</summary>
 </histogram>
@@ -111239,7 +111307,7 @@
 </histogram>
 
 <histogram name="Previews.ServerLitePage.IneligibleReasons"
-    enum="PreviewsServerLitePageIneligibleReason" expires_after="2020-01-20">
+    enum="PreviewsServerLitePageIneligibleReason" expires_after="2020-03-22">
   <owner>robertogden@chromium.org</owner>
   <summary>
     The reasons that a navigation is not eligible to be shown a server lite page
@@ -111367,7 +111435,7 @@
 </histogram>
 
 <histogram name="Previews.ServerLitePage.ServerResponse"
-    enum="PreviewsServerLitePageServerResponse" expires_after="2020-01-20">
+    enum="PreviewsServerLitePageServerResponse" expires_after="2020-03-22">
   <owner>robertogden@chromium.org</owner>
   <summary>
     The type of response given by the previews server when a server lite page
@@ -112099,7 +112167,7 @@
 </histogram>
 
 <histogram name="PrintPreview.UserAction" enum="PrintPreviewUserActionType"
-    expires_after="2020-01-20">
+    expires_after="2020-03-22">
   <owner>thestig@chromium.org</owner>
   <summary>
     Action taken by the user in the preview tab such as print, cancel, print to
@@ -112631,7 +112699,7 @@
 </histogram>
 
 <histogram name="Profile.NumberOfProfiles" units="units"
-    expires_after="2020-01-20">
+    expires_after="2020-03-22">
   <owner>rogerta@chromium.org</owner>
   <summary>
     Counts the number of profiles on a user's machine at least every 24 hours
@@ -113810,7 +113878,7 @@
 </histogram>
 
 <histogram name="Quota.AvailableDiskSpace" units="MB"
-    expires_after="2020-01-20">
+    expires_after="2020-03-22">
   <owner>jarrydg@chromium.org</owner>
   <summary>
     Amount of free disk space for the storage directory. Logged at irregular
@@ -113839,7 +113907,7 @@
   </summary>
 </histogram>
 
-<histogram name="Quota.DiskspaceShortage" units="MB" expires_after="2020-01-20">
+<histogram name="Quota.DiskspaceShortage" units="MB" expires_after="2020-03-22">
   <owner>jarrydg@chromium.org</owner>
   <summary>
     Difference between acceptable lower limit of diskspace and actual free
@@ -113913,7 +113981,7 @@
 </histogram>
 
 <histogram name="Quota.EvictionRoundsPerHour" units="units"
-    expires_after="2020-01-20">
+    expires_after="2020-03-22">
   <owner>jarrydg@chromium.org</owner>
   <summary>Number of eviction rounds in an hour.</summary>
 </histogram>
@@ -113940,7 +114008,7 @@
 </histogram>
 
 <histogram name="Quota.GlobalUsageOfTemporaryStorage" units="MB"
-    expires_after="2020-01-20">
+    expires_after="2020-03-22">
   <owner>jarrydg@chromium.org</owner>
   <summary>Global usage of temporary storage.</summary>
 </histogram>
@@ -114035,7 +114103,7 @@
 </histogram>
 
 <histogram name="Quota.PercentDiskAvailable" units="%"
-    expires_after="2020-01-20">
+    expires_after="2020-03-22">
   <owner>jarrydg@chromium.org</owner>
   <summary>
     Percentage of the storage device that is free. Logged at irregular
@@ -114062,7 +114130,7 @@
 </histogram>
 
 <histogram name="Quota.PercentUsedForTemporaryStorage2" units="%"
-    expires_after="2020-01-20">
+    expires_after="2020-03-22">
   <owner>jarrydg@chromium.org</owner>
   <summary>
     Percentage of the storage device that is being use for temporary storage.
@@ -114143,7 +114211,7 @@
   </summary>
 </histogram>
 
-<histogram name="Quota.TotalDiskSpace" units="MB" expires_after="2020-01-20">
+<histogram name="Quota.TotalDiskSpace" units="MB" expires_after="2020-03-22">
   <owner>jarrydg@chromium.org</owner>
   <summary>
     Total disk space for the storage directory. Logged at irregular intervals.
@@ -137230,7 +137298,7 @@
 </histogram>
 
 <histogram name="Stability.Android.ProcessedCrashCounts"
-    enum="AndroidProcessedCrashCounts" expires_after="2020-01-20">
+    enum="AndroidProcessedCrashCounts" expires_after="2020-03-22">
   <owner>boliu@chromium.org</owner>
   <summary>
     Individual enum counts specific conditions of child process terminations.
@@ -137287,7 +137355,7 @@
 </histogram>
 
 <histogram name="Stability.Android.RendererCrash" enum="Boolean"
-    expires_after="2020-01-20">
+    expires_after="2020-03-22">
   <owner>wnwen@chromium.org</owner>
   <summary>
     Counts renderer crashes including OOMs. Android only. Mirrors old stability
@@ -137332,7 +137400,7 @@
 </histogram>
 
 <histogram name="Stability.BadMessageTerminated.Content"
-    enum="BadMessageReasonContent" expires_after="2020-01-20">
+    enum="BadMessageReasonContent" expires_after="2020-03-22">
   <owner>jam@chromium.org</owner>
   <owner>jamescook@chromium.org</owner>
   <summary>
@@ -137392,7 +137460,7 @@
 </histogram>
 
 <histogram name="Stability.ChildFrameCrash.ShownAfterCrashingReason"
-    enum="ShownAfterCrashingReason" expires_after="2020-01-20">
+    enum="ShownAfterCrashingReason" expires_after="2020-03-22">
   <owner>alexmos@chromium.org</owner>
   <owner>boliu@chromium.org</owner>
   <owner>lukasza@chromium.org</owner>
@@ -137425,7 +137493,7 @@
 </histogram>
 
 <histogram name="Stability.ChildFrameCrash.Visibility" enum="CrashVisibility"
-    expires_after="2020-01-20">
+    expires_after="2020-03-22">
   <owner>boliu@chromium.org</owner>
   <owner>lfg@chromium.org</owner>
   <owner>lukasza@chromium.org</owner>
@@ -137891,7 +137959,7 @@
 </histogram>
 
 <histogram base="true" name="Startup.Android.Cold.TimeToFirstContentfulPaint"
-    units="ms" expires_after="2020-01-20">
+    units="ms" expires_after="2020-03-22">
   <owner>pasko@chromium.org</owner>
   <owner>alexilin@chromium.org</owner>
   <summary>
@@ -137904,7 +137972,7 @@
 </histogram>
 
 <histogram base="true" name="Startup.Android.Cold.TimeToFirstNavigationCommit"
-    units="ms" expires_after="2020-01-20">
+    units="ms" expires_after="2020-03-22">
   <owner>pasko@chromium.org</owner>
   <owner>alexilin@chromium.org</owner>
   <summary>
@@ -139533,7 +139601,7 @@
 </histogram>
 
 <histogram name="Style.InvalidationTime" units="microseconds"
-    expires_after="2020-01-20">
+    expires_after="2020-03-22">
   <owner>futhark@chromium.org</owner>
   <summary>
     Microseconds spent in StyleEngine::InvalidateStyle. Only samples from high
@@ -139557,7 +139625,7 @@
 </histogram>
 
 <histogram name="Style.RebuildLayoutTreeTime" units="microseconds"
-    expires_after="2020-01-20">
+    expires_after="2020-03-22">
   <owner>futhark@chromium.org</owner>
   <summary>
     Microseconds spent in RebuildLayoutTree called from Document::UpdateStyle.
@@ -139565,7 +139633,7 @@
 </histogram>
 
 <histogram name="Style.RecalcTime" units="microseconds"
-    expires_after="2020-01-20">
+    expires_after="2020-03-22">
   <owner>futhark@chromium.org</owner>
   <summary>
     Microseconds spent in RecalcStyle called from Document::UpdateStyle.
@@ -140387,8 +140455,7 @@
 </histogram>
 
 <histogram name="SubresourceRedirect.CompressionAttempt.ResponseCode"
-    enum="HttpResponseCode" expires_after="M80">
-  <owner>harrisonsean@chromium.org</owner>
+    enum="HttpResponseCode" expires_after="M90">
   <owner>robertogden@chromium.org</owner>
   <owner>tbansal@chromium.org</owner>
   <summary>
@@ -140400,8 +140467,7 @@
 </histogram>
 
 <histogram name="SubresourceRedirect.CompressionAttempt.ServerResponded"
-    enum="Boolean" expires_after="M80">
-  <owner>harrisonsean@chromium.org</owner>
+    enum="Boolean" expires_after="M90">
   <owner>robertogden@chromium.org</owner>
   <owner>tbansal@chromium.org</owner>
   <summary>
@@ -140412,8 +140478,7 @@
 </histogram>
 
 <histogram name="SubresourceRedirect.DidCompress.BytesSaved" units="bytes"
-    expires_after="M80">
-  <owner>harrisonsean@chromium.org</owner>
+    expires_after="M90">
   <owner>robertogden@chromium.org</owner>
   <owner>tbansal@chromium.org</owner>
   <summary>
@@ -140423,8 +140488,7 @@
 </histogram>
 
 <histogram name="SubresourceRedirect.DidCompress.CompressionPercent" units="%"
-    expires_after="M80">
-  <owner>harrisonsean@chromium.org</owner>
+    expires_after="M90">
   <owner>robertogden@chromium.org</owner>
   <owner>tbansal@chromium.org</owner>
   <summary>
@@ -141027,7 +141091,7 @@
 </histogram>
 
 <histogram name="Sync.ConfigureDataTypes" enum="SyncModelTypes"
-    expires_after="2020-01-20">
+    expires_after="2020-03-22">
   <owner>zea@chromium.org</owner>
   <summary>
     Breakdown of sync data types being configured at first time signin, restart,
@@ -142002,7 +142066,7 @@
 </histogram>
 
 <histogram name="Sync.InitialState" enum="SyncInitialState"
-    expires_after="2020-01-20">
+    expires_after="2020-03-22">
   <owner>treib@chromium.org</owner>
   <owner>mastiz@chromium.org</owner>
   <summary>
@@ -142703,7 +142767,7 @@
 </histogram>
 
 <histogram name="Sync.PostedClientToServerMessage"
-    enum="SyncClientToServerMessageContents" expires_after="2020-01-20">
+    enum="SyncClientToServerMessageContents" expires_after="2020-03-22">
   <owner>mastiz@chromium.org</owner>
   <summary>
     Number of network requests issued by sync to the sync server, grouped by
@@ -142712,7 +142776,7 @@
 </histogram>
 
 <histogram name="Sync.PostedClientToServerMessageError" enum="SyncErrorType"
-    expires_after="2020-01-20">
+    expires_after="2020-03-22">
   <owner>mastiz@chromium.org</owner>
   <summary>
     A sync error code received from the sync server as a result of a
@@ -143350,7 +143414,7 @@
 </histogram>
 
 <histogram name="Sync.SyncEverything2" enum="Boolean"
-    expires_after="2020-01-20">
+    expires_after="2020-03-22">
   <owner>treib@chromium.org</owner>
   <summary>
     Boolean histogram for whether the &quot;Sync Everything&quot; option was
@@ -147888,7 +147952,7 @@
 </histogram>
 
 <histogram name="Translate.CompactInfobar.Event" enum="TranslateCompactUIEvent"
-    expires_after="2020-01-20">
+    expires_after="2020-03-22">
   <owner>anthonyvd@chromium.org</owner>
   <summary>Various user actions performed in the translate infobar.</summary>
 </histogram>
@@ -148358,7 +148422,7 @@
 </histogram>
 
 <histogram name="Translate.SourceLanguage" enum="CLD3LanguageCode"
-    expires_after="2020-01-20">
+    expires_after="2020-03-22">
   <owner>yyushkina@google.com</owner>
   <summary>
     The number of requests sent to the Translate server, grouped by source
@@ -149465,7 +149529,7 @@
   </summary>
 </histogram>
 
-<histogram name="UMA.LogSize.OnSuccess" units="KB" expires_after="2020-01-20">
+<histogram name="UMA.LogSize.OnSuccess" units="KB" expires_after="2020-03-22">
   <owner>asvitkine@chromium.org</owner>
   <owner>src/base/metrics/OWNERS</owner>
   <summary>
@@ -149865,7 +149929,7 @@
 </histogram>
 
 <histogram name="UMA.TruncatedEvents.UserAction" units="events"
-    expires_after="2020-03-15">
+    expires_after="2020-03-22">
   <owner>rkaplow@chromium.org</owner>
   <owner>src/base/metrics/OWNERS</owner>
   <summary>
@@ -151060,7 +151124,7 @@
 </histogram>
 
 <histogram name="UserManager.LoginUserType" enum="UserType"
-    expires_after="2020-01-20">
+    expires_after="2020-03-22">
   <owner>achuith@chromium.org</owner>
   <summary>
     The number of users of different types that log in to the system (Chrome
@@ -151622,7 +151686,7 @@
   </summary>
 </histogram>
 
-<histogram name="V8.Execute" units="ms" expires_after="2020-01-20">
+<histogram name="V8.Execute" units="ms" expires_after="2020-03-22">
   <owner>rmcilroy@chromium.org</owner>
   <summary>
     Time spent in JavaScript Execution, including runtime calls, callbacks, and
@@ -152217,7 +152281,7 @@
 </histogram>
 
 <histogram name="V8.MemoryHeapSampleTotalCommitted" units="KB"
-    expires_after="2020-01-20">
+    expires_after="2020-03-22">
   <owner>hpayer@chromium.org</owner>
   <summary>
     The total size of committed memory used by V8 after each GC in KB.
@@ -152225,7 +152289,7 @@
 </histogram>
 
 <histogram name="V8.MemoryHeapSampleTotalUsed" units="KB"
-    expires_after="2020-01-20">
+    expires_after="2020-03-22">
   <owner>hpayer@chromium.org</owner>
   <summary>
     The total size of live memory used by V8 after each GC in KB.
@@ -154738,7 +154802,7 @@
 </histogram>
 
 <histogram name="WebApk.Session.TotalDuration2" units="ms"
-    expires_after="2020-01-20">
+    expires_after="2020-03-22">
   <owner>hanxi@chromium.org</owner>
   <owner>pkotwicz@chromium.org</owner>
   <owner>yfriedman@chromium.org</owner>
@@ -154763,7 +154827,7 @@
 </histogram>
 
 <histogram base="true" name="WebApk.ShellApkVersion2" units="units"
-    expires_after="2020-01-20">
+    expires_after="2020-03-22">
   <owner>hanxi@chromium.org</owner>
   <owner>pkotwicz@chromium.org</owner>
   <owner>yfriedman@chromium.org</owner>
@@ -156423,7 +156487,8 @@
   </summary>
 </histogram>
 
-<histogram name="WebCore.IndexedDB.SchemaV2HasBlobs" enum="Boolean">
+<histogram name="WebCore.IndexedDB.SchemaV2HasBlobs" enum="Boolean"
+    expires_after="2020-03-22">
   <owner>dmurph@chromium.org</owner>
   <summary>
     Records if a v2 schema has blob keys in the database, which means the
@@ -156437,7 +156502,8 @@
   </summary>
 </histogram>
 
-<histogram name="WebCore.IndexedDB.SchemaV2HasBlobs.Docs" enum="Boolean">
+<histogram name="WebCore.IndexedDB.SchemaV2HasBlobs.Docs" enum="Boolean"
+    expires_after="2020-03-22">
   <owner>dmurph@chromium.org</owner>
   <summary>
     Records if a v2 schema has blob keys in the database, which means the
@@ -158241,7 +158307,7 @@
 </histogram>
 
 <histogram name="WebRTC.Audio.ExpandRatePercent" units="%"
-    expires_after="2020-01-20">
+    expires_after="2020-03-22">
   <owner>hlundin@chromium.org</owner>
   <summary>
     Measures the expand rate for an incoming WebRTC audio stream. The expand
@@ -159241,7 +159307,7 @@
 </histogram>
 
 <histogram name="WebRTC.PeerConnection.UsagePattern"
-    enum="WebRtcPeerConnectionUsagePattern" expires_after="2020-01-20">
+    enum="WebRtcPeerConnectionUsagePattern" expires_after="2020-03-22">
   <owner>hta@chromium.org</owner>
   <summary>
     Capsule history of a WebRTC PeerConnection, encoded as a sequence of bits
@@ -160791,7 +160857,7 @@
 </histogram>
 
 <histogram name="WebRtcEventLogging.Api" enum="WebRtcEventLoggingApiEnum"
-    expires_after="2020-01-20">
+    expires_after="2020-03-22">
   <owner>eladalon@chromium.org</owner>
   <owner>saeedj@google.com</owner>
   <owner>manj@google.com</owner>
@@ -160855,7 +160921,7 @@
 </histogram>
 
 <histogram name="WebShare.ApiCount" enum="WebShareMethod"
-    expires_after="2020-01-20">
+    expires_after="2020-03-22">
   <owner>mgiuca@chromium.org</owner>
   <summary>
     Counts the number of calls to navigator.share. Includes both successful and
@@ -161137,7 +161203,7 @@
 </histogram>
 
 <histogram name="WebUI.Settings.PathVisited" enum="WebUISettingsPathHashes"
-    expires_after="2020-01-20">
+    expires_after="2020-03-22">
   <owner>dschuyler@chromium.org</owner>
   <owner>tbuckley@chromium.org</owner>
   <owner>bettes@chromium.org</owner>
@@ -162016,7 +162082,7 @@
 </histogram>
 
 <histogram name="XHR.Sync.PageDismissal_forbidden" enum="XHRPageDismissalState"
-    expires_after="2020-01-03">
+    expires_after="2020-03-22">
   <owner>kdillon@chromium.org</owner>
   <owner>panicker@chromium.org</owner>
   <summary>
diff --git a/tools/metrics/histograms/update_histogram_enum.py b/tools/metrics/histograms/update_histogram_enum.py
index 59509136..31e5979 100644
--- a/tools/metrics/histograms/update_histogram_enum.py
+++ b/tools/metrics/histograms/update_histogram_enum.py
@@ -191,7 +191,7 @@
   for child in enum_node.childNodes:
     if child.nodeName == 'int':
       value = int(child.attributes['value'].value)
-      if not source_enum_values.has_key(value):
+      if value not in source_enum_values:
         new_item_nodes[value] = child
     # Preserve existing non-generated comments.
     elif (child.nodeType == minidom.Node.COMMENT_NODE and
diff --git a/tools/origin_trials/check_token.py b/tools/origin_trials/check_token.py
index 99a5dfa..ba0bacbf 100755
--- a/tools/origin_trials/check_token.py
+++ b/tools/origin_trials/check_token.py
@@ -195,7 +195,7 @@
 
   # Extract the required fields
   for field in ["origin", "feature", "expiry"]:
-    if not token_data.has_key(field):
+    if field not in token_data:
       print "Token is missing required field: %s" % field
       sys.exit(1)
 
diff --git a/tools/perf/benchmarks/system_health_smoke_test.py b/tools/perf/benchmarks/system_health_smoke_test.py
index 75cb063..5a021a4c 100644
--- a/tools/perf/benchmarks/system_health_smoke_test.py
+++ b/tools/perf/benchmarks/system_health_smoke_test.py
@@ -48,6 +48,7 @@
   'system_health.memory_mobile/browse:news:cnn',
   'system_health.memory_mobile/load:media:facebook_photos',
   'system_health.memory_mobile/load:news:cnn',
+  'system_health.memory_mobile/load:news:reddit',
   'system_health.memory_mobile/load:news:washingtonpost',
   'system_health.memory_mobile/load:tools:stackoverflow',
   'system_health.memory_desktop/load_accessibility:shopping:amazon',
diff --git a/tools/perf/page_sets/data/system_health_mobile.json b/tools/perf/page_sets/data/system_health_mobile.json
index 95cecac9..1c5d7fa4 100644
--- a/tools/perf/page_sets/data/system_health_mobile.json
+++ b/tools/perf/page_sets/data/system_health_mobile.json
@@ -210,6 +210,9 @@
         "load:news:reddit": {
             "DEFAULT": "system_health_mobile_011.wprgo"
         },
+        "load:news:reddit:2019": {
+            "DEFAULT": "system_health_mobile_7d648ad696.wprgo"
+        },
         "load:news:sohu": {
             "DEFAULT": "system_health_mobile_002.wprgo"
         },
diff --git a/tools/perf/page_sets/data/system_health_mobile_7d648ad696.wprgo.sha1 b/tools/perf/page_sets/data/system_health_mobile_7d648ad696.wprgo.sha1
new file mode 100644
index 0000000..182f4fe
--- /dev/null
+++ b/tools/perf/page_sets/data/system_health_mobile_7d648ad696.wprgo.sha1
@@ -0,0 +1 @@
+7d648ad696824e1c2e5d60339e4657b501ed9141
\ No newline at end of file
diff --git a/tools/perf/page_sets/system_health/loading_stories.py b/tools/perf/page_sets/system_health/loading_stories.py
index fa0e8f7..af9647c 100644
--- a/tools/perf/page_sets/system_health/loading_stories.py
+++ b/tools/perf/page_sets/system_health/loading_stories.py
@@ -224,6 +224,11 @@
   SUPPORTED_PLATFORMS = platforms.MOBILE_ONLY
   TAGS = [story_tags.HEALTH_CHECK, story_tags.YEAR_2016]
 
+class LoadRedditMobileStory2019(_LoadingStory):
+  NAME = 'load:news:reddit:2019'
+  URL = 'https://www.reddit.com/r/news/top/?sort=top&t=week'
+  SUPPORTED_PLATFORMS = platforms.MOBILE_ONLY
+  TAGS = [story_tags.HEALTH_CHECK, story_tags.YEAR_2019]
 
 class LoadWashingtonPostMobileStory(_LoadingStory):
   NAME = 'load:news:washingtonpost'
diff --git a/tools/perf/process_perf_results.py b/tools/perf/process_perf_results.py
index 9202796..40c2d98 100755
--- a/tools/perf/process_perf_results.py
+++ b/tools/perf/process_perf_results.py
@@ -59,6 +59,7 @@
   'latency_perftests',
   'load_library_perf_tests',
   'media_perftests',
+  'net_perftests',
   'views_perftests',
   'viz_perftests',
   'xr.vr.common_perftests',
diff --git a/tools/traceline/traceline/scripts/crit_sec.py b/tools/traceline/traceline/scripts/crit_sec.py
index ee710bd..aa23b920 100755
--- a/tools/traceline/traceline/scripts/crit_sec.py
+++ b/tools/traceline/traceline/scripts/crit_sec.py
@@ -17,7 +17,7 @@
         e['eventtype'] == 'EVENT_TYPE_TRYENTER_CS' or
         e['eventtype'] == 'EVENT_TYPE_LEAVE_CS'):
       cs = e['critical_section']
-      if not crits.has_key(cs):
+      if cs not in crits:
         crits[cs] = [ ]
       crits[cs].append(e)
 
diff --git a/tools/traceline/traceline/scripts/filter_short.py b/tools/traceline/traceline/scripts/filter_short.py
index 1b73bf9..12e50aec 100755
--- a/tools/traceline/traceline/scripts/filter_short.py
+++ b/tools/traceline/traceline/scripts/filter_short.py
@@ -17,7 +17,7 @@
 def parseEvents(z):
   print 'parseEvents(['
   for e in z:
-    if e.has_key('ms') and e.has_key('done'):
+    if 'ms' in e and 'done' in e:
       dur = e['done'] - e['ms']
       if dur < 0.2:
         continue
diff --git a/tools/traffic_annotation/auditor/BUILD.gn b/tools/traffic_annotation/auditor/BUILD.gn
index b3c2a1e..a2c8ebfa 100644
--- a/tools/traffic_annotation/auditor/BUILD.gn
+++ b/tools/traffic_annotation/auditor/BUILD.gn
@@ -97,8 +97,7 @@
     "tests/annotations_sample1.xml",
     "tests/annotations_sample2.xml",
     "tests/annotations_sample3.xml",
-    "tests/extractor_outputs/bad_assignment1.txt",
-    "tests/extractor_outputs/bad_assignment2.txt",
+    "tests/extractor_outputs/bad_assignment.txt",
     "tests/extractor_outputs/bad_call.txt",
     "tests/extractor_outputs/bad_syntax_annotation1.txt",
     "tests/extractor_outputs/bad_syntax_annotation2.txt",
diff --git a/tools/traffic_annotation/auditor/instance.cc b/tools/traffic_annotation/auditor/instance.cc
index 256d6d9a..0408518 100644
--- a/tools/traffic_annotation/auditor/instance.cc
+++ b/tools/traffic_annotation/auditor/instance.cc
@@ -130,14 +130,13 @@
     const std::vector<std::string>& serialized_lines,
     int start_line,
     int end_line) {
-  if (end_line - start_line < 7) {
+  if (end_line - start_line < 6) {
     return AuditorResult(AuditorResult::Type::ERROR_FATAL,
                          "Not enough lines to deserialize annotation.");
   }
 
   // Extract header lines.
   const std::string& file_path = serialized_lines[start_line++];
-  const std::string& function_context = serialized_lines[start_line++];
   int line_number;
   base::StringToInt(serialized_lines[start_line++], &line_number);
   std::string function_type = serialized_lines[start_line++];
@@ -196,7 +195,6 @@
   traffic_annotation::NetworkTrafficAnnotation_TrafficSource* src =
       proto.mutable_source();
   src->set_file(file_path);
-  src->set_function(function_context);
   src->set_line(line_number);
   proto.set_unique_id(unique_id);
   second_id_hash_code = TrafficAnnotationAuditor::ComputeHashValue(second_id);
@@ -645,7 +643,6 @@
 CallInstance::CallInstance(const CallInstance& other)
     : file_path(other.file_path),
       line_number(other.line_number),
-      function_context(other.function_context),
       function_name(other.function_name),
       is_annotated(other.is_annotated) {}
 
@@ -653,13 +650,12 @@
     const std::vector<std::string>& serialized_lines,
     int start_line,
     int end_line) {
-  if (end_line - start_line != 5) {
+  if (end_line - start_line != 4) {
     return AuditorResult(AuditorResult::Type::ERROR_FATAL,
                          "Not enough lines to deserialize call.");
   }
 
   file_path = serialized_lines[start_line++];
-  function_context = serialized_lines[start_line++];
   int line_number_int;
   base::StringToInt(serialized_lines[start_line++], &line_number_int);
   line_number = static_cast<uint32_t>(line_number_int);
@@ -673,20 +669,17 @@
 AssignmentInstance::AssignmentInstance() : line_number(0) {}
 
 AssignmentInstance::AssignmentInstance(const AssignmentInstance& other)
-    : file_path(other.file_path),
-      line_number(other.line_number),
-      function_context(other.function_context) {}
+    : file_path(other.file_path), line_number(other.line_number) {}
 
 AuditorResult AssignmentInstance::Deserialize(
     const std::vector<std::string>& serialized_lines,
     int start_line,
     int end_line) {
-  if (end_line - start_line != 3) {
+  if (end_line - start_line != 2) {
     return AuditorResult(AuditorResult::Type::ERROR_FATAL,
                          "Not enough lines to deserialize assignment.");
   }
   file_path = serialized_lines[start_line++];
-  function_context = serialized_lines[start_line++];
   int line_number_int;
   base::StringToInt(serialized_lines[start_line++], &line_number_int);
   line_number = static_cast<uint32_t>(line_number_int);
diff --git a/tools/traffic_annotation/auditor/instance.h b/tools/traffic_annotation/auditor/instance.h
index 688b649..74e003ce 100644
--- a/tools/traffic_annotation/auditor/instance.h
+++ b/tools/traffic_annotation/auditor/instance.h
@@ -156,9 +156,6 @@
   std::string file_path;
   uint32_t line_number;
 
-  // Name of the function in which the call happens.
-  std::string function_context;
-
   // Name of the function that may need annotation.
   std::string function_name;
 
@@ -187,9 +184,6 @@
 
   std::string file_path;
   uint32_t line_number;
-
-  // Name of the function in which assignment happens.
-  std::string function_context;
 };
 
 #endif  // TOOLS_TRAFFIC_ANNOTATION_AUDITOR_INSTANCE_H_
diff --git a/tools/traffic_annotation/auditor/tests/extractor_outputs/bad_assignment1.txt b/tools/traffic_annotation/auditor/tests/extractor_outputs/bad_assignment.txt
similarity index 92%
rename from tools/traffic_annotation/auditor/tests/extractor_outputs/bad_assignment1.txt
rename to tools/traffic_annotation/auditor/tests/extractor_outputs/bad_assignment.txt
index e884c2d..9065624 100644
--- a/tools/traffic_annotation/auditor/tests/extractor_outputs/bad_assignment1.txt
+++ b/tools/traffic_annotation/auditor/tests/extractor_outputs/bad_assignment.txt
@@ -1,2 +1 @@
-components/download/internal/proto_conversions.cc
-267
\ No newline at end of file
+components/download/internal/proto_conversions.cc
\ No newline at end of file
diff --git a/tools/traffic_annotation/auditor/tests/extractor_outputs/bad_assignment2.txt b/tools/traffic_annotation/auditor/tests/extractor_outputs/bad_assignment2.txt
deleted file mode 100644
index 56e27a45..0000000
--- a/tools/traffic_annotation/auditor/tests/extractor_outputs/bad_assignment2.txt
+++ /dev/null
@@ -1,2 +0,0 @@
-components/download/internal/proto_conversions.cc
-download::ProtoConversions::EntryFromProto
\ No newline at end of file
diff --git a/tools/traffic_annotation/auditor/tests/extractor_outputs/bad_call.txt b/tools/traffic_annotation/auditor/tests/extractor_outputs/bad_call.txt
index ea130c4..3d66333 100644
--- a/tools/traffic_annotation/auditor/tests/extractor_outputs/bad_call.txt
+++ b/tools/traffic_annotation/auditor/tests/extractor_outputs/bad_call.txt
@@ -1,4 +1,3 @@
 headless/public/util/http_url_fetcher.cc
-headless::HttpURLFetcher::Delegate::Delegate
 net::URLRequestContext::CreateRequest
 1
\ No newline at end of file
diff --git a/tools/traffic_annotation/auditor/tests/extractor_outputs/bad_syntax_annotation1.txt b/tools/traffic_annotation/auditor/tests/extractor_outputs/bad_syntax_annotation1.txt
index dc3f1819..b6e6305 100644
--- a/tools/traffic_annotation/auditor/tests/extractor_outputs/bad_syntax_annotation1.txt
+++ b/tools/traffic_annotation/auditor/tests/extractor_outputs/bad_syntax_annotation1.txt
@@ -1,5 +1,4 @@
 chrome/browser/supervised_user/legacy/supervised_user_refresh_token_fetcher.cc
-OnGetTokenSuccess
 166
 Definition
 supervised_user_refresh_token_fetcher
diff --git a/tools/traffic_annotation/auditor/tests/extractor_outputs/bad_syntax_annotation2.txt b/tools/traffic_annotation/auditor/tests/extractor_outputs/bad_syntax_annotation2.txt
index 22c1b2c..78a286a 100644
--- a/tools/traffic_annotation/auditor/tests/extractor_outputs/bad_syntax_annotation2.txt
+++ b/tools/traffic_annotation/auditor/tests/extractor_outputs/bad_syntax_annotation2.txt
@@ -1,5 +1,4 @@
 chrome/browser/supervised_user/legacy/supervised_user_refresh_token_fetcher.cc
-OnGetTokenSuccess
 166
 Definition
 supervised_user_refresh_token_fetcher
diff --git a/tools/traffic_annotation/auditor/tests/extractor_outputs/bad_syntax_annotation3.txt b/tools/traffic_annotation/auditor/tests/extractor_outputs/bad_syntax_annotation3.txt
index b469160..44487d2 100644
--- a/tools/traffic_annotation/auditor/tests/extractor_outputs/bad_syntax_annotation3.txt
+++ b/tools/traffic_annotation/auditor/tests/extractor_outputs/bad_syntax_annotation3.txt
@@ -1,5 +1,4 @@
 chrome/browser/supervised_user/legacy/supervised_user_refresh_token_fetcher.cc
-OnGetTokenSuccess
 166
 Definition
 supervised_user_refresh_token_fetcher
diff --git a/tools/traffic_annotation/auditor/tests/extractor_outputs/bad_syntax_annotation4.txt b/tools/traffic_annotation/auditor/tests/extractor_outputs/bad_syntax_annotation4.txt
index ff9b95d..7511728 100644
--- a/tools/traffic_annotation/auditor/tests/extractor_outputs/bad_syntax_annotation4.txt
+++ b/tools/traffic_annotation/auditor/tests/extractor_outputs/bad_syntax_annotation4.txt
@@ -1,5 +1,4 @@
 chrome/browser/supervised_user/legacy/supervised_user_refresh_token_fetcher.cc
-OnGetTokenSuccess
 166
 Definition
 supervised_user_refresh_token_fetcher
diff --git a/tools/traffic_annotation/auditor/tests/extractor_outputs/fatal_annotation1.txt b/tools/traffic_annotation/auditor/tests/extractor_outputs/fatal_annotation1.txt
index bb6ddef9..7194718 100644
--- a/tools/traffic_annotation/auditor/tests/extractor_outputs/fatal_annotation1.txt
+++ b/tools/traffic_annotation/auditor/tests/extractor_outputs/fatal_annotation1.txt
@@ -1,5 +1,4 @@
 chrome/browser/supervised_user/legacy/supervised_user_refresh_token_fetcher.cc
-OnGetTokenSuccess
 Definition
 supervised_user_refresh_token_fetcher
 
diff --git a/tools/traffic_annotation/auditor/tests/extractor_outputs/fatal_annotation2.txt b/tools/traffic_annotation/auditor/tests/extractor_outputs/fatal_annotation2.txt
index 2637577..4ab9a44 100644
--- a/tools/traffic_annotation/auditor/tests/extractor_outputs/fatal_annotation2.txt
+++ b/tools/traffic_annotation/auditor/tests/extractor_outputs/fatal_annotation2.txt
@@ -1,5 +1,4 @@
 chrome/browser/supervised_user/legacy/supervised_user_refresh_token_fetcher.cc
-OnGetTokenSuccess
 122
 definition
 supervised_user_refresh_token_fetcher
diff --git a/tools/traffic_annotation/auditor/tests/extractor_outputs/fatal_annotation3.txt b/tools/traffic_annotation/auditor/tests/extractor_outputs/fatal_annotation3.txt
index 7e89f6f..7bd3bf9 100644
--- a/tools/traffic_annotation/auditor/tests/extractor_outputs/fatal_annotation3.txt
+++ b/tools/traffic_annotation/auditor/tests/extractor_outputs/fatal_annotation3.txt
@@ -1,5 +1,4 @@
 chrome/browser/supervised_user/legacy/supervised_user_refresh_token_fetcher.cc
-OnGetTokenSuccess
 122
 definition
 supervised_user_refresh_token_fetcher
diff --git a/tools/traffic_annotation/auditor/tests/extractor_outputs/good_assignment.txt b/tools/traffic_annotation/auditor/tests/extractor_outputs/good_assignment.txt
index 67a84a98..be3fad0 100644
--- a/tools/traffic_annotation/auditor/tests/extractor_outputs/good_assignment.txt
+++ b/tools/traffic_annotation/auditor/tests/extractor_outputs/good_assignment.txt
@@ -1,3 +1,2 @@
 components/download/internal/background_service/proto_conversions.cc
-download::ProtoConversions::EntryFromProto
 267
\ No newline at end of file
diff --git a/tools/traffic_annotation/auditor/tests/extractor_outputs/good_branched_completing_annotation.txt b/tools/traffic_annotation/auditor/tests/extractor_outputs/good_branched_completing_annotation.txt
index 03fce93..1800c14 100644
--- a/tools/traffic_annotation/auditor/tests/extractor_outputs/good_branched_completing_annotation.txt
+++ b/tools/traffic_annotation/auditor/tests/extractor_outputs/good_branched_completing_annotation.txt
@@ -1,5 +1,4 @@
 chrome/service/cloud_print/cloud_print_url_fetcher.cc
-cloud_print::CloudPrintURLFetcher::StartRequestHelper
 265
 BranchedCompleting
 cloud_print
diff --git a/tools/traffic_annotation/auditor/tests/extractor_outputs/good_call.txt b/tools/traffic_annotation/auditor/tests/extractor_outputs/good_call.txt
index e74c34d2..7162c390 100644
--- a/tools/traffic_annotation/auditor/tests/extractor_outputs/good_call.txt
+++ b/tools/traffic_annotation/auditor/tests/extractor_outputs/good_call.txt
@@ -1,5 +1,4 @@
 headless/public/util/http_url_fetcher.cc
-headless::HttpURLFetcher::Delegate::Delegate
 100
 net::URLRequestContext::CreateRequest
 1
\ No newline at end of file
diff --git a/tools/traffic_annotation/auditor/tests/extractor_outputs/good_complete_annotation.txt b/tools/traffic_annotation/auditor/tests/extractor_outputs/good_complete_annotation.txt
index 0e878a2..c74390d 100644
--- a/tools/traffic_annotation/auditor/tests/extractor_outputs/good_complete_annotation.txt
+++ b/tools/traffic_annotation/auditor/tests/extractor_outputs/good_complete_annotation.txt
@@ -1,5 +1,4 @@
 chrome/browser/supervised_user/legacy/supervised_user_refresh_token_fetcher.cc
-OnGetTokenSuccess
 166
 Definition
 supervised_user_refresh_token_fetcher
diff --git a/tools/traffic_annotation/auditor/tests/extractor_outputs/good_completing_annotation.txt b/tools/traffic_annotation/auditor/tests/extractor_outputs/good_completing_annotation.txt
index dcd5297..df296ee 100644
--- a/tools/traffic_annotation/auditor/tests/extractor_outputs/good_completing_annotation.txt
+++ b/tools/traffic_annotation/auditor/tests/extractor_outputs/good_completing_annotation.txt
@@ -1,5 +1,4 @@
 chrome/service/cloud_print/cloud_print_url_fetcher.cc
-cloud_print::CloudPrintURLFetcher::StartRequestHelper
 265
 Completing
 cloud_print
diff --git a/tools/traffic_annotation/auditor/tests/extractor_outputs/good_partial_annotation.txt b/tools/traffic_annotation/auditor/tests/extractor_outputs/good_partial_annotation.txt
index 07422ab..176d562 100644
--- a/tools/traffic_annotation/auditor/tests/extractor_outputs/good_partial_annotation.txt
+++ b/tools/traffic_annotation/auditor/tests/extractor_outputs/good_partial_annotation.txt
@@ -1,5 +1,4 @@
 components/browsing_data/core/counters/history_counter.cc
-browsing_data::HistoryCounter::Count
 98
 Partial
 web_history_counter
diff --git a/tools/traffic_annotation/auditor/tests/extractor_outputs/good_test_annotation.txt b/tools/traffic_annotation/auditor/tests/extractor_outputs/good_test_annotation.txt
index 07bd917..24821ff 100644
--- a/tools/traffic_annotation/auditor/tests/extractor_outputs/good_test_annotation.txt
+++ b/tools/traffic_annotation/auditor/tests/extractor_outputs/good_test_annotation.txt
@@ -1,5 +1,4 @@
 google_apis/drive/request_sender_unittest.cc
-google_apis::(anonymous namespace)::RequestSenderTest::RequestSenderTest
 67
 Definition
 test
diff --git a/tools/traffic_annotation/auditor/tests/extractor_outputs/missing_annotation.txt b/tools/traffic_annotation/auditor/tests/extractor_outputs/missing_annotation.txt
index 2efb3c3..670ffb0 100644
--- a/tools/traffic_annotation/auditor/tests/extractor_outputs/missing_annotation.txt
+++ b/tools/traffic_annotation/auditor/tests/extractor_outputs/missing_annotation.txt
@@ -1,5 +1,4 @@
 net/url_request/url_request_context.cc
-net::URLRequestContext::CreateRequest
 120
 Definition
 missing
diff --git a/tools/traffic_annotation/auditor/traffic_annotation_auditor_unittest.cc b/tools/traffic_annotation/auditor/traffic_annotation_auditor_unittest.cc
index 7b0d3ad..4dbd6c2 100644
--- a/tools/traffic_annotation/auditor/traffic_annotation_auditor_unittest.cc
+++ b/tools/traffic_annotation/auditor/traffic_annotation_auditor_unittest.cc
@@ -334,7 +334,6 @@
     EXPECT_EQ(annotation.proto.source().file(),
               "chrome/browser/supervised_user/legacy/"
               "supervised_user_refresh_token_fetcher.cc");
-    EXPECT_EQ(annotation.proto.source().function(), "OnGetTokenSuccess");
     EXPECT_EQ(annotation.proto.source().line(), 166);
     EXPECT_EQ(annotation.proto.semantics().sender(), "Supervised Users");
     EXPECT_EQ(annotation.proto.policy().cookies_allowed(), 1);
@@ -365,8 +364,6 @@
 
     EXPECT_EQ(call.file_path, "headless/public/util/http_url_fetcher.cc");
     EXPECT_EQ(call.line_number, 100u);
-    EXPECT_EQ(call.function_context,
-              "headless::HttpURLFetcher::Delegate::Delegate");
     EXPECT_EQ(call.function_name, "net::URLRequestContext::CreateRequest");
     EXPECT_EQ(call.is_annotated, true);
   }
@@ -381,8 +378,7 @@
 
   Assignmentample test_cases[] = {
       {"good_assignment.txt", AuditorResult::Type::RESULT_OK},
-      {"bad_assignment1.txt", AuditorResult::Type::ERROR_FATAL},
-      {"bad_assignment2.txt", AuditorResult::Type::ERROR_FATAL},
+      {"bad_assignment.txt", AuditorResult::Type::ERROR_FATAL},
   };
 
   for (const auto& test_case : test_cases) {
diff --git a/tools/traffic_annotation/bin/README.md b/tools/traffic_annotation/bin/README.md
index 414b178..8ce2ac3 100644
--- a/tools/traffic_annotation/bin/README.md
+++ b/tools/traffic_annotation/bin/README.md
@@ -74,5 +74,5 @@
 The following two lines will be updated by the above script, and the modified
 README should be committed along with the updated .sha1 checksums.
 
-CLANG_REVISION = '8288453f6aac05080b751b680455349e09d49825'
-LASTCHANGE=1f619bc51ddddccc493776c0d2d3596d1c758a28-refs/heads/master@{#691147}
+CLANG_REVISION = 'b4160cb94c54f0b31d0ce14694950dac7b6cd83f'
+LASTCHANGE=af603aeed8f4a091c4f94605c00084c69c708bca-refs/heads/master@{#696886}
\ No newline at end of file
diff --git a/tools/traffic_annotation/bin/linux64/traffic_annotation_auditor.sha1 b/tools/traffic_annotation/bin/linux64/traffic_annotation_auditor.sha1
index 90bc42f..5ad4497 100644
--- a/tools/traffic_annotation/bin/linux64/traffic_annotation_auditor.sha1
+++ b/tools/traffic_annotation/bin/linux64/traffic_annotation_auditor.sha1
@@ -1 +1 @@
-eee0f2d9b6f83e03206912c748132495a21b5e37
\ No newline at end of file
+e7c47594d4aeafff709f574183586e7ebc99817a
\ No newline at end of file
diff --git a/tools/traffic_annotation/bin/linux64/traffic_annotation_extractor.sha1 b/tools/traffic_annotation/bin/linux64/traffic_annotation_extractor.sha1
index a9a4ec87..ec1fc1e 100644
--- a/tools/traffic_annotation/bin/linux64/traffic_annotation_extractor.sha1
+++ b/tools/traffic_annotation/bin/linux64/traffic_annotation_extractor.sha1
@@ -1 +1 @@
-c4e2a772fbab2f1f7e47cf8f4f23425933f8cd0f
\ No newline at end of file
+8618305abe807cbeefaaa512e47796f89116bf98
\ No newline at end of file
diff --git a/tools/traffic_annotation/bin/win32/traffic_annotation_auditor.exe.sha1 b/tools/traffic_annotation/bin/win32/traffic_annotation_auditor.exe.sha1
index 7cd2563..25f6525 100644
--- a/tools/traffic_annotation/bin/win32/traffic_annotation_auditor.exe.sha1
+++ b/tools/traffic_annotation/bin/win32/traffic_annotation_auditor.exe.sha1
@@ -1 +1 @@
-1709431c1a680a1d3880b4d9852c7d3d1a7e3661
\ No newline at end of file
+23b304a9748dc1cf023e4c35e34c2b0a90cc5e95
\ No newline at end of file
diff --git a/tools/traffic_annotation/bin/win32/traffic_annotation_extractor.exe.sha1 b/tools/traffic_annotation/bin/win32/traffic_annotation_extractor.exe.sha1
index 688de3b..49fa49b 100644
--- a/tools/traffic_annotation/bin/win32/traffic_annotation_extractor.exe.sha1
+++ b/tools/traffic_annotation/bin/win32/traffic_annotation_extractor.exe.sha1
@@ -1 +1 @@
-eac43e1fb9f3fb1a300515061ad9daef6d51839d
\ No newline at end of file
+4f467153df6ea1dee2d83efd8b01dc78673e88c3
\ No newline at end of file
diff --git a/tools/traffic_annotation/scripts/extractor.py b/tools/traffic_annotation/scripts/extractor.py
index 5c45726..9c360d8 100755
--- a/tools/traffic_annotation/scripts/extractor.py
+++ b/tools/traffic_annotation/scripts/extractor.py
@@ -54,13 +54,6 @@
       file_path: Path to the file that contains this annotation.
     """
     self.file_path = file_path
-    # TODO(crbug/966883): Remove function_name from here and from the clang
-    # tool's output.
-    #
-    # |function_name| is not supported, but keep it in the output for
-    # compatibility with the clang tool. Use an obviously wrong function name
-    # in case this details ends up in auditor output.
-    self.function_name = "XXX_UNIMPLEMENTED_XXX"
     self.line_number = line_number
     self.type_name = type_name
     self.unique_id = unique_id
@@ -88,7 +81,6 @@
     return "\n".join(map(str, [
         "==== NEW ANNOTATION ====",
         self.file_path,
-        self.function_name,
         self.line_number,
         self.type_name,
         self.unique_id,
diff --git a/tools/traffic_annotation/scripts/test_data/valid_file-stdout.txt b/tools/traffic_annotation/scripts/test_data/valid_file-stdout.txt
index b98d267..825fcc9 100644
--- a/tools/traffic_annotation/scripts/test_data/valid_file-stdout.txt
+++ b/tools/traffic_annotation/scripts/test_data/valid_file-stdout.txt
@@ -1,6 +1,5 @@
 ==== NEW ANNOTATION ====
 valid_file.cc
-XXX_UNIMPLEMENTED_XXX
 14
 Definition
 id1
@@ -26,7 +25,6 @@
 ==== ANNOTATION ENDS ====
 ==== NEW ANNOTATION ====
 valid_file.cc
-XXX_UNIMPLEMENTED_XXX
 36
 Partial
 id2
@@ -42,7 +40,6 @@
 ==== ANNOTATION ENDS ====
 ==== NEW ANNOTATION ====
 valid_file.cc
-XXX_UNIMPLEMENTED_XXX
 46
 Completing
 id3
@@ -62,7 +59,6 @@
 ==== ANNOTATION ENDS ====
 ==== NEW ANNOTATION ====
 valid_file.cc
-XXX_UNIMPLEMENTED_XXX
 61
 BranchedCompleting
 id4
@@ -77,7 +73,6 @@
 ==== ANNOTATION ENDS ====
 ==== NEW ANNOTATION ====
 valid_file.cc
-XXX_UNIMPLEMENTED_XXX
 126
 Mutable
 
@@ -86,7 +81,6 @@
 ==== ANNOTATION ENDS ====
 ==== NEW ANNOTATION ====
 valid_file.cc
-XXX_UNIMPLEMENTED_XXX
 86
 Definition
 test
@@ -95,7 +89,6 @@
 ==== ANNOTATION ENDS ====
 ==== NEW ANNOTATION ====
 valid_file.cc
-XXX_UNIMPLEMENTED_XXX
 88
 Partial
 test_partial
diff --git a/tools/traffic_annotation/traffic_annotation.proto b/tools/traffic_annotation/traffic_annotation.proto
index d00be7e..65bc32b 100644
--- a/tools/traffic_annotation/traffic_annotation.proto
+++ b/tools/traffic_annotation/traffic_annotation.proto
@@ -38,12 +38,6 @@
     // specified.
     string file = 1;
 
-    // Function name where the network request is instantiated.
-    // This is typically filled by the extractor and does not need to be
-    // specified in the source code. For manual whitelisting this needs to be
-    // specified.
-    string function = 2;
-
     // __LINE__ in file, where the AuditPolicy object is instantiated.
     // This is typically filled by the extractor and does not need to be
     // specified in the source code.
diff --git a/ui/accessibility/ax_enum_util.cc b/ui/accessibility/ax_enum_util.cc
index fdddb63..a4440c96 100644
--- a/ui/accessibility/ax_enum_util.cc
+++ b/ui/accessibility/ax_enum_util.cc
@@ -275,8 +275,16 @@
       return "alert";
     case ax::mojom::Role::kAnchor:
       return "anchor";
-    case ax::mojom::Role::kAnnotation:
-      return "annotation";
+    case ax::mojom::Role::kAnnotationAttribution:
+      return "annotationAttribution";
+    case ax::mojom::Role::kAnnotationCommentary:
+      return "annotationCommentary";
+    case ax::mojom::Role::kAnnotationPresence:
+      return "annotationPresence";
+    case ax::mojom::Role::kAnnotationRevision:
+      return "annotationRevision";
+    case ax::mojom::Role::kAnnotationSuggestion:
+      return "annotationSuggestion";
     case ax::mojom::Role::kApplication:
       return "application";
     case ax::mojom::Role::kArticle:
@@ -555,6 +563,8 @@
       return "row";
     case ax::mojom::Role::kRuby:
       return "ruby";
+    case ax::mojom::Role::kRubyAnnotation:
+      return "rubyAnnotation";
     case ax::mojom::Role::kSvgRoot:
       return "svgRoot";
     case ax::mojom::Role::kScrollBar:
@@ -639,8 +649,16 @@
     return ax::mojom::Role::kAlert;
   if (0 == strcmp(role, "anchor"))
     return ax::mojom::Role::kAnchor;
-  if (0 == strcmp(role, "annotation"))
-    return ax::mojom::Role::kAnnotation;
+  if (0 == strcmp(role, "annotationAttribution"))
+    return ax::mojom::Role::kAnnotationAttribution;
+  if (0 == strcmp(role, "annotationCommentary"))
+    return ax::mojom::Role::kAnnotationCommentary;
+  if (0 == strcmp(role, "annotationPresence"))
+    return ax::mojom::Role::kAnnotationPresence;
+  if (0 == strcmp(role, "annotationRevision"))
+    return ax::mojom::Role::kAnnotationRevision;
+  if (0 == strcmp(role, "annotationSuggestion"))
+    return ax::mojom::Role::kAnnotationSuggestion;
   if (0 == strcmp(role, "application"))
     return ax::mojom::Role::kApplication;
   if (0 == strcmp(role, "article"))
@@ -919,6 +937,8 @@
     return ax::mojom::Role::kRow;
   if (0 == strcmp(role, "ruby"))
     return ax::mojom::Role::kRuby;
+  if (0 == strcmp(role, "rubyAnnotation"))
+    return ax::mojom::Role::kRubyAnnotation;
   if (0 == strcmp(role, "svgRoot"))
     return ax::mojom::Role::kSvgRoot;
   if (0 == strcmp(role, "scrollBar"))
diff --git a/ui/accessibility/ax_enums.mojom b/ui/accessibility/ax_enums.mojom
index 1448d95..54827184 100644
--- a/ui/accessibility/ax_enums.mojom
+++ b/ui/accessibility/ax_enums.mojom
@@ -109,7 +109,11 @@
   kAlert,
   kAlertDialog,
   kAnchor,
-  kAnnotation,
+  kAnnotationAttribution,
+  kAnnotationCommentary,
+  kAnnotationPresence,
+  kAnnotationRevision,
+  kAnnotationSuggestion,
   kApplication,
   kArticle,
   kAudio,
@@ -262,6 +266,7 @@
   kRow,
   kRowHeader,
   kRuby,
+  kRubyAnnotation,
   kScrollBar,
   kScrollView,
   kSearch,
diff --git a/ui/accessibility/ax_position.h b/ui/accessibility/ax_position.h
index 858df79..8880215 100644
--- a/ui/accessibility/ax_position.h
+++ b/ui/accessibility/ax_position.h
@@ -697,16 +697,7 @@
   // because positions can handle moving across multiple trees, while trees
   // cannot.
   AXPositionInstance LowestCommonAncestor(const AXPosition& second) const {
-    AXNodeType* common_anchor = LowestCommonAnchor(second);
-    if (!common_anchor)
-      return CreateNullPosition();
-
-    AXPositionInstance common_ancestor = Clone();
-    while (!common_ancestor->IsNullPosition() &&
-           common_ancestor->GetAnchor() != common_anchor) {
-      common_ancestor = common_ancestor->CreateParentPosition();
-    }
-    return common_ancestor;
+    return CreateAncestorPosition(LowestCommonAnchor(second));
   }
 
   AXPositionInstance AsTreePosition() const {
@@ -763,8 +754,11 @@
       // If the current text offset is valid, we don't touch it to potentially
       // allow converting from a text position to a tree position and back
       // without losing information.
-      if (copy->text_offset_ < 0 || copy->text_offset_ >= copy->MaxTextOffset())
+      if (copy->text_offset_ < 0 ||
+          (copy->text_offset_ > 0 &&
+           copy->text_offset_ >= copy->MaxTextOffset())) {
         copy->text_offset_ = 0;
+      }
     } else if (copy->child_index_ == copy->AnchorChildCount()) {
       copy->text_offset_ = copy->MaxTextOffset();
     } else {
@@ -774,15 +768,22 @@
       for (int i = 0; i <= child_index_; ++i) {
         AXPositionInstance child = copy->CreateChildPositionAt(i);
         DCHECK(child);
-        int child_length = child->MaxTextOffsetInParent();
+        // If the current text offset is valid, we don't touch it to
+        // potentially allow converting from a text position to a tree
+        // position and back without losing information. Otherwise, if the
+        // text_offset is invalid, equals to 0 or is smaller than
+        // |new_offset|, we reset it to the beginning of the current child
+        // node.
+        if (i == child_index_ && copy->text_offset_ <= new_offset) {
+          copy->text_offset_ = new_offset;
+          break;
+        }
 
-        // If the current text offset is valid, we don't touch it to potentially
-        // allow converting from a text position to a tree position and back
-        // without losing information. Otherwise, we reset it to the beginning
-        // of the current child node.
+        int child_length = child->MaxTextOffsetInParent();
+        // Same comment as above: we don't touch the text offset if it's
+        // already valid.
         if (i == child_index_ &&
-            (copy->text_offset_ < new_offset ||
-             copy->text_offset_ > (new_offset + child_length) ||
+            (copy->text_offset_ > (new_offset + child_length) ||
              // When the text offset is equal to the text's length but this is
              // not an "after text" position.
              (!copy->AtEndOfAnchor() &&
@@ -1212,12 +1213,15 @@
         // position might unambiguously be at the end of a line, its parent
         // position might be the same as the parent position of the position
         // representing the start of the next line.
+        const int max_text_offset = MaxTextOffset();
+        const int max_text_offset_in_parent =
+            IsEmbeddedObjectInParent() ? 1 : max_text_offset;
         int parent_offset = AnchorTextOffsetInParent();
         ax::mojom::TextAffinity parent_affinity = affinity_;
-        if (MaxTextOffset() == MaxTextOffsetInParent()) {
+        if (max_text_offset == max_text_offset_in_parent) {
           parent_offset += text_offset_;
         } else if (text_offset_ > 0) {
-          parent_offset += MaxTextOffsetInParent();
+          parent_offset += max_text_offset_in_parent;
           parent_affinity = ax::mojom::TextAffinity::kDownstream;
         }
 
@@ -1243,7 +1247,7 @@
         // checked if the child was at the end of the line, because our line end
         // testing logic takes into account line breaks, which don't apply in
         // this situation.
-        if (text_offset_ == MaxTextOffset() && parent_position->AtStartOfLine())
+        if (text_offset_ == max_text_offset && parent_position->AtStartOfLine())
           parent_position->affinity_ = ax::mojom::TextAffinity::kUpstream;
         return parent_position;
       }
@@ -1445,10 +1449,9 @@
     // If the word boundary is in the same subtree, return a position rooted at
     // this position's anchor. This is necessary because we don't want to return
     // a position that might be in the shadow DOM when this position is not.
-    AXPositionInstance common_ancestor =
-        text_position->LowestCommonAncestor(*this);
-    if (GetAnchor() == common_ancestor->GetAnchor()) {
-      text_position = std::move(common_ancestor);
+    const AXNodeType* common_anchor = text_position->LowestCommonAnchor(*this);
+    if (GetAnchor() == common_anchor) {
+      text_position = text_position->CreateAncestorPosition(common_anchor);
     } else if (boundary_behavior == AXBoundaryBehavior::StopAtAnchorBoundary) {
       return CreatePositionAtEndOfAnchor();
     }
@@ -1527,10 +1530,9 @@
     // If the word boundary is in the same subtree, return a position rooted at
     // this position's anchor. This is necessary because we don't want to return
     // a position that might be in the shadow DOM when this position is not.
-    AXPositionInstance common_ancestor =
-        text_position->LowestCommonAncestor(*this);
-    if (GetAnchor() == common_ancestor->GetAnchor()) {
-      text_position = std::move(common_ancestor);
+    const AXNodeType* common_anchor = text_position->LowestCommonAnchor(*this);
+    if (GetAnchor() == common_anchor) {
+      text_position = text_position->CreateAncestorPosition(common_anchor);
     } else if (boundary_behavior == AXBoundaryBehavior::StopAtAnchorBoundary) {
       return CreatePositionAtStartOfAnchor();
     }
@@ -1615,10 +1617,9 @@
     // If the word boundary is in the same subtree, return a position rooted at
     // this position's anchor. This is necessary because we don't want to return
     // a position that might be in the shadow DOM when this position is not.
-    AXPositionInstance common_ancestor =
-        text_position->LowestCommonAncestor(*this);
-    if (GetAnchor() == common_ancestor->GetAnchor()) {
-      text_position = std::move(common_ancestor);
+    const AXNodeType* common_anchor = text_position->LowestCommonAnchor(*this);
+    if (GetAnchor() == common_anchor) {
+      text_position = text_position->CreateAncestorPosition(common_anchor);
     } else if (boundary_behavior == AXBoundaryBehavior::StopAtAnchorBoundary) {
       return CreatePositionAtEndOfAnchor();
     }
@@ -1703,10 +1704,9 @@
     // If the word boundary is in the same subtree, return a position rooted at
     // this position's anchor. This is necessary because we don't want to return
     // a position that might be in the shadow DOM when this position is not.
-    AXPositionInstance common_ancestor =
-        text_position->LowestCommonAncestor(*this);
-    if (GetAnchor() == common_ancestor->GetAnchor()) {
-      text_position = std::move(common_ancestor);
+    const AXNodeType* common_anchor = text_position->LowestCommonAnchor(*this);
+    if (GetAnchor() == common_anchor) {
+      text_position = text_position->CreateAncestorPosition(common_anchor);
     } else if (boundary_behavior == AXBoundaryBehavior::StopAtAnchorBoundary) {
       return CreatePositionAtStartOfAnchor();
     }
@@ -1804,10 +1804,9 @@
     // 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 =
-        tree_position->LowestCommonAncestor(*this);
-    if (GetAnchor() == common_ancestor->GetAnchor()) {
-      tree_position = std::move(common_ancestor);
+    const AXNodeType* common_anchor = tree_position->LowestCommonAnchor(*this);
+    if (GetAnchor() == common_anchor) {
+      tree_position = tree_position->CreateAncestorPosition(common_anchor);
     } else if (boundary_behavior == AXBoundaryBehavior::StopAtAnchorBoundary) {
       return CreatePositionAtStartOfAnchor();
     }
@@ -1871,10 +1870,9 @@
     // 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 =
-        tree_position->LowestCommonAncestor(*this);
-    if (GetAnchor() == common_ancestor->GetAnchor()) {
-      tree_position = std::move(common_ancestor);
+    const AXNodeType* common_anchor = tree_position->LowestCommonAnchor(*this);
+    if (GetAnchor() == common_anchor) {
+      tree_position = tree_position->CreateAncestorPosition(common_anchor);
     } else if (boundary_behavior == AXBoundaryBehavior::StopAtAnchorBoundary) {
       return CreatePositionAtEndOfAnchor();
     }
@@ -2058,10 +2056,9 @@
     // If the boundary is in the same subtree, return a position rooted at this
     // position's anchor. This is necessary because we don't want to return a
     // position that might be in the shadow DOM when this position is not.
-    AXPositionInstance common_ancestor =
-        text_position->LowestCommonAncestor(*this);
-    if (GetAnchor() == common_ancestor->GetAnchor()) {
-      text_position = std::move(common_ancestor);
+    const AXNodeType* common_anchor = text_position->LowestCommonAnchor(*this);
+    if (GetAnchor() == common_anchor) {
+      text_position = text_position->CreateAncestorPosition(common_anchor);
     } else if (boundary_behavior == AXBoundaryBehavior::StopAtAnchorBoundary) {
       return (boundary_direction == AXTextBoundaryDirection::kForwards)
                  ? CreatePositionAtEndOfAnchor()
@@ -2135,10 +2132,9 @@
     // If the boundary is in the same subtree, return a position rooted at this
     // position's anchor. This is necessary because we don't want to return a
     // position that might be in the shadow DOM when this position is not.
-    AXPositionInstance common_ancestor =
-        text_position->LowestCommonAncestor(*this);
-    if (GetAnchor() == common_ancestor->GetAnchor()) {
-      text_position = std::move(common_ancestor);
+    const AXNodeType* common_anchor = text_position->LowestCommonAnchor(*this);
+    if (GetAnchor() == common_anchor) {
+      text_position = text_position->CreateAncestorPosition(common_anchor);
     } else if (boundary_behavior == AXBoundaryBehavior::StopAtAnchorBoundary) {
       return (boundary_direction == AXTextBoundaryDirection::kForwards)
                  ? CreatePositionAtEndOfAnchor()
@@ -2192,52 +2188,41 @@
     // common ancestor position when converted to tree positions?" The answer is
     // when they are both text positions and they either have the same anchor,
     // or one is the ancestor of the other.
-    std::unique_ptr<AXPosition> this_tree_position = this->AsTreePosition();
-    std::unique_ptr<AXPosition> other_tree_position = other.AsTreePosition();
-    std::unique_ptr<AXPosition> this_tree_position_ancestor =
-        this_tree_position->LowestCommonAncestor(*other_tree_position);
-    std::unique_ptr<AXPosition> other_tree_position_ancestor =
-        other_tree_position->LowestCommonAncestor(*this_tree_position);
-    DCHECK_EQ(this_tree_position_ancestor->GetAnchor(),
-              other_tree_position_ancestor->GetAnchor());
-    if (this_tree_position_ancestor->IsNullPosition())
+    const AXNodeType* common_anchor = this->LowestCommonAnchor(other);
+    if (!common_anchor)
       return base::Optional<int>(base::nullopt);
-    DCHECK(this_tree_position_ancestor->IsTreePosition() &&
-           other_tree_position_ancestor->IsTreePosition());
 
     // Attempt to avoid recomputing the lowest common ancestor because we may
     // already have its anchor in which case just find the text offset.
     if (this->IsTextPosition() && other.IsTextPosition()) {
       // This text position's anchor is the common ancestor of the other text
       // position's anchor.
-      if (this->GetAnchor() == other_tree_position_ancestor->GetAnchor()) {
-        std::unique_ptr<AXPosition> other_text_position = other.Clone();
-        while (other_text_position->GetAnchor() != this->GetAnchor())
-          other_text_position = other_text_position->CreateParentPosition();
+      if (this->GetAnchor() == common_anchor) {
+        AXPositionInstance other_text_position =
+            other.CreateAncestorPosition(common_anchor);
         return base::Optional<int>(this->text_offset_ -
                                    other_text_position->text_offset_);
       }
 
       // The other text position's anchor is the common ancestor of this text
       // position's anchor.
-      if (other.GetAnchor() == this_tree_position_ancestor->GetAnchor()) {
-        std::unique_ptr<AXPosition> this_text_position = this->Clone();
-        while (this_text_position->GetAnchor() != other.GetAnchor())
-          this_text_position = this_text_position->CreateParentPosition();
+      if (other.GetAnchor() == common_anchor) {
+        AXPositionInstance this_text_position =
+            this->CreateAncestorPosition(common_anchor);
         return base::Optional<int>(this_text_position->text_offset_ -
                                    other.text_offset_);
       }
 
       // All optimizations failed. Fall back to comparing text positions with
       // the common text position ancestor.
-      std::unique_ptr<AXPosition> this_text_position_ancestor =
-          this->LowestCommonAncestor(other);
-      std::unique_ptr<AXPosition> other_text_position_ancestor =
-          other.LowestCommonAncestor(*this);
+      AXPositionInstance this_text_position_ancestor =
+          this->CreateAncestorPosition(common_anchor);
+      AXPositionInstance other_text_position_ancestor =
+          other.CreateAncestorPosition(common_anchor);
       DCHECK(this_text_position_ancestor->IsTextPosition());
       DCHECK(other_text_position_ancestor->IsTextPosition());
-      DCHECK_EQ(this_text_position_ancestor->GetAnchor(),
-                other_text_position_ancestor->GetAnchor());
+      DCHECK_EQ(common_anchor, this_text_position_ancestor->GetAnchor());
+      DCHECK_EQ(common_anchor, other_text_position_ancestor->GetAnchor());
 
       // TODO - This does not take into account |affinity_|, so we may return
       // a false positive when comparing at the end of a line.
@@ -2264,6 +2249,17 @@
                                  other_text_position_ancestor->text_offset_);
     }
 
+    // All optimizations failed. Fall back to comparing child index with
+    // the common tree position ancestor.
+    AXPositionInstance this_tree_position_ancestor =
+        this->AsTreePosition()->CreateAncestorPosition(common_anchor);
+    AXPositionInstance other_tree_position_ancestor =
+        other.AsTreePosition()->CreateAncestorPosition(common_anchor);
+    DCHECK(this_tree_position_ancestor->IsTreePosition());
+    DCHECK(other_tree_position_ancestor->IsTreePosition());
+    DCHECK_EQ(common_anchor, this_tree_position_ancestor->GetAnchor());
+    DCHECK_EQ(common_anchor, other_tree_position_ancestor->GetAnchor());
+
     return base::Optional<int>(this_tree_position_ancestor->child_index() -
                                other_tree_position_ancestor->child_index());
   }
@@ -2368,7 +2364,13 @@
   // Returns the length of text that this anchor node takes up in its parent.
   // On some platforms, embedded objects are represented in their parent with a
   // single embedded object character.
-  virtual int MaxTextOffsetInParent() const { return MaxTextOffset(); }
+  int MaxTextOffsetInParent() const {
+    return IsEmbeddedObjectInParent() ? 1 : MaxTextOffset();
+  }
+
+  // Returns whether or not this anchor is represented in their parent with a
+  // single embedded object character.
+  virtual bool IsEmbeddedObjectInParent() const { return false; }
 
   // Determines if the anchor containing this position produces a hard line
   // break in the text representation, e.g. a block level element or a <br>.
@@ -2724,6 +2726,19 @@
     return iterator;
   }
 
+  AXPositionInstance CreateAncestorPosition(
+      const AXNodeType* ancestor_anchor) const {
+    if (!ancestor_anchor)
+      return CreateNullPosition();
+
+    AXPositionInstance ancestor_position = Clone();
+    while (!ancestor_position->IsNullPosition() &&
+           ancestor_position->GetAnchor() != ancestor_anchor) {
+      ancestor_position = ancestor_position->CreateParentPosition();
+    }
+    return ancestor_position;
+  }
+
   AXPositionKind kind_;
   AXTreeID tree_id_;
   int32_t anchor_id_;
diff --git a/ui/accessibility/platform/ax_platform_node_auralinux.cc b/ui/accessibility/platform/ax_platform_node_auralinux.cc
index fb0d8e9..5b797fd 100644
--- a/ui/accessibility/platform/ax_platform_node_auralinux.cc
+++ b/ui/accessibility/platform/ax_platform_node_auralinux.cc
@@ -2294,10 +2294,12 @@
       return ATK_ROLE_DIALOG;
     case ax::mojom::Role::kAnchor:
       return ATK_ROLE_LINK;
-    case ax::mojom::Role::kAnnotation:
-      // TODO(accessibility) Panels are generally for containers of widgets.
-      // This should probably be a section (if a container) or static if text.
-      return ATK_ROLE_PANEL;
+    case ax::mojom::Role::kAnnotationAttribution:
+    case ax::mojom::Role::kAnnotationCommentary:
+    case ax::mojom::Role::kAnnotationPresence:
+    case ax::mojom::Role::kAnnotationRevision:
+    case ax::mojom::Role::kAnnotationSuggestion:
+      return ATK_ROLE_SECTION;
     case ax::mojom::Role::kApplication:
       // Only use ATK_ROLE_APPLICATION for elements with no parent, since it
       // is only for top level app windows and not ARIA applications.
@@ -2567,6 +2569,10 @@
       return ATK_ROLE_ROW_HEADER;
     case ax::mojom::Role::kRuby:
       return kStaticRole;
+    case ax::mojom::Role::kRubyAnnotation:
+      // TODO(accessibility) Panels are generally for containers of widgets.
+      // This should probably be a section (if a container) or static if text.
+      return ATK_ROLE_PANEL;
     case ax::mojom::Role::kScrollBar:
       return ATK_ROLE_SCROLL_BAR;
     case ax::mojom::Role::kSearch:
diff --git a/ui/accessibility/platform/ax_platform_node_mac.mm b/ui/accessibility/platform/ax_platform_node_mac.mm
index d0f9db5..1221e74 100644
--- a/ui/accessibility/platform/ax_platform_node_mac.mm
+++ b/ui/accessibility/platform/ax_platform_node_mac.mm
@@ -43,7 +43,11 @@
       {ax::mojom::Role::kAlert, NSAccessibilityGroupRole},
       {ax::mojom::Role::kAlertDialog, NSAccessibilityGroupRole},
       {ax::mojom::Role::kAnchor, NSAccessibilityGroupRole},
-      {ax::mojom::Role::kAnnotation, NSAccessibilityUnknownRole},
+      {ax::mojom::Role::kAnnotationAttribution, NSAccessibilityGroupRole},
+      {ax::mojom::Role::kAnnotationCommentary, NSAccessibilityGroupRole},
+      {ax::mojom::Role::kAnnotationPresence, NSAccessibilityGroupRole},
+      {ax::mojom::Role::kAnnotationRevision, NSAccessibilityGroupRole},
+      {ax::mojom::Role::kAnnotationSuggestion, NSAccessibilityGroupRole},
       {ax::mojom::Role::kApplication, NSAccessibilityGroupRole},
       {ax::mojom::Role::kArticle, NSAccessibilityGroupRole},
       {ax::mojom::Role::kAudio, NSAccessibilityGroupRole},
@@ -182,6 +186,10 @@
       {ax::mojom::Role::kRootWebArea, @"AXWebArea"},
       {ax::mojom::Role::kRow, NSAccessibilityRowRole},
       {ax::mojom::Role::kRowHeader, @"AXCell"},
+      // TODO(accessibility) What should kRuby be? It's not listed? Any others
+      // missing? Maybe use switch statement so that compiler doesn't allow us
+      // to miss any.
+      {ax::mojom::Role::kRubyAnnotation, NSAccessibilityUnknownRole},
       {ax::mojom::Role::kScrollBar, NSAccessibilityScrollBarRole},
       {ax::mojom::Role::kSearch, NSAccessibilityGroupRole},
       {ax::mojom::Role::kSearchBox, NSAccessibilityTextFieldRole},
diff --git a/ui/accessibility/platform/ax_platform_node_win.cc b/ui/accessibility/platform/ax_platform_node_win.cc
index 841f1de..deda9d6 100644
--- a/ui/accessibility/platform/ax_platform_node_win.cc
+++ b/ui/accessibility/platform/ax_platform_node_win.cc
@@ -4456,6 +4456,13 @@
     case ax::mojom::Role::kAnchor:
       return ROLE_SYSTEM_LINK;
 
+    case ax::mojom::Role::kAnnotationAttribution:
+    case ax::mojom::Role::kAnnotationCommentary:
+    case ax::mojom::Role::kAnnotationPresence:
+    case ax::mojom::Role::kAnnotationRevision:
+    case ax::mojom::Role::kAnnotationSuggestion:
+      return ROLE_SYSTEM_GROUPING;
+
     case ax::mojom::Role::kApplication:
       return ROLE_SYSTEM_APPLICATION;
 
@@ -4818,7 +4825,7 @@
     case ax::mojom::Role::kSwitch:
       return ROLE_SYSTEM_CHECKBUTTON;
 
-    case ax::mojom::Role::kAnnotation:
+    case ax::mojom::Role::kRubyAnnotation:
     case ax::mojom::Role::kListMarker:
     case ax::mojom::Role::kStaticText:
       return ROLE_SYSTEM_STATICTEXT;
@@ -5042,6 +5049,12 @@
   int32_t ia2_role = 0;
 
   switch (GetData().role) {
+    case ax::mojom::Role::kAnnotationAttribution:
+    case ax::mojom::Role::kAnnotationCommentary:
+    case ax::mojom::Role::kAnnotationPresence:
+    case ax::mojom::Role::kAnnotationRevision:
+    case ax::mojom::Role::kAnnotationSuggestion:
+      return IA2_ROLE_SECTION;
     case ax::mojom::Role::kBanner:
     case ax::mojom::Role::kHeader:
       // CORE-AAM recommends LANDMARK instead of HEADER.
@@ -5255,6 +5268,13 @@
     case ax::mojom::Role::kAnchor:
       return L"link";
 
+    case ax::mojom::Role::kAnnotationAttribution:
+    case ax::mojom::Role::kAnnotationCommentary:
+    case ax::mojom::Role::kAnnotationPresence:
+    case ax::mojom::Role::kAnnotationRevision:
+    case ax::mojom::Role::kAnnotationSuggestion:
+      return L"group";
+
     case ax::mojom::Role::kApplication:
       return L"application";
 
@@ -5620,7 +5640,7 @@
     case ax::mojom::Role::kSwitch:
       return L"checkbox";
 
-    case ax::mojom::Role::kAnnotation:
+    case ax::mojom::Role::kRubyAnnotation:
     case ax::mojom::Role::kListMarker:
     case ax::mojom::Role::kStaticText:
       return L"description";
@@ -5901,6 +5921,13 @@
     case ax::mojom::Role::kAnchor:
       return UIA_HyperlinkControlTypeId;
 
+    case ax::mojom::Role::kAnnotationAttribution:
+    case ax::mojom::Role::kAnnotationCommentary:
+    case ax::mojom::Role::kAnnotationPresence:
+    case ax::mojom::Role::kAnnotationRevision:
+    case ax::mojom::Role::kAnnotationSuggestion:
+      return ROLE_SYSTEM_GROUPING;
+
     case ax::mojom::Role::kApplication:
       return UIA_PaneControlTypeId;
 
@@ -6264,7 +6291,7 @@
     case ax::mojom::Role::kSwitch:
       return UIA_CheckBoxControlTypeId;
 
-    case ax::mojom::Role::kAnnotation:
+    case ax::mojom::Role::kRubyAnnotation:
     case ax::mojom::Role::kListMarker:
     case ax::mojom::Role::kStaticText:
       return UIA_TextControlTypeId;
diff --git a/ui/aura/window_occlusion_tracker_unittest.cc b/ui/aura/window_occlusion_tracker_unittest.cc
index 74f744cb..30d4435e 100644
--- a/ui/aura/window_occlusion_tracker_unittest.cc
+++ b/ui/aura/window_occlusion_tracker_unittest.cc
@@ -9,6 +9,8 @@
 #include "base/run_loop.h"
 #include "base/test/bind_test_util.h"
 #include "base/test/gtest_util.h"
+#include "base/test/scoped_feature_list.h"
+#include "build/build_config.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "ui/aura/env.h"
 #include "ui/aura/test/aura_test_base.h"
@@ -16,6 +18,7 @@
 #include "ui/aura/test/test_windows.h"
 #include "ui/aura/test/window_occlusion_tracker_test_api.h"
 #include "ui/aura/window_observer.h"
+#include "ui/base/ui_base_features.h"
 #include "ui/compositor/layer_animation_observer.h"
 #include "ui/compositor/layer_animation_sequence.h"
 #include "ui/compositor/layer_animator.h"
@@ -73,6 +76,17 @@
  public:
   WindowOcclusionTrackerTest() = default;
 
+#if defined(OS_WIN)
+  void SetUp() override {
+    // Native Window Occlusion calculation runs in the background and can
+    // interfere with the expectations of these tests, so, disable it.
+    scoped_feature_list_.InitWithFeatures(
+        /*enabled_features=*/{},
+        /*disabled_features=*/{features::kCalculateNativeWinOcclusion});
+    AuraTestBase::SetUp();
+  }
+#endif
+
   Window* CreateTrackedWindow(MockWindowDelegate* delegate,
                               const gfx::Rect& bounds,
                               Window* parent = nullptr,
@@ -102,6 +116,8 @@
   }
 
  private:
+  base::test::ScopedFeatureList scoped_feature_list_;
+
   DISALLOW_COPY_AND_ASSIGN(WindowOcclusionTrackerTest);
 };
 
diff --git a/ui/compositor/test/in_process_context_factory.cc b/ui/compositor/test/in_process_context_factory.cc
index e56d3c2..b2546249 100644
--- a/ui/compositor/test/in_process_context_factory.cc
+++ b/ui/compositor/test/in_process_context_factory.cc
@@ -138,7 +138,11 @@
 
  private:
   void OnSwapBuffersComplete() {
-    client_->DidReceiveSwapBuffersAck(gfx::SwapTimings());
+    // Metrics tracking in OutputSurfaceClient expects non-null SwapTimings
+    // so we provide dummy values here.
+    base::TimeTicks now = base::TimeTicks::Now();
+    gfx::SwapTimings timings = {now, now};
+    client_->DidReceiveSwapBuffersAck(timings);
     client_->DidReceivePresentationFeedback(gfx::PresentationFeedback());
   }
 
diff --git a/ui/file_manager/image_loader/manifest.json b/ui/file_manager/image_loader/manifest.json
index 2dad10d..18449d0 100644
--- a/ui/file_manager/image_loader/manifest.json
+++ b/ui/file_manager/image_loader/manifest.json
@@ -12,11 +12,9 @@
       "fileSystem": ["requestFileSystem"]
     },
     "fileManagerPrivate",
-    "https://www.googledrive.com/",
-    "https://lh3.googleusercontent.com/",
     "storage"
   ],
-  "content_security_policy": "default-src 'none'; script-src 'self' 'wasm-eval' blob: filesystem: chrome://resources chrome-extension://hhaomjibdihmijegdhdafkllkbggdgoj; style-src 'self' blob: filesystem:; frame-src 'self' blob: filesystem:; img-src 'self' blob: filesystem: data:; media-src 'self' blob: filesystem:; connect-src 'self' blob: filesystem: https://www.googledrive.com https://lh3.googleusercontent.com",
+  "content_security_policy": "default-src 'none'; script-src 'self' 'wasm-eval' blob: filesystem: chrome://resources chrome-extension://hhaomjibdihmijegdhdafkllkbggdgoj; style-src 'self' blob: filesystem:; frame-src 'self' blob: filesystem:; img-src 'self' blob: filesystem: data:; media-src 'self' blob: filesystem:; connect-src 'self' blob: filesystem:",
   "background": {
     "scripts": [
       "chrome://resources/js/assert.js",
diff --git a/ui/file_manager/image_loader/request.js b/ui/file_manager/image_loader/request.js
index ba9386b1..5353ad7 100644
--- a/ui/file_manager/image_loader/request.js
+++ b/ui/file_manager/image_loader/request.js
@@ -69,10 +69,10 @@
 
   /**
    * Used to download remote images using http:// or https:// protocols.
-   * @type {AuthorizedXHR}
+   * @type {XMLHttpRequest}
    * @private
    */
-  this.xhr_ = new AuthorizedXHR();
+  this.xhr_ = null;
 
   /**
    * Temporary canvas used to resize and compress the image.
@@ -95,6 +95,12 @@
    * @private
    */
   this.downloadCallback_ = null;
+
+  /**
+   * @type {boolean}
+   * @private
+   */
+  this.aborted_ = false;
 }
 
 /**
@@ -116,6 +122,19 @@
 ImageRequest.MAX_MILLISECONDS_TO_LOAD_VIDEO = 3000;
 
 /**
+ * A map which is used to estimate content type from extension.
+ * @enum {string}
+ */
+ImageRequest.ExtensionContentTypeMap = {
+  gif: 'image/gif',
+  png: 'image/png',
+  svg: 'image/svg',
+  bmp: 'image/bmp',
+  jpg: 'image/jpeg',
+  jpeg: 'image/jpeg'
+};
+
+/**
  * Returns ID of the request.
  * @return {string} Request ID.
  */
@@ -310,7 +329,7 @@
     return;
   }
 
-  // Fetch the image via authorized XHR and parse it.
+  // Fetch the image via XHR and parse it.
   var parseImage = function(contentType, blob) {
     if (contentType) {
       this.contentType_ = contentType;
@@ -319,7 +338,7 @@
   }.bind(this);
 
   // Request raw data via XHR.
-  this.xhr_.load(this.request_.url, parseImage, onFailure);
+  this.load(this.request_.url, parseImage, onFailure);
 };
 
 /**
@@ -366,47 +385,14 @@
 };
 
 /**
- * Creates a XmlHttpRequest wrapper with injected OAuth2 authentication headers.
- * @constructor
- */
-function AuthorizedXHR() {
-  this.xhr_ = null;
-  this.aborted_ = false;
-}
-
-/**
- * A map which is used to estimate content type from extension.
- * @enum {string}
- */
-AuthorizedXHR.ExtensionContentTypeMap = {
-  gif: 'image/gif',
-  png: 'image/png',
-  svg: 'image/svg',
-  bmp: 'image/bmp',
-  jpg: 'image/jpeg',
-  jpeg: 'image/jpeg'
-};
-
-/**
- * Aborts the current request (if running).
- */
-AuthorizedXHR.prototype.abort = function() {
-  this.aborted_ = true;
-  if (this.xhr_) {
-    this.xhr_.abort();
-  }
-};
-
-/**
- * Loads an image using a OAuth2 token. If it fails, then tries to retry with
- * a refreshed OAuth2 token.
+ * Loads an image.
  *
  * @param {string} url URL to the resource to be fetched.
  * @param {function(string, Blob)} onSuccess Success callback with the content
  *     type and the fetched data.
  * @param {function()} onFailure Failure callback.
  */
-AuthorizedXHR.prototype.load = function(url, onSuccess, onFailure) {
+ImageRequest.prototype.load = function(url, onSuccess, onFailure) {
   this.aborted_ = false;
 
   // Do not call any callbacks when aborting.
@@ -414,8 +400,8 @@
       function(contentType, response) {
         // When content type is not available, try to estimate it from url.
         if (!contentType) {
-          contentType = AuthorizedXHR.ExtensionContentTypeMap[
-              this.extractExtension_(url)];
+          contentType =
+              ImageRequest.ExtensionContentTypeMap[this.extractExtension_(url)];
         }
 
         if (!this.aborted_) {
@@ -429,63 +415,25 @@
     }
   }.bind(this));
 
-  // Fetches the access token and makes an authorized call. If refresh is true,
-  // then forces refreshing the access token.
-  var requestTokenAndCall = function(refresh, onInnerSuccess, onInnerFailure) {
-    chrome.fileManagerPrivate.requestAccessToken(refresh, function(token) {
-      if (this.aborted_) {
-        return;
-      }
-      if (!token) {
-        onInnerFailure();
-        return;
-      }
-      this.xhr_ = AuthorizedXHR.load_(
-          token, url, onInnerSuccess, onInnerFailure);
-    }.bind(this));
-  }.bind(this);
-
-  // Refreshes the access token and retries the request.
-  var maybeRetryCall = function(code) {
-    if (this.aborted_) {
-      return;
-    }
-    requestTokenAndCall(true, onMaybeSuccess, onMaybeFailure);
-  }.bind(this);
-
-  // Do not request a token for local resources, since it is not necessary.
-  if (/^filesystem:/.test(url)) {
-    // The query parameter is workaround for
-    // crbug.com/379678, which force to obtain the latest contents of the image.
-    var noCacheUrl = url + '?nocache=' + Date.now();
-    this.xhr_ = AuthorizedXHR.load_(
-        null,
-        noCacheUrl,
-        onMaybeSuccess,
-        onMaybeFailure);
-    return;
-  }
-
-  // Make the request with reusing the current token. If it fails, then retry.
-  requestTokenAndCall(false, onMaybeSuccess, maybeRetryCall);
+  // The query parameter is workaround for crbug.com/379678, which forces the
+  // browser to obtain the latest contents of the image.
+  var noCacheUrl = url + '?nocache=' + Date.now();
+  this.xhr_ = ImageRequest.load_(noCacheUrl, onMaybeSuccess, onMaybeFailure);
 };
 
 /**
  * Extracts extension from url.
  * @param {string} url Url.
- * @return {string} Extracted extensiion, e.g. png.
+ * @return {string} Extracted extension, e.g. png.
  */
-AuthorizedXHR.prototype.extractExtension_ = function(url) {
+ImageRequest.prototype.extractExtension_ = function(url) {
   var result = (/\.([a-zA-Z]+)$/i).exec(url);
   return result ? result[1] : '';
 };
 
 /**
- * Fetches data using authorized XmlHttpRequest with the provided OAuth2 token.
- * If the token is invalid, the request will fail.
+ * Fetches data using XmlHttpRequest.
  *
- * @param {?string} token OAuth2 token to be injected to the request. Null for
- *     no token.
  * @param {string} url URL to the resource to be fetched.
  * @param {function(string, Blob)} onSuccess Success callback with the content
  *     type and the fetched data.
@@ -494,7 +442,7 @@
  * @return {XMLHttpRequest} XHR instance.
  * @private
  */
-AuthorizedXHR.load_ = function(token, url, onSuccess, onFailure) {
+ImageRequest.load_ = function(url, onSuccess, onFailure) {
   let xhr = new XMLHttpRequest();
   xhr.responseType = 'blob';
 
@@ -514,9 +462,6 @@
   // Perform a xhr request.
   try {
     xhr.open('GET', url, true);
-    if (token) {
-      xhr.setRequestHeader('Authorization', 'Bearer ' + token);
-    }
     xhr.send();
   } catch (e) {
     onFailure();
@@ -641,7 +586,10 @@
   this.image_.src = 'data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAA' +
       'ABAAEAAAICTAEAOw==';
 
-  this.xhr_.abort();
+  this.aborted_ = true;
+  if (this.xhr_) {
+    this.xhr_.abort();
+  }
 
   // Dispose memory allocated by Canvas.
   this.canvas_.width = 0;
diff --git a/ui/file_manager/integration_tests/file_manager/context_menu.js b/ui/file_manager/integration_tests/file_manager/context_menu.js
index f9cf458e..d95bcd5 100644
--- a/ui/file_manager/integration_tests/file_manager/context_menu.js
+++ b/ui/file_manager/integration_tests/file_manager/context_menu.js
@@ -356,6 +356,50 @@
 };
 
 /**
+ * Tests that the "Replace your Linux apps and files" file context menu item is
+ * hidden for a *.tini file if Crostini backup is disabled.
+ */
+testcase.checkImportCrostiniImageDisabled = async () => {
+  const optionHidden = '#file-context-menu:not([hidden]) ' +
+      '[command="#default-task"][hidden]';
+
+  // Open FilesApp on Downloads with test.tini file.
+  const appId =
+      await setupAndWaitUntilReady(RootPath.DOWNLOADS, [ENTRIES.tiniFile], []);
+
+  // Disable Crostini backup.
+  await sendTestMessage(
+      {name: 'setCrostiniExportImportAllowed', enabled: false});
+
+  // Select and right click the tini file to show its context menu.
+  await selectFile(appId, 'test.tini');
+  await rightClickSelectedFile(appId);
+
+  // Check: the context menu item should be hidden.
+  await remoteCall.waitForElement(appId, optionHidden);
+};
+
+/**
+ * Tests that the "Replace your Linux apps and files" file context menu item is
+ * shown for a *.tini file if Crostini backup is enabled.
+ */
+testcase.checkImportCrostiniImageEnabled = async () => {
+  const optionShown = '#file-context-menu:not([hidden]) ' +
+      '[command="#default-task"]:not([hidden])';
+
+  // Open FilesApp on Downloads with test.tini file.
+  const appId =
+      await setupAndWaitUntilReady(RootPath.DOWNLOADS, [ENTRIES.tiniFile], []);
+
+  // Select and right click the tini file to show its context menu.
+  await selectFile(appId, 'test.tini');
+  await rightClickSelectedFile(appId);
+
+  // Check: the context menu item should be shown.
+  await remoteCall.waitForElement(appId, optionShown);
+};
+
+/**
  * Tests that text selection context menus are disabled in tablet mode.
  */
 testcase.checkContextMenusForInputElements = async () => {
diff --git a/ui/file_manager/integration_tests/test_util.js b/ui/file_manager/integration_tests/test_util.js
index 47bd9c1..fb90e1e9 100644
--- a/ui/file_manager/integration_tests/test_util.js
+++ b/ui/file_manager/integration_tests/test_util.js
@@ -911,6 +911,17 @@
     typeText: 'DEB file'
   }),
 
+  tiniFile: new TestEntryInfo({
+    type: EntryType.FILE,
+    sourceFileName: 'archive.tar.gz',
+    targetPath: 'test.tini',
+    mimeType: 'application/gzip',
+    lastModifiedTime: 'Jan 1, 2014, 1:00 AM',
+    nameText: 'test.tini',
+    sizeText: '439 bytes',
+    typeText: 'Crostini image file'
+  }),
+
   hiddenFile: new TestEntryInfo({
     type: EntryType.FILE,
     sourceFileName: 'text.txt',
diff --git a/ui/gfx/mojom/buffer_types.mojom b/ui/gfx/mojom/buffer_types.mojom
index 4bdbccec..3cce5e66 100644
--- a/ui/gfx/mojom/buffer_types.mojom
+++ b/ui/gfx/mojom/buffer_types.mojom
@@ -23,7 +23,6 @@
   RGBA_F16,
   YVU_420,
   YUV_420_BIPLANAR,
-  UYVY_422,
   P010,
 };
 
diff --git a/ui/gfx/mojom/buffer_types_mojom_traits.h b/ui/gfx/mojom/buffer_types_mojom_traits.h
index 3d417f18..31d0a587 100644
--- a/ui/gfx/mojom/buffer_types_mojom_traits.h
+++ b/ui/gfx/mojom/buffer_types_mojom_traits.h
@@ -102,9 +102,6 @@
       case gfx::mojom::BufferFormat::P010:
         *out = gfx::BufferFormat::P010;
         return true;
-      // TODO(crbug.com/988538): remove
-      case gfx::mojom::BufferFormat::UYVY_422:
-        return false;
     }
     NOTREACHED();
     return false;
diff --git a/ui/login/account_picker/chromeos_user_pod_row.js b/ui/login/account_picker/chromeos_user_pod_row.js
index 403ea6f3..5436e436 100644
--- a/ui/login/account_picker/chromeos_user_pod_row.js
+++ b/ui/login/account_picker/chromeos_user_pod_row.js
@@ -63,13 +63,6 @@
   var POD_SWITCH_ANIMATION_DURATION_MS = 180;
 
   /**
-   * Public session help topic identifier.
-   * @type {number}
-   * @const
-   */
-  var HELP_TOPIC_PUBLIC_SESSION = 3041033;
-
-  /**
    * Tab order for user pods. Update these when adding new controls.
    * @enum {number}
    * @const
@@ -2469,11 +2462,6 @@
         }
       }).bind(this));
 
-      var learnMore = this.querySelector('.learn-more');
-      learnMore.addEventListener('mousedown', stopEventPropagation);
-      learnMore.addEventListener('click', this.handleLearnMoreEvent);
-      learnMore.addEventListener('keydown', this.handleLearnMoreEvent);
-
       var languageSelect = this.querySelector('.language-select');
       languageSelect.tabIndex = UserPodTabOrder.POD_INPUT;
       languageSelect.manuallyChanged = false;
@@ -2593,34 +2581,6 @@
     },
 
     /**
-     * Handle mouse and keyboard events for the learn more button. Triggering
-     * the button causes information about public sessions to be shown.
-     * @param {Event} event Mouse or keyboard event.
-     */
-    handleLearnMoreEvent: function(event) {
-      switch (event.type) {
-        // Show informaton on left click. Let any other clicks propagate.
-        case 'click':
-          if (event.button != 0)
-            return;
-          break;
-        // Show informaton when <Return> or <Space> is pressed. Let any other
-        // key presses propagate.
-        case 'keydown':
-          switch (event.keyCode) {
-            case 13:  // Return.
-            case 32:  // Space.
-              break;
-            default:
-              return;
-          }
-          break;
-      }
-      chrome.send('launchHelpApp', [HELP_TOPIC_PUBLIC_SESSION]);
-      stopEventPropagation(event);
-    },
-
-    /**
      * Transition the expanded pod from the basic to the advanced view.
      */
     transitionToAdvanced_: function() {
diff --git a/ui/login/account_picker/user_pod_row.js b/ui/login/account_picker/user_pod_row.js
index 5fcb01ad..1f24d26 100644
--- a/ui/login/account_picker/user_pod_row.js
+++ b/ui/login/account_picker/user_pod_row.js
@@ -77,13 +77,6 @@
   var POD_ROW_IMAGES_LOAD_TIMEOUT_MS = 3000;
 
   /**
-   * Public session help topic identifier.
-   * @type {number}
-   * @const
-   */
-  var HELP_TOPIC_PUBLIC_SESSION = 3041033;
-
-  /**
    * Tab order for user pods. Update these when adding new controls.
    * @enum {number}
    * @const
@@ -2112,15 +2105,6 @@
         }
       }).bind(this));
 
-      var learnMore = this.querySelector('.learn-more');
-      learnMore.addEventListener('mousedown', stopEventPropagation);
-      learnMore.addEventListener('click', this.handleLearnMoreEvent);
-      learnMore.addEventListener('keydown', this.handleLearnMoreEvent);
-
-      learnMore = this.querySelector('.expanded-pane-learn-more');
-      learnMore.addEventListener('click', this.handleLearnMoreEvent);
-      learnMore.addEventListener('keydown', this.handleLearnMoreEvent);
-
       var languageSelect = this.querySelector('.language-select');
       languageSelect.tabIndex = UserPodTabOrder.POD_INPUT;
       languageSelect.manuallyChanged = false;
@@ -2233,34 +2217,6 @@
       this.update();
     },
 
-    /**
-     * Handle mouse and keyboard events for the learn more button. Triggering
-     * the button causes information about public sessions to be shown.
-     * @param {Event} event Mouse or keyboard event.
-     */
-    handleLearnMoreEvent: function(event) {
-      switch (event.type) {
-        // Show informaton on left click. Let any other clicks propagate.
-        case 'click':
-          if (event.button != 0)
-            return;
-          break;
-        // Show informaton when <Return> or <Space> is pressed. Let any other
-        // key presses propagate.
-        case 'keydown':
-          switch (event.keyCode) {
-            case 13:  // Return.
-            case 32:  // Space.
-              break;
-            default:
-              return;
-          }
-          break;
-      }
-      chrome.send('launchHelpApp', [HELP_TOPIC_PUBLIC_SESSION]);
-      stopEventPropagation(event);
-    },
-
     makeSpaceForExpandedPod_: function() {
       var width = this.classList.contains('advanced') ?
           PUBLIC_EXPANDED_ADVANCED_WIDTH : PUBLIC_EXPANDED_BASIC_WIDTH;
diff --git a/ui/login/display_manager.js b/ui/login/display_manager.js
index 445bf69a..b7546c9 100644
--- a/ui/login/display_manager.js
+++ b/ui/login/display_manager.js
@@ -102,15 +102,6 @@
   var MAX_SCREEN_TRANSITION_DURATION = 250;
 
   /**
-   * Groups of screens (screen IDs) that should have the same dimensions.
-   * @type Array<Array<string>>
-   * @const
-   */
-  var SCREEN_GROUPS = [[
-    SCREEN_OOBE_NETWORK, SCREEN_OOBE_EULA, SCREEN_OOBE_UPDATE,
-    SCREEN_OOBE_AUTO_ENROLLMENT_CHECK
-  ]];
-  /**
    * Group of screens (screen IDs) where factory-reset screen invocation is
    * available.
    * @type Array<string>
@@ -167,11 +158,6 @@
   ];
 
   /**
-   * OOBE screens group index.
-   */
-  var SCREEN_GROUP_OOBE = 0;
-
-  /**
    * Constructor a display manager that manages initialization of screens,
    * transitions, error messages display.
    *
@@ -265,7 +251,7 @@
     },
 
     /**
-     * Returns dimensions of screen exluding header bar.
+     * Returns dimensions of screen excluding header bar.
      * @type {Object}
      */
     get clientAreaSize() {
@@ -631,7 +617,6 @@
           innerContainer.classList.remove('down');
           innerContainer.addEventListener('transitionend', function f(e) {
             innerContainer.removeEventListener('transitionend', f);
-            outerContainer.classList.remove('down');
             chrome.send('loginVisible', ['oobe']);
             // Refresh defaultControl. It could have changed.
             var defaultControl = newStep.defaultControl;
@@ -756,10 +741,6 @@
       }
       this.appendButtons_(el.buttons, screenId);
 
-      if (attributes && attributes.commonScreenSize) {
-        SCREEN_GROUPS[0].push(screenId);
-      }
-
       if (el.updateOobeConfiguration && this.oobe_configuration_)
         el.updateOobeConfiguration(this.oobe_configuration_);
     },
@@ -785,22 +766,7 @@
 
       var width = screen.getPreferredSize().width;
       var height = screen.getPreferredSize().height;
-      for (let i = 0; i < SCREEN_GROUPS.length; ++i) {
-        let screenGroup = SCREEN_GROUPS[i];
-        if (screenGroup.indexOf(screen.id) != -1) {
-          // Set screen dimensions to maximum dimensions within this group.
-          for (let j = 0; j < screenGroup.length; ++j) {
-            let screen2 = $(screenGroup[j]);
-            // Other screens in this screen group might be missing if we're not
-            // in OOBE.
-            if (!screen2)
-              continue;
-            width = Math.max(width, screen2.getPreferredSize().width);
-            height = Math.max(height, screen2.getPreferredSize().height);
-          }
-          break;
-        }
-      }
+
       if (!this.isAdaptiveOobe) {
         if (screen.classList.contains('fullscreen')) {
           $('inner-container').style.height = '100%';
@@ -876,18 +842,6 @@
       }
     },
 
-    /**
-     * Initialized first group of OOBE screens.
-     */
-    initializeOOBEScreens: function() {
-      if (this.isOobeUI() && $('inner-container').classList.contains('down')) {
-        for (let i = 0; i < SCREEN_GROUPS[SCREEN_GROUP_OOBE].length; ++i) {
-          let screen = $(SCREEN_GROUPS[SCREEN_GROUP_OOBE][i]);
-          screen.hidden = false;
-        }
-      }
-    },
-
     /** Initializes demo mode start listener. */
     initializeDemoModeMultiTapListener: function() {
       if (this.displayType_ == DISPLAY_TYPE.OOBE) {
@@ -1077,7 +1031,6 @@
       instance.displayType = DISPLAY_TYPE.LOGIN;
     }
 
-    instance.initializeOOBEScreens();
     instance.initializeDemoModeMultiTapListener();
 
     window.addEventListener('resize', instance.onWindowResize_.bind(instance));
diff --git a/ui/login/display_manager_types.js b/ui/login/display_manager_types.js
index ed3bac4..4c023aae5 100644
--- a/ui/login/display_manager_types.js
+++ b/ui/login/display_manager_types.js
@@ -9,7 +9,6 @@
 
 /**
  * @typedef {{
- *   commonScreenSize: (boolean|undefined),
  *   enableDebuggingAllowed: (boolean|undefined),
  *   enterDemoModeAllowed: (boolean|undefined),
  *   noAnimatedTransition: (boolean|undefined),
@@ -22,13 +21,6 @@
 var DisplayManagerScreenAttributes = {};
 
 /**
- * True when screen should have size matched with others.
- * (i.e. it's a part of main flow)
- * @type {boolean|undefined}
- */
-DisplayManagerScreenAttributes.commonScreenSize;
-
-/**
  * True if showing "enable debugging" is allowed for the screen.
  * @type {boolean|undefined}
  */
diff --git a/ui/strings/ui_strings.grd b/ui/strings/ui_strings.grd
index 2669d72db..9a6daa6 100644
--- a/ui/strings/ui_strings.grd
+++ b/ui/strings/ui_strings.grd
@@ -426,12 +426,6 @@
 
       <!--Accessible name strings-->
       <if expr="is_macosx">
-        <message name="IDS_ACCNAME_CONTENT_DELETION_ROLE_DESCRIPTION" desc="Accessibility role description for content deletion, meaning content that is has been or is suggested to be removed from a document, such as in a revision review" meaning="UI element">
-          Deletion
-        </message>
-        <message name="IDS_ACCNAME_CONTENT_INSERTION_ROLE_DESCRIPTION" desc="Accessibility role description for content insertion, meaning content that is has been or is suggested to be inserted into a document, such as in a revision review" meaning="UI element">
-          Insertion
-        </message>
         <message name="IDS_ACCNAME_TAB_ROLE_DESCRIPTION" desc="A generic description of a tab button's role." meaning="UI element">
           Tab
         </message>
diff --git a/ui/views/controls/label.cc b/ui/views/controls/label.cc
index e992238..1837167 100644
--- a/ui/views/controls/label.cc
+++ b/ui/views/controls/label.cc
@@ -512,10 +512,6 @@
   return height + GetInsets().height();
 }
 
-void Label::Layout() {
-  ClearDisplayText();
-}
-
 View* Label::GetTooltipHandlerForPoint(const gfx::Point& point) {
   if (!handles_tooltips_ ||
       (tooltip_text_.empty() && !ShouldShowDefaultTooltip()))
@@ -638,6 +634,7 @@
 void Label::OnBoundsChanged(const gfx::Rect& previous_bounds) {
   if (previous_bounds.size() != size())
     InvalidateLayout();
+  ClearDisplayText();
 }
 
 void Label::OnPaint(gfx::Canvas* canvas) {
diff --git a/ui/views/controls/label.h b/ui/views/controls/label.h
index bc90b6f..aea6bb5 100644
--- a/ui/views/controls/label.h
+++ b/ui/views/controls/label.h
@@ -246,7 +246,6 @@
   gfx::Size CalculatePreferredSize() const override;
   gfx::Size GetMinimumSize() const override;
   int GetHeightForWidth(int w) const override;
-  void Layout() override;
   View* GetTooltipHandlerForPoint(const gfx::Point& point) override;
   bool CanProcessEventsWithinSubtree() const override;
   WordLookupClient* GetWordLookupClient() override;
diff --git a/ui/views/linux_ui/linux_ui.h b/ui/views/linux_ui/linux_ui.h
index c1b3a57b..9f89eb2 100644
--- a/ui/views/linux_ui/linux_ui.h
+++ b/ui/views/linux_ui/linux_ui.h
@@ -16,7 +16,6 @@
 #include "ui/shell_dialogs/shell_dialog_linux.h"
 #include "ui/views/buildflags.h"
 #include "ui/views/controls/button/button.h"
-#include "ui/views/linux_ui/status_icon_linux.h"
 #include "ui/views/views_export.h"
 
 // The main entrypoint into Linux toolkit specific code. GTK code should only
@@ -125,16 +124,6 @@
   virtual void SetDownloadCount(int count) const = 0;
   virtual void SetProgressFraction(float percentage) const = 0;
 
-  // Checks for platform support for status icons.
-  virtual bool IsStatusIconSupported() const = 0;
-
-  // Create a native status icon. The id_prefix is used to distinguish Chrome's
-  // status icons from other apps' status icons, and should be unique.
-  virtual std::unique_ptr<StatusIconLinux> CreateLinuxStatusIcon(
-      const gfx::ImageSkia& image,
-      const base::string16& tool_tip,
-      const char* id_prefix) const = 0;
-
   // Returns the icon for a given content type from the icon theme.
   // TODO(davidben): Add an observer for the theme changing, so we can drop the
   // caches.
diff --git a/ui/webui/resources/cr_elements/chromeos/network/cr_network_listener_behavior.js b/ui/webui/resources/cr_elements/chromeos/network/cr_network_listener_behavior.js
index f13b4a2..08695444 100644
--- a/ui/webui/resources/cr_elements/chromeos/network/cr_network_listener_behavior.js
+++ b/ui/webui/resources/cr_elements/chromeos/network/cr_network_listener_behavior.js
@@ -25,24 +25,19 @@
   // CrosNetworkConfigObserver methods. Override these in the implementation.
 
   /**
-   * CrosNetworkConfigObserver impl
    * @param {!Array<chromeos.networkConfig.mojom.NetworkStateProperties>}
    *     activeNetworks
    */
   onActiveNetworksChanged: function(activeNetworks) {},
 
-  /**
-   * CrosNetworkConfigObserver impl
-   * @param {!chromeos.networkConfig.mojom.NetworkStateProperties} network
-   */
+  /** @param {!chromeos.networkConfig.mojom.NetworkStateProperties} network */
   onNetworkStateChanged: function(network) {},
 
-  /** CrosNetworkConfigObserver impl */
   onNetworkStateListChanged: function() {},
 
-  /** CrosNetworkConfigObserver impl */
   onDeviceStateListChanged: function() {},
 
-  /** CrosNetworkConfigObserver impl */
   onVpnProvidersChanged: function() {},
+
+  onNetworkCertificatesChanged: function() {},
 };
diff --git a/ui/webui/resources/cr_elements/cr_toolbar/cr_toolbar_search_field.js b/ui/webui/resources/cr_elements/cr_toolbar/cr_toolbar_search_field.js
index ae9a1ed..3850a43d 100644
--- a/ui/webui/resources/cr_elements/cr_toolbar/cr_toolbar_search_field.js
+++ b/ui/webui/resources/cr_elements/cr_toolbar/cr_toolbar_search_field.js
@@ -121,14 +121,12 @@
     }
   },
 
-  /**
-   * @param {Event} e
-   * @private
-   */
+  /** @private */
   showSearch_: function(e) {
-    if (e.target != this.$.clearSearch) {
-      this.showingSearch = true;
-    }
+    const clearSearchButton = this.$$('#clearSearch');
+    const wasClearSearchClicked = e.composedPath().includes(clearSearchButton);
+
+    this.showingSearch = !wasClearSearchClicked;
   },
 
   /**