diff --git a/.gitignore b/.gitignore
index 64e3765..e5568f67 100644
--- a/.gitignore
+++ b/.gitignore
@@ -293,3 +293,6 @@
 
 # Ignore any Android RenderTest goldens
 **/render_tests/*.png
+
+# Ignore IntelliJ files.
+.idea/
diff --git a/.gn b/.gn
index 17ea97b..0591fc5 100644
--- a/.gn
+++ b/.gn
@@ -315,6 +315,7 @@
   "//testing/*",
 
   #"//third_party/*",  # May not ever want this.
+  "//third_party:freetype_harfbuzz",
   "//third_party/Python-Markdown/*",
 
   # "//third_party/SPIRV-Tools/*",  # 30ish errors
@@ -592,7 +593,6 @@
   "//third_party/wtl/*",
   "//third_party/xdg-utils/*",
   "//third_party/xstream/*",
-  "//third_party/yara/*",
   "//third_party/yasm/*",
   "//third_party/zlib/*",
   "//third_party/woff2/*",
diff --git a/AUTHORS b/AUTHORS
index 522cfbd..b1f3fdbf 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -811,7 +811,6 @@
 Shaobo Yan <shaobo.yan@intel.com>
 Shashi Kumar <sk.kumar@samsung.com>
 Shawn Anastasio <shawnanastasio@gmail.com>
-Shelley Vohr <shelley.vohr@gmail.com>
 Shen Yu <shenyu.tcv@gmail.com>
 Sherry Mou <wenjinm@amazon.com>
 Shez Baig <sbaig1@bloomberg.net>
@@ -904,6 +903,7 @@
 Umar Hansa <umar.hansa@gmail.com>
 Upendra Gowda <upendrag.gowda@gmail.com>
 Uzair Jaleel <uzair.jaleel@samsung.com>
+Vadim Gorbachev <bmsdave@gmail.com>
 Vaibhav Agrawal <vaibhav1.a@samsung.com>
 Valentin Ilie <valentin.ilie@intel.com>
 Vamshikrishna Yellenki <vamshi@motorola.com>
diff --git a/DEPS b/DEPS
index d04c969..8beb64bd 100644
--- a/DEPS
+++ b/DEPS
@@ -68,6 +68,12 @@
   # restricted to Googlers only.
   'checkout_chromium_autofill_test_dependencies': False,
 
+  # By default, do not check out Chromium password manager captured sites test
+  # dependencies. These dependencies include very large numbers of very
+  # large web capture files. Captured sites test dependencies are also
+  # restricted to Googlers only.
+  'checkout_chromium_password_manager_test_dependencies': False,
+
   # Check out and download nacl by default. This can be disabled e.g. with
   # custom_vars.
   'checkout_nacl': True,
@@ -127,11 +133,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': 'fbc887df72ec102843c009977b24ed5b4bb2fab1',
+  'skia_revision': '24b0b59e4409f41d8e6749a32001a7fdbfe18e7e',
   # 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': 'd8b0d88de4b7d73ea02abb8511c146944d6ccf67',
+  'v8_revision': '3e8a733af17a7812eba188dad612be503bd45c57',
   # 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.
@@ -139,11 +145,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': 'b8cff9e9347cee1fe46092a10ae039df2fdd4c33',
+  'angle_revision': 'f094bac949922dabc3dd6944f3dec04111e953b6',
   # 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': 'd69cdaba1445a6dc99be8309175b10cc2b979209',
+  'swiftshader_revision': '4c84dbdce271d152f95a73a244569c522a0239f7',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling PDFium
   # and whatever else without interference from each other.
@@ -178,7 +184,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling freetype
   # and whatever else without interference from each other.
-  'freetype_revision': 'a6feefdfefd6eb8e62728f40638241bb1d8c993e',
+  'freetype_revision': '6d65c60fca0ebce88e2bcfeac92a7a791e03bf42',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling HarfBuzz
   # and whatever else without interference from each other.
@@ -238,7 +244,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling feed
   # and whatever else without interference from each other.
-  'spv_tools_revision': '5fb83a97087d4171c45fc3f0dcbaa5c43e195536',
+  'spv_tools_revision': '86f6ac086c8bb8323fb94a0977278d7466d6ebd4',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling feed
   # and whatever else without interference from each other.
@@ -353,6 +359,17 @@
     'dep_type': 'cipd',
   },
 
+  'src/chrome/test/data/password/captured_sites': {
+    'packages': [
+      {
+        'package': 'chromium/chrome/test/data/password/captured_sites',
+        'version': 'jYvTM_KjFi4v_BTqCfkAxM1XJ4PkMDSi31FgEDf0z9sC',
+      }
+    ],
+    'condition': 'checkout_chromium_password_manager_test_dependencies',
+    'dep_type': 'cipd',
+  },
+
   'src/chrome/test/data/perf/canvas_bench':
     Var('chromium_git') + '/chromium/canvas_bench.git' + '@' + 'a7b40ea5ae0239517d78845a5fc9b12976bfc732',
 
@@ -649,7 +666,7 @@
     Var('chromium_git') + '/angle/angle.git' + '@' +  Var('angle_revision'),
 
   'src/third_party/dav1d/libdav1d':
-    Var('chromium_git') + '/external/github.com/videolan/dav1d.git' + '@' + '493155af054cf29a4dead7ffe92acd5d6b3d6a59',
+    Var('chromium_git') + '/external/github.com/videolan/dav1d.git' + '@' + '86ce4a3c50c08f0aaeb0fc341013ca9af61f9e98',
 
   'src/third_party/dawn':
     Var('dawn_git') + '/dawn.git' + '@' +  Var('dawn_revision'),
@@ -735,7 +752,7 @@
 
   # Build tools for Chrome OS. Note: This depends on third_party/pyelftools.
   'src/third_party/chromite': {
-      'url': Var('chromium_git') + '/chromiumos/chromite.git' + '@' + '2905eac7889deb0ffe2e8833a3bc777bfda7189a',
+      'url': Var('chromium_git') + '/chromiumos/chromite.git' + '@' + '3a9f8f45de46efae4eea2758dc6922634d121d58',
       'condition': 'checkout_linux',
   },
 
@@ -760,7 +777,7 @@
   },
 
   'src/third_party/depot_tools':
-    Var('chromium_git') + '/chromium/tools/depot_tools.git' + '@' + 'adbf3bef68724e31ddd7a5ab9e7b111a4d898829',
+    Var('chromium_git') + '/chromium/tools/depot_tools.git' + '@' + '7e68767e2fe21638044fb7cc020a74db33086e77',
 
   'src/third_party/devtools-node-modules':
     Var('chromium_git') + '/external/github.com/ChromeDevTools/devtools-node-modules' + '@' + Var('devtools_node_modules_revision'),
@@ -829,7 +846,7 @@
   },
 
   'src/third_party/glslang/src':
-    Var('chromium_git') + '/external/github.com/KhronosGroup/glslang.git' + '@' + '9390154c557e6211e273628805a1827a4738c751',
+    Var('chromium_git') + '/external/github.com/KhronosGroup/glslang.git' + '@' + 'cc4a756d48e9c49d31265962c85fee28b5828e5f',
 
   'src/third_party/google_toolbox_for_mac/src': {
       'url': Var('chromium_git') + '/external/github.com/google/google-toolbox-for-mac.git' + '@' + Var('google_toolbox_for_mac_revision'),
@@ -1099,7 +1116,7 @@
   },
 
   'src/third_party/perfetto':
-    Var('android_git') + '/platform/external/perfetto.git' + '@' +  '156cf83837bcdfab014a4485e2023344740b0d7b',
+    Var('android_git') + '/platform/external/perfetto.git' + '@' +  'eb748c338927823601af8d364e1e172cd590f01c',
 
   'src/third_party/perl': {
       'url': Var('chromium_git') + '/chromium/deps/perl.git' + '@' + 'ac0d98b5cee6c024b0cffeb4f8f45b6fc5ccdb78',
@@ -1273,7 +1290,7 @@
     Var('chromium_git') + '/external/khronosgroup/webgl.git' + '@' + 'a0f51b2e123f39c9ff12e621b0b47dd28dd64424',
 
   'src/third_party/webrtc':
-    Var('webrtc_git') + '/src.git' + '@' + 'b678940d3a444c9bf5b53a0bc2e2951f222dab6a',
+    Var('webrtc_git') + '/src.git' + '@' + 'd36c08623d9085dad821fbb1462a7fe03badcef5',
 
   'src/third_party/xdg-utils': {
       'url': Var('chromium_git') + '/chromium/deps/xdg-utils.git' + '@' + 'd80274d5869b17b8c9067a1022e4416ee7ed5e0d',
@@ -1314,7 +1331,7 @@
     Var('chromium_git') + '/v8/v8.git' + '@' +  Var('v8_revision'),
 
   'src-internal': {
-    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@f87f16a7d1618cce8a62d42b008c3eb077f28cb1',
+    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@327e9ae49a2d7bbfdef893e5977dd92c9dae9d1c',
     'condition': 'checkout_src_internal',
   },
 
diff --git a/android_webview/BUILD.gn b/android_webview/BUILD.gn
index 1a6c750..ccad462 100644
--- a/android_webview/BUILD.gn
+++ b/android_webview/BUILD.gn
@@ -740,6 +740,9 @@
     "//components/autofill/content/renderer",
     "//components/cdm/browser",
     "//components/cdm/renderer",
+    "//components/content_capture/android",
+    "//components/content_capture/browser",
+    "//components/content_capture/renderer",
     "//components/crash/android:crashpad_main",
     "//components/crash/content/app",
     "//components/crash/content/browser",
@@ -927,6 +930,7 @@
     "//components/autofill/android:autofill_java",
     "//components/autofill/android:provider_java",
     "//components/background_task_scheduler:background_task_scheduler_task_ids_java",
+    "//components/content_capture/android:java",
     "//components/crash/android:handler_java",
     "//components/crash/android:java",
     "//components/embedder_support/android:web_contents_delegate_java",
diff --git a/android_webview/DEPS b/android_webview/DEPS
index baec786..8ac381e 100644
--- a/android_webview/DEPS
+++ b/android_webview/DEPS
@@ -7,6 +7,7 @@
   # lib is the top-level target, and must remain a leaf in the dependency tree.
   "-android_webview/lib",
 
+  "+components/content_capture",
   "+components/heap_profiling",
   "+components/google/core",
   "+components/network_session_configurator/common",
diff --git a/android_webview/browser/aw_content_browser_client.cc b/android_webview/browser/aw_content_browser_client.cc
index de72b7a..d1c8ebaa 100644
--- a/android_webview/browser/aw_content_browser_client.cc
+++ b/android_webview/browser/aw_content_browser_client.cc
@@ -54,6 +54,7 @@
 #include "base/task/post_task.h"
 #include "components/autofill/content/browser/content_autofill_driver_factory.h"
 #include "components/cdm/browser/cdm_message_filter_android.h"
+#include "components/content_capture/browser/content_capture_receiver_manager.h"
 #include "components/crash/content/browser/crash_handler_host_linux.h"
 #include "components/navigation_interception/intercept_navigation_delegate.h"
 #include "components/policy/content/policy_blacklist_navigation_throttle.h"
@@ -755,6 +756,13 @@
         autofill::mojom::AutofillDriverAssociatedRequest(std::move(*handle)),
         render_frame_host);
     return true;
+  } else if (interface_name ==
+             content_capture::mojom::ContentCaptureReceiver::Name_) {
+    content_capture::ContentCaptureReceiverManager::BindContentCaptureReceiver(
+        content_capture::mojom::ContentCaptureReceiverAssociatedRequest(
+            std::move(*handle)),
+        render_frame_host);
+    return true;
   }
 
   return false;
diff --git a/android_webview/browser/aw_contents.cc b/android_webview/browser/aw_contents.cc
index 658e19f..6e5772d 100644
--- a/android_webview/browser/aw_contents.cc
+++ b/android_webview/browser/aw_contents.cc
@@ -62,6 +62,7 @@
 #include "components/autofill/content/browser/content_autofill_driver_factory.h"
 #include "components/autofill/core/browser/autofill_manager.h"
 #include "components/autofill/core/browser/webdata/autofill_webdata_service.h"
+#include "components/content_capture/android/content_capture_receiver_manager_android.h"
 #include "components/navigation_interception/intercept_navigation_delegate.h"
 #include "content/public/browser/android/child_process_importance.h"
 #include "content/public/browser/android/synchronous_compositor.h"
@@ -279,7 +280,8 @@
     const JavaParamRef<jobject>& contents_client_bridge,
     const JavaParamRef<jobject>& io_thread_client,
     const JavaParamRef<jobject>& intercept_navigation_delegate,
-    const JavaParamRef<jobject>& autofill_provider) {
+    const JavaParamRef<jobject>& autofill_provider,
+    const JavaParamRef<jobject>& content_capture_receiver_manager) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
   // The |aw_content| param is technically spurious as it duplicates |obj| but
   // is passed over anyway to make the binding more explicit.
@@ -299,7 +301,10 @@
   InterceptNavigationDelegate::Associate(
       web_contents_.get(), std::make_unique<InterceptNavigationDelegate>(
                                env, intercept_navigation_delegate));
-
+  if (content_capture_receiver_manager) {
+    content_capture::ContentCaptureReceiverManagerAndroid::Create(
+        web_contents_.get(), content_capture_receiver_manager);
+  }
   if (!autofill_provider.is_null()) {
     autofill_provider_ = std::make_unique<autofill::AutofillProviderAndroid>(
         autofill_provider, web_contents_.get());
diff --git a/android_webview/browser/aw_contents.h b/android_webview/browser/aw_contents.h
index bc7e248..f67e852 100644
--- a/android_webview/browser/aw_contents.h
+++ b/android_webview/browser/aw_contents.h
@@ -97,7 +97,9 @@
       const base::android::JavaParamRef<jobject>& contents_client_bridge,
       const base::android::JavaParamRef<jobject>& io_thread_client,
       const base::android::JavaParamRef<jobject>& intercept_navigation_delegate,
-      const base::android::JavaParamRef<jobject>& autofill_provider);
+      const base::android::JavaParamRef<jobject>& autofill_provider,
+      const base::android::JavaParamRef<jobject>&
+          content_capture_receiver_manager);
   base::android::ScopedJavaLocalRef<jobject> GetWebContents(
       JNIEnv* env,
       const base::android::JavaParamRef<jobject>& obj);
diff --git a/android_webview/browser/cookie_manager.cc b/android_webview/browser/cookie_manager.cc
index 3726caf2..5dc07cd 100644
--- a/android_webview/browser/cookie_manager.cc
+++ b/android_webview/browser/cookie_manager.cc
@@ -403,8 +403,8 @@
                                              base::OnceClosure complete) {
   net::CookieOptions options;
   options.set_include_httponly();
-  options.set_same_site_cookie_mode(
-      net::CookieOptions::SameSiteCookieMode::INCLUDE_STRICT_AND_LAX);
+  options.set_same_site_cookie_context(
+      net::CookieOptions::SameSiteCookieContext::SAME_SITE_STRICT);
 
   if (base::FeatureList::IsEnabled(network::features::kNetworkService)) {
     GetCookieManagerWrapper()->GetCookieList(
diff --git a/android_webview/browser/gfx/DEPS b/android_webview/browser/gfx/DEPS
index e7494c97..ef91ac3 100644
--- a/android_webview/browser/gfx/DEPS
+++ b/android_webview/browser/gfx/DEPS
@@ -2,6 +2,7 @@
   "-android_webview",
   "-android_webview/browser",
   "+android_webview/browser/gfx",
+  "+android_webview/common/aw_switches.h",
   "+android_webview/public/browser",
 ]
 
diff --git a/android_webview/browser/gfx/deferred_gpu_command_service.cc b/android_webview/browser/gfx/deferred_gpu_command_service.cc
index 8ecf1c3d..9733b7a 100644
--- a/android_webview/browser/gfx/deferred_gpu_command_service.cc
+++ b/android_webview/browser/gfx/deferred_gpu_command_service.cc
@@ -128,16 +128,21 @@
   DCHECK(base::CommandLine::InitializedForCurrentProcess());
   gpu::GpuPreferences gpu_preferences =
       content::GetGpuPreferencesFromCommandLine();
-  bool success = gpu::InitializeGLThreadSafe(
-      base::CommandLine::ForCurrentProcess(), gpu_preferences, &gpu_info,
-      &gpu_feature_info);
+  auto* command_line = base::CommandLine::ForCurrentProcess();
+  bool success = gpu::InitializeGLThreadSafe(command_line, gpu_preferences,
+                                             &gpu_info, &gpu_feature_info);
   if (!success) {
     LOG(FATAL) << "gpu::InitializeGLThreadSafe() failed.";
   }
+  auto sync_point_manager = std::make_unique<gpu::SyncPointManager>();
+  auto mailbox_manager = gpu::gles2::CreateMailboxManager(gpu_preferences);
+  // The shared_image_manager will be shared between renderer thread and GPU
+  // main thread, so it should be thread safe.
+  auto shared_image_manager =
+      std::make_unique<gpu::SharedImageManager>(true /* thread_safe */);
   return new DeferredGpuCommandService(
-      std::make_unique<gpu::SyncPointManager>(),
-      gpu::gles2::CreateMailboxManager(gpu_preferences),
-      std::make_unique<gpu::SharedImageManager>(), gpu_preferences, gpu_info,
+      std::move(sync_point_manager), std::move(mailbox_manager),
+      std::move(shared_image_manager), gpu_preferences, gpu_info,
       gpu_feature_info);
 }
 
diff --git a/android_webview/browser/gfx/surfaces_instance.cc b/android_webview/browser/gfx/surfaces_instance.cc
index dd46a4c7..998ca24 100644
--- a/android_webview/browser/gfx/surfaces_instance.cc
+++ b/android_webview/browser/gfx/surfaces_instance.cc
@@ -12,6 +12,8 @@
 #include "android_webview/browser/gfx/aw_render_thread_context_provider.h"
 #include "android_webview/browser/gfx/deferred_gpu_command_service.h"
 #include "android_webview/browser/gfx/parent_output_surface.h"
+#include "android_webview/common/aw_switches.h"
+#include "base/command_line.h"
 #include "base/stl_util.h"
 #include "components/viz/common/display/renderer_settings.h"
 #include "components/viz/common/features.h"
@@ -106,7 +108,9 @@
     if (settings.use_skia_renderer_non_ddl) {
       output_surface = std::make_unique<viz::SkiaOutputSurfaceImplNonDDL>(
           base::MakeRefCounted<AwGLSurface>(), shared_context_state_,
-          task_executor->mailbox_manager(), task_executor->sync_point_manager(),
+          task_executor->mailbox_manager(),
+          task_executor->shared_image_manager(),
+          task_executor->sync_point_manager(),
           false /* need_swapbuffers_ack */);
     } else {
       output_surface = std::make_unique<viz::SkiaOutputSurfaceImpl>(
@@ -131,8 +135,11 @@
       nullptr /* shared_bitmap_manager */, settings, frame_sink_id_,
       std::move(output_surface), std::move(scheduler),
       nullptr /* current_task_runner */, skia_output_surface);
+  const bool enable_shared_image =
+      base::CommandLine::ForCurrentProcess()->HasSwitch(
+          switches::kWebViewEnableSharedImage);
   display_->Initialize(this, frame_sink_manager_->surface_manager(),
-                       false /* enable_shared_images */);
+                       enable_shared_image);
   frame_sink_manager_->RegisterBeginFrameSource(begin_frame_source_.get(),
                                                 frame_sink_id_);
 
diff --git a/android_webview/browser/net/aw_url_request_context_getter.cc b/android_webview/browser/net/aw_url_request_context_getter.cc
index 50709653..54b5e12 100644
--- a/android_webview/browser/net/aw_url_request_context_getter.cc
+++ b/android_webview/browser/net/aw_url_request_context_getter.cc
@@ -42,6 +42,7 @@
 #include "net/base/cache_type.h"
 #include "net/cert/cert_verifier.h"
 #include "net/cookies/cookie_store.h"
+#include "net/dns/host_resolver.h"
 #include "net/dns/mapped_host_resolver.h"
 #include "net/http/http_auth_filter.h"
 #include "net/http/http_auth_handler_factory.h"
@@ -320,8 +321,7 @@
       new net::MappedHostResolver(
           net::HostResolver::CreateDefaultResolver(nullptr)));
   ApplyCmdlineOverridesToHostResolver(host_resolver.get());
-  builder.SetHttpAuthHandlerFactory(
-      CreateAuthHandlerFactory(host_resolver.get()));
+  builder.SetHttpAuthHandlerFactory(CreateAuthHandlerFactory());
   builder.set_host_resolver(std::move(host_resolver));
 
   url_request_context_ = builder.Build();
@@ -385,10 +385,7 @@
 }
 
 std::unique_ptr<net::HttpAuthHandlerFactory>
-AwURLRequestContextGetter::CreateAuthHandlerFactory(
-    net::HostResolver* resolver) {
-  DCHECK(resolver);
-
+AwURLRequestContextGetter::CreateAuthHandlerFactory() {
   // In Chrome this is configurable via the AuthSchemes policy. For WebView
   // there is no interest to have it available so far.
   std::vector<std::string> supported_schemes = {"basic", "digest", "ntlm",
@@ -399,7 +396,7 @@
   UpdateAndroidAuthNegotiateAccountType();
 
   return net::HttpAuthHandlerRegistryFactory::Create(
-      resolver, http_auth_preferences_.get(), supported_schemes);
+      http_auth_preferences_.get(), supported_schemes);
 }
 
 void AwURLRequestContextGetter::UpdateServerWhitelist() {
diff --git a/android_webview/browser/net/aw_url_request_context_getter.h b/android_webview/browser/net/aw_url_request_context_getter.h
index 25f0cfd..dfaf266 100644
--- a/android_webview/browser/net/aw_url_request_context_getter.h
+++ b/android_webview/browser/net/aw_url_request_context_getter.h
@@ -22,7 +22,6 @@
 
 namespace net {
 class FileNetLogObserver;
-class HostResolver;
 class HttpAuthHandlerFactory;
 class HttpAuthPreferences;
 class HttpUserAgentSettings;
@@ -78,8 +77,7 @@
 
   // This is called to create a HttpAuthHandlerFactory that will handle
   // auth challenges for the new URLRequestContext
-  std::unique_ptr<net::HttpAuthHandlerFactory> CreateAuthHandlerFactory(
-      net::HostResolver* resolver);
+  std::unique_ptr<net::HttpAuthHandlerFactory> CreateAuthHandlerFactory();
 
   // Update methods for the auth related preferences
   void UpdateServerWhitelist();
diff --git a/android_webview/common/aw_switches.cc b/android_webview/common/aw_switches.cc
index 2286d02..c909a271 100644
--- a/android_webview/common/aw_switches.cc
+++ b/android_webview/common/aw_switches.cc
@@ -16,4 +16,7 @@
 const char kWebViewDisableSafeBrowsingSupport[] =
     "webview-disable-safebrowsing-support";
 
+// Used to enable shared image API for webview.
+const char kWebViewEnableSharedImage[] = "webview-enable-shared-image";
+
 }  // namespace switches
diff --git a/android_webview/common/aw_switches.h b/android_webview/common/aw_switches.h
index cb2fc2c..410c1c8 100644
--- a/android_webview/common/aw_switches.h
+++ b/android_webview/common/aw_switches.h
@@ -10,6 +10,7 @@
 extern const char kWebViewSandboxedRenderer[];
 extern const char kWebViewEnableSafeBrowsingSupport[];
 extern const char kWebViewDisableSafeBrowsingSupport[];
+extern const char kWebViewEnableSharedImage[];
 
 // Please note that if you are adding a flag that is intended for a renderer,
 // you also need to add it into
diff --git a/android_webview/common/crash_reporter/aw_crash_reporter_client.cc b/android_webview/common/crash_reporter/aw_crash_reporter_client.cc
index 0d85d6c..69a49279 100644
--- a/android_webview/common/crash_reporter/aw_crash_reporter_client.cc
+++ b/android_webview/common/crash_reporter/aw_crash_reporter_client.cc
@@ -69,7 +69,7 @@
     *sanitize_stacks = true;
   }
 
-  unsigned int GetCrashDumpPercentageForWebView() override { return 100; }
+  unsigned int GetCrashDumpPercentage() override { return 100; }
 
   bool GetBrowserProcessType(std::string* ptype) override {
     *ptype = base::CommandLine::ForCurrentProcess()->HasSwitch(
diff --git a/android_webview/common/crash_reporter/crash_keys.cc b/android_webview/common/crash_reporter/crash_keys.cc
index 12c4a87d..f114c06 100644
--- a/android_webview/common/crash_reporter/crash_keys.cc
+++ b/android_webview/common/crash_reporter/crash_keys.cc
@@ -21,6 +21,9 @@
     kAppPackageVersionCode,
     kAndroidSdkInt,
 
+    // process type
+    "ptype",
+
     // Java exception stack traces
     "exception_info",
 
diff --git a/android_webview/gpu/aw_content_gpu_client.cc b/android_webview/gpu/aw_content_gpu_client.cc
index c650652..922f62f 100644
--- a/android_webview/gpu/aw_content_gpu_client.cc
+++ b/android_webview/gpu/aw_content_gpu_client.cc
@@ -7,8 +7,10 @@
 namespace android_webview {
 
 AwContentGpuClient::AwContentGpuClient(
-    const GetSyncPointManagerCallback& sync_point_manager_callback)
-    : sync_point_manager_callback_(sync_point_manager_callback) {}
+    const GetSyncPointManagerCallback& sync_point_manager_callback,
+    const GetSharedImageManagerCallback& shared_image_manager_callback)
+    : sync_point_manager_callback_(sync_point_manager_callback),
+      shared_image_manager_callback_(shared_image_manager_callback) {}
 
 AwContentGpuClient::~AwContentGpuClient() {}
 
@@ -16,4 +18,8 @@
   return sync_point_manager_callback_.Run();
 }
 
+gpu::SharedImageManager* AwContentGpuClient::GetSharedImageManager() {
+  return shared_image_manager_callback_.Run();
+}
+
 }  // namespace android_webview
diff --git a/android_webview/gpu/aw_content_gpu_client.h b/android_webview/gpu/aw_content_gpu_client.h
index b623305d..4819c60 100644
--- a/android_webview/gpu/aw_content_gpu_client.h
+++ b/android_webview/gpu/aw_content_gpu_client.h
@@ -13,17 +13,23 @@
 
 class AwContentGpuClient : public content::ContentGpuClient {
  public:
-  using GetSyncPointManagerCallback = base::Callback<gpu::SyncPointManager*()>;
+  using GetSyncPointManagerCallback =
+      base::RepeatingCallback<gpu::SyncPointManager*()>;
+  using GetSharedImageManagerCallback =
+      base::RepeatingCallback<gpu::SharedImageManager*()>;
 
-  explicit AwContentGpuClient(
-      const GetSyncPointManagerCallback& sync_point_manager_callback);
+  AwContentGpuClient(
+      const GetSyncPointManagerCallback& sync_point_manager_callback,
+      const GetSharedImageManagerCallback& shared_image_manager_callback);
   ~AwContentGpuClient() override;
 
   // content::ContentGpuClient implementation.
   gpu::SyncPointManager* GetSyncPointManager() override;
+  gpu::SharedImageManager* GetSharedImageManager() override;
 
  private:
   GetSyncPointManagerCallback sync_point_manager_callback_;
+  GetSharedImageManagerCallback shared_image_manager_callback_;
   DISALLOW_COPY_AND_ASSIGN(AwContentGpuClient);
 };
 
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 063fbaba..592c717 100644
--- a/android_webview/java/src/org/chromium/android_webview/AwContents.java
+++ b/android_webview/java/src/org/chromium/android_webview/AwContents.java
@@ -66,6 +66,9 @@
 import org.chromium.base.task.AsyncTask;
 import org.chromium.base.task.PostTask;
 import org.chromium.components.autofill.AutofillProvider;
+import org.chromium.components.content_capture.ContentCaptureConsumer;
+import org.chromium.components.content_capture.ContentCaptureFeatures;
+import org.chromium.components.content_capture.ContentCaptureReceiverManager;
 import org.chromium.components.navigation_interception.InterceptNavigationDelegate;
 import org.chromium.components.navigation_interception.NavigationParams;
 import org.chromium.content_public.browser.ChildProcessImportance;
@@ -481,6 +484,8 @@
 
     private JavascriptInjector mJavascriptInjector;
 
+    private ContentCaptureReceiverManager mContentCaptureReceiverManager;
+
     private static class WebContentsInternalsHolder implements WebContents.InternalsHolder {
         private final WeakReference<AwContents> mAwContentsRef;
 
@@ -933,6 +938,10 @@
             setOverScrollMode(mContainerView.getOverScrollMode());
             setScrollBarStyle(mInternalAccessAdapter.super_getScrollBarStyle());
 
+            if (ContentCaptureFeatures.isEnabled()) {
+                mContentCaptureReceiverManager = new ContentCaptureReceiverManager();
+            }
+
             setNewAwContents(nativeInit(mBrowserContext));
 
             onContainerViewChanged();
@@ -1113,6 +1122,9 @@
         awViewMethodsImpl.onWindowFocusChanged(mContainerView.hasWindowFocus());
         awViewMethodsImpl.onFocusChanged(mContainerView.hasFocus(), 0, null);
         mContainerView.requestLayout();
+        if (mContentCaptureReceiverManager != null) {
+            mContentCaptureReceiverManager.onContainerViewChanged(mContainerView);
+        }
         if (mAutofillProvider != null) mAutofillProvider.onContainerViewChanged(mContainerView);
     }
 
@@ -1245,7 +1257,8 @@
         initWebContents(mViewAndroidDelegate, mInternalAccessAdapter, mWebContents,
                 mWindowAndroid.getWindowAndroid(), mWebContentsInternalsHolder);
         nativeSetJavaPeers(mNativeAwContents, this, mWebContentsDelegate, mContentsClientBridge,
-                mIoThreadClient, mInterceptNavigationDelegate, mAutofillProvider);
+                mIoThreadClient, mInterceptNavigationDelegate, mAutofillProvider,
+                mContentCaptureReceiverManager);
         GestureListenerManager.fromWebContents(mWebContents)
                 .addListener(new AwGestureStateListener());
 
@@ -1553,6 +1566,11 @@
         return sLocalGlobalVisibleRect;
     }
 
+    public void setContentCaptureConsumer(ContentCaptureConsumer consumer) {
+        if (mContentCaptureReceiverManager != null)
+            mContentCaptureReceiverManager.setContentCaptureConsumer(consumer);
+    }
+
     //--------------------------------------------------------------------------------------------
     //  WebView[Provider] method implementations
     //--------------------------------------------------------------------------------------------
@@ -3850,7 +3868,8 @@
             AwWebContentsDelegate webViewWebContentsDelegate,
             AwContentsClientBridge contentsClientBridge, AwContentsIoThreadClient ioThreadClient,
             InterceptNavigationDelegate navigationInterceptionDelegate,
-            AutofillProvider autofillProvider);
+            AutofillProvider autofillProvider,
+            ContentCaptureReceiverManager contentCaptureReceiverManager);
     private native WebContents nativeGetWebContents(long nativeAwContents);
     private native void nativeSetCompositorFrameConsumer(
             long nativeAwContents, long nativeCompositorFrameConsumer);
diff --git a/android_webview/javatests/src/org/chromium/android_webview/test/AwActivityTestRule.java b/android_webview/javatests/src/org/chromium/android_webview/test/AwActivityTestRule.java
index b2701be..6914c83 100644
--- a/android_webview/javatests/src/org/chromium/android_webview/test/AwActivityTestRule.java
+++ b/android_webview/javatests/src/org/chromium/android_webview/test/AwActivityTestRule.java
@@ -526,7 +526,8 @@
         TestAwContentsClient.OnCreateWindowHelper onCreateWindowHelper =
                 parentAwContentsClient.getOnCreateWindowHelper();
         int currentCallCount = onCreateWindowHelper.getCallCount();
-        parentAwContents.evaluateJavaScriptForTests(triggerScript, null);
+        ThreadUtils.runOnUiThreadBlocking(
+                () -> parentAwContents.evaluateJavaScriptForTests(triggerScript, null));
         onCreateWindowHelper.waitForCallback(
                 currentCallCount, 1, WAIT_TIMEOUT_MS, TimeUnit.MILLISECONDS);
     }
diff --git a/android_webview/javatests/src/org/chromium/android_webview/test/AwJavaBridgeTest.java b/android_webview/javatests/src/org/chromium/android_webview/test/AwJavaBridgeTest.java
index 466af56a..5960cb1 100644
--- a/android_webview/javatests/src/org/chromium/android_webview/test/AwJavaBridgeTest.java
+++ b/android_webview/javatests/src/org/chromium/android_webview/test/AwJavaBridgeTest.java
@@ -15,6 +15,7 @@
 import org.junit.runner.RunWith;
 
 import org.chromium.android_webview.AwContents;
+import org.chromium.base.ThreadUtils;
 import org.chromium.base.test.util.Feature;
 
 /**
@@ -76,7 +77,8 @@
                 mActivityTestRule.executeJavaScriptAndWaitForResult(
                         awContents, mContentsClient, "typeof test.destroy"));
         int currentCallCount = client2.getOnPageFinishedHelper().getCallCount();
-        awContents.evaluateJavaScriptForTests("test.destroy()", null);
+        ThreadUtils.runOnUiThreadBlocking(
+                () -> awContents.evaluateJavaScriptForTests("test.destroy()", null));
 
         client2.getOnPageFinishedHelper().waitForCallback(currentCallCount);
     }
diff --git a/android_webview/javatests/src/org/chromium/android_webview/test/GeolocationTest.java b/android_webview/javatests/src/org/chromium/android_webview/test/GeolocationTest.java
index 9a69a2f9..56e6561 100644
--- a/android_webview/javatests/src/org/chromium/android_webview/test/GeolocationTest.java
+++ b/android_webview/javatests/src/org/chromium/android_webview/test/GeolocationTest.java
@@ -20,6 +20,7 @@
 import org.chromium.android_webview.AwGeolocationPermissions;
 import org.chromium.android_webview.AwSettings;
 import org.chromium.android_webview.test.AwActivityTestRule.TestDependencyFactory;
+import org.chromium.base.ThreadUtils;
 import org.chromium.base.test.util.Feature;
 import org.chromium.base.test.util.RetryOnFailure;
 import org.chromium.content_public.common.ContentUrlConstants;
@@ -156,11 +157,15 @@
                 mContentsClient.getOnPageFinishedHelper(), RAW_HTML, "text/html", false,
                 "https://google.com/", ContentUrlConstants.ABOUT_BLANK_DISPLAY_URL);
 
-        mAwContents.evaluateJavaScriptForTests("initiate_getCurrentPosition();", null);
+        ThreadUtils.runOnUiThreadBlocking(
+                () -> mAwContents.evaluateJavaScriptForTests(
+                        "initiate_getCurrentPosition();", null));
 
         AwActivityTestRule.pollInstrumentationThread(() -> getPositionCountFromJS() == 1);
 
-        mAwContents.evaluateJavaScriptForTests("initiate_getCurrentPosition();", null);
+        ThreadUtils.runOnUiThreadBlocking(
+                () -> mAwContents.evaluateJavaScriptForTests(
+                        "initiate_getCurrentPosition();", null));
         AwActivityTestRule.pollInstrumentationThread(() -> getPositionCountFromJS() == 2);
     }
 
@@ -177,7 +182,8 @@
                 mContentsClient.getOnPageFinishedHelper(), RAW_HTML, "text/html", false,
                 "https://google.com/", ContentUrlConstants.ABOUT_BLANK_DISPLAY_URL);
 
-        mAwContents.evaluateJavaScriptForTests("initiate_watchPosition();", null);
+        ThreadUtils.runOnUiThreadBlocking(
+                () -> mAwContents.evaluateJavaScriptForTests("initiate_watchPosition();", null));
 
         AwActivityTestRule.pollInstrumentationThread(() -> getPositionCountFromJS() > 1);
     }
@@ -192,7 +198,8 @@
                 mContentsClient.getOnPageFinishedHelper(), RAW_HTML, "text/html", false,
                 "https://google.com/", ContentUrlConstants.ABOUT_BLANK_DISPLAY_URL);
 
-        mAwContents.evaluateJavaScriptForTests("initiate_watchPosition();", null);
+        ThreadUtils.runOnUiThreadBlocking(
+                () -> mAwContents.evaluateJavaScriptForTests("initiate_watchPosition();", null));
 
         AwActivityTestRule.pollInstrumentationThread(() -> getPositionCountFromJS() > 1);
 
@@ -229,7 +236,8 @@
                 mContentsClient.getOnPageFinishedHelper(), RAW_HTML, "text/html", false,
                 "https://google.com/", ContentUrlConstants.ABOUT_BLANK_DISPLAY_URL);
 
-        mAwContents.evaluateJavaScriptForTests("initiate_watchPosition();", null);
+        ThreadUtils.runOnUiThreadBlocking(
+                () -> mAwContents.evaluateJavaScriptForTests("initiate_watchPosition();", null));
 
         Assert.assertEquals(0, getPositionCountFromJS());
 
@@ -268,7 +276,9 @@
                 mContentsClient.getOnPageFinishedHelper(), RAW_HTML, "text/html", false,
                 "https://google.com/", ContentUrlConstants.ABOUT_BLANK_DISPLAY_URL);
 
-        mAwContents.evaluateJavaScriptForTests("initiate_getCurrentPosition();", null);
+        ThreadUtils.runOnUiThreadBlocking(
+                () -> mAwContents.evaluateJavaScriptForTests(
+                        "initiate_getCurrentPosition();", null));
 
         AwActivityTestRule.pollInstrumentationThread(new Callable<Boolean>() {
             @Override
@@ -290,7 +300,9 @@
                 mContentsClient.getOnPageFinishedHelper(), RAW_HTML, "text/html", false,
                 "http://google.com/", ContentUrlConstants.ABOUT_BLANK_DISPLAY_URL);
 
-        mAwContents.evaluateJavaScriptForTests("initiate_getCurrentPosition();", null);
+        ThreadUtils.runOnUiThreadBlocking(
+                () -> mAwContents.evaluateJavaScriptForTests(
+                        "initiate_getCurrentPosition();", null));
 
         AwActivityTestRule.pollInstrumentationThread(new Callable<Boolean>() {
             @Override
@@ -310,7 +322,9 @@
                 mContentsClient.getOnPageFinishedHelper(), RAW_HTML, "text/html", false,
                 "http://google.com/", ContentUrlConstants.ABOUT_BLANK_DISPLAY_URL);
 
-        mAwContents.evaluateJavaScriptForTests("initiate_getCurrentPosition();", null);
+        ThreadUtils.runOnUiThreadBlocking(
+                () -> mAwContents.evaluateJavaScriptForTests(
+                        "initiate_getCurrentPosition();", null));
 
         AwActivityTestRule.pollInstrumentationThread(() -> getPositionCountFromJS() > 0);
     }
diff --git a/android_webview/javatests/src/org/chromium/android_webview/test/SafeBrowsingTest.java b/android_webview/javatests/src/org/chromium/android_webview/test/SafeBrowsingTest.java
index 0b28a13..25953c9 100644
--- a/android_webview/javatests/src/org/chromium/android_webview/test/SafeBrowsingTest.java
+++ b/android_webview/javatests/src/org/chromium/android_webview/test/SafeBrowsingTest.java
@@ -549,7 +549,6 @@
         assertTargetPageHasLoaded(MALWARE_PAGE_BACKGROUND_COLOR);
     }
 
-    @DisabledTest(message = "crbug.com/855732")
     @Test
     @SmallTest
     @Feature({"AndroidWebView"})
diff --git a/android_webview/lib/aw_main_delegate.cc b/android_webview/lib/aw_main_delegate.cc
index d90ea5dc9..b49c21d8 100644
--- a/android_webview/lib/aw_main_delegate.cc
+++ b/android_webview/lib/aw_main_delegate.cc
@@ -347,11 +347,21 @@
   DCHECK(DeferredGpuCommandService::GetInstance());
   return DeferredGpuCommandService::GetInstance()->sync_point_manager();
 }
+gpu::SharedImageManager* GetSharedImageManager() {
+  DCHECK(DeferredGpuCommandService::GetInstance());
+  const bool enable_shared_image =
+      base::CommandLine::ForCurrentProcess()->HasSwitch(
+          switches::kWebViewEnableSharedImage);
+  return enable_shared_image
+             ? DeferredGpuCommandService::GetInstance()->shared_image_manager()
+             : nullptr;
+}
 }  // namespace
 
 content::ContentGpuClient* AwMainDelegate::CreateContentGpuClient() {
-  content_gpu_client_.reset(
-      new AwContentGpuClient(base::BindRepeating(&GetSyncPointManager)));
+  content_gpu_client_ = std::make_unique<AwContentGpuClient>(
+      base::BindRepeating(&GetSyncPointManager),
+      base::BindRepeating(&GetSharedImageManager));
   return content_gpu_client_.get();
 }
 
diff --git a/android_webview/renderer/aw_render_frame_ext.cc b/android_webview/renderer/aw_render_frame_ext.cc
index 4e907f48..afd3a5b5 100644
--- a/android_webview/renderer/aw_render_frame_ext.cc
+++ b/android_webview/renderer/aw_render_frame_ext.cc
@@ -13,6 +13,8 @@
 #include "base/strings/utf_string_conversions.h"
 #include "components/autofill/content/renderer/autofill_agent.h"
 #include "components/autofill/content/renderer/password_autofill_agent.h"
+#include "components/content_capture/common/content_capture_features.h"
+#include "components/content_capture/renderer/content_capture_sender.h"
 #include "content/public/renderer/document_state.h"
 #include "content/public/renderer/render_frame.h"
 #include "content/public/renderer/render_view.h"
@@ -156,6 +158,8 @@
       new autofill::PasswordAutofillAgent(render_frame, &registry_);
   new autofill::AutofillAgent(render_frame, password_autofill_agent, nullptr,
                               &registry_);
+  if (content_capture::features::IsContentCaptureEnabled())
+    new content_capture::ContentCaptureSender(render_frame);
 
   // Add myself to the RenderFrame => AwRenderFrameExt register.
   render_frame_ext_map.Get().emplace(render_frame, this);
diff --git a/ash/app_list/app_list_controller_impl.cc b/ash/app_list/app_list_controller_impl.cc
index 8752d7e..faf058c 100644
--- a/ash/app_list/app_list_controller_impl.cc
+++ b/ash/app_list/app_list_controller_impl.cc
@@ -873,10 +873,12 @@
     presenter_.GetView()->CloseOpenedPage();
 }
 
-void AppListControllerImpl::LogSearchClick(const std::string& result_id,
-                                           int suggestion_index) {
+void AppListControllerImpl::LogSearchClick(
+    const std::string& result_id,
+    int suggestion_index,
+    ash::mojom::AppListLaunchedFrom launched_from) {
   if (client_)
-    client_->LogSearchClick(result_id, suggestion_index);
+    client_->LogSearchClick(result_id, suggestion_index, launched_from);
 }
 
 void AppListControllerImpl::InvokeSearchResultAction(
diff --git a/ash/app_list/app_list_controller_impl.h b/ash/app_list/app_list_controller_impl.h
index 03d6825f..3a20e51 100644
--- a/ash/app_list/app_list_controller_impl.h
+++ b/ash/app_list/app_list_controller_impl.h
@@ -164,7 +164,8 @@
   void StartSearch(const base::string16& raw_query) override;
   void OpenSearchResult(const std::string& result_id, int event_flags) override;
   void LogSearchClick(const std::string& result_id,
-                      int suggestion_index) override;
+                      int suggestion_index,
+                      ash::mojom::AppListLaunchedFrom launched_from) override;
   void InvokeSearchResultAction(const std::string& result_id,
                                 int action_index,
                                 int event_flags) override;
diff --git a/ash/app_list/app_list_view_delegate.h b/ash/app_list/app_list_view_delegate.h
index 79cbe0070..afb1107 100644
--- a/ash/app_list/app_list_view_delegate.h
+++ b/ash/app_list/app_list_view_delegate.h
@@ -10,6 +10,7 @@
 
 #include "ash/assistant/ui/assistant_view_delegate.h"
 #include "ash/public/cpp/ash_public_export.h"
+#include "ash/public/interfaces/app_list.mojom.h"
 #include "ash/public/interfaces/menu.mojom.h"
 #include "base/callback_forward.h"
 #include "base/strings/string16.h"
@@ -51,9 +52,13 @@
   virtual void OpenSearchResult(const std::string& result_id,
                                 int event_flags) = 0;
   // Called to log a click on the search result with |result_id| located at
-  // position |suggestion_index| in the suggestion chip.
-  virtual void LogSearchClick(const std::string& result_id,
-                              int suggestion_index) = 0;
+  // position |suggestion_index|. Logged for the suggestion chip and the tile
+  // item view. |launched_from| values must match the LaunchedFrom enum in
+  // chrome/browser/ui/app_list/app_launch_event_logger.proto.
+  virtual void LogSearchClick(
+      const std::string& result_id,
+      int suggestion_index,
+      ash::mojom::AppListLaunchedFrom launched_from) = 0;
 
   // Called to invoke a custom action on a result with |result_id|.
   // |action_index| corresponds to the index of an icon in
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 3202007..9bbfd6e4 100644
--- a/ash/app_list/test/app_list_test_view_delegate.h
+++ b/ash/app_list/test/app_list_test_view_delegate.h
@@ -60,7 +60,8 @@
   void StartSearch(const base::string16& raw_query) override {}
   void OpenSearchResult(const std::string& result_id, int event_flags) override;
   void LogSearchClick(const std::string& result_id,
-                      int suggestion_index) override {}
+                      int suggestion_index,
+                      ash::mojom::AppListLaunchedFrom launched_from) override {}
   void InvokeSearchResultAction(const std::string& result_id,
                                 int action_index,
                                 int event_flags) override {}
diff --git a/ash/app_list/test/test_app_list_client.h b/ash/app_list/test/test_app_list_client.h
index 8bb67df..eb6f0c3 100644
--- a/ash/app_list/test/test_app_list_client.h
+++ b/ash/app_list/test/test_app_list_client.h
@@ -27,7 +27,8 @@
   void OpenSearchResult(const std::string& result_id,
                         int event_flags) override {}
   void LogSearchClick(const std::string& result_id,
-                      int suggestion_index) override {}
+                      int suggestion_index,
+                      ash::mojom::AppListLaunchedFrom launched_from) override {}
   void InvokeSearchResultAction(const std::string& result_id,
                                 int action_index,
                                 int event_flags) override {}
diff --git a/ash/app_list/views/search_result_actions_view.cc b/ash/app_list/views/search_result_actions_view.cc
index b402dfc..a3867a0b 100644
--- a/ash/app_list/views/search_result_actions_view.cc
+++ b/ash/app_list/views/search_result_actions_view.cc
@@ -103,6 +103,8 @@
 
   SetAccessibleName(action.tooltip_text);
 
+  SetTooltipText(action.tooltip_text);
+
   SetVisible(!visible_on_hover_);
 }
 
diff --git a/ash/app_list/views/search_result_suggestion_chip_view.cc b/ash/app_list/views/search_result_suggestion_chip_view.cc
index ad04ce4e..fa07d9b 100644
--- a/ash/app_list/views/search_result_suggestion_chip_view.cc
+++ b/ash/app_list/views/search_result_suggestion_chip_view.cc
@@ -10,6 +10,7 @@
 #include "ash/app_list/app_list_view_delegate.h"
 #include "ash/app_list/model/search/search_result.h"
 #include "ash/public/cpp/app_list/internal_app_id_constants.h"
+#include "ash/public/interfaces/app_list.mojom.h"
 #include "base/metrics/histogram_functions.h"
 #include "base/metrics/histogram_macros.h"
 #include "base/metrics/user_metrics.h"
@@ -71,8 +72,9 @@
   RecordSearchResultOpenSource(result(), view_delegate_->GetModel(),
                                view_delegate_->GetSearchModel());
   view_delegate_->OpenSearchResult(result()->id(), event.flags());
-  view_delegate_->LogSearchClick(result()->id(),
-                                 index_in_suggestion_chip_container_);
+  view_delegate_->LogSearchClick(
+      result()->id(), index_in_suggestion_chip_container_,
+      ash::mojom::AppListLaunchedFrom::kLaunchedFromSuggestionChip);
 }
 
 void SearchResultSuggestionChipView::Layout() {
diff --git a/ash/app_list/views/search_result_tile_item_list_view.cc b/ash/app_list/views/search_result_tile_item_list_view.cc
index c4a9829..8e1a78b 100644
--- a/ash/app_list/views/search_result_tile_item_list_view.cc
+++ b/ash/app_list/views/search_result_tile_item_list_view.cc
@@ -82,6 +82,7 @@
     SearchResultTileItemView* tile_item = new SearchResultTileItemView(
         view_delegate, nullptr /* pagination model */,
         false /* show_in_apps_page */);
+    tile_item->SetIndexInItemListView(i);
     tile_item->SetParentBackgroundColor(
         AppListConfig::instance().card_background_color());
     tile_views_.push_back(tile_item);
diff --git a/ash/app_list/views/search_result_tile_item_view.cc b/ash/app_list/views/search_result_tile_item_view.cc
index 1af97ab..a4e6ed0 100644
--- a/ash/app_list/views/search_result_tile_item_view.cc
+++ b/ash/app_list/views/search_result_tile_item_view.cc
@@ -16,6 +16,7 @@
 #include "ash/public/cpp/app_list/app_list_config.h"
 #include "ash/public/cpp/app_list/app_list_features.h"
 #include "ash/public/cpp/app_list/vector_icons/vector_icons.h"
+#include "ash/public/interfaces/app_list.mojom.h"
 #include "base/bind.h"
 #include "base/i18n/number_formatting.h"
 #include "base/metrics/histogram_macros.h"
@@ -239,6 +240,10 @@
   SetAccessibleName(accessible_name);
 }
 
+void SearchResultTileItemView::SetIndexInItemListView(size_t index) {
+  index_in_item_list_view_ = index;
+}
+
 void SearchResultTileItemView::SetParentBackgroundColor(SkColor color) {
   parent_background_color_ = color;
   UpdateBackgroundColor();
@@ -252,6 +257,9 @@
   RecordSearchResultOpenSource(result(), view_delegate_->GetModel(),
                                view_delegate_->GetSearchModel());
   view_delegate_->OpenSearchResult(result()->id(), event.flags());
+  view_delegate_->LogSearchClick(
+      result()->id(), index_in_item_list_view_,
+      ash::mojom::AppListLaunchedFrom::kLaunchedFromSearchBox);
 }
 
 void SearchResultTileItemView::GetAccessibleNodeData(
diff --git a/ash/app_list/views/search_result_tile_item_view.h b/ash/app_list/views/search_result_tile_item_view.h
index 2fc1926..3c2ddec2 100644
--- a/ash/app_list/views/search_result_tile_item_view.h
+++ b/ash/app_list/views/search_result_tile_item_view.h
@@ -39,6 +39,7 @@
   ~SearchResultTileItemView() override;
 
   void OnResultChanged() override;
+  void SetIndexInItemListView(size_t index);
 
   // Informs the SearchResultTileItemView of its parent's background color. The
   // controls within the SearchResultTileItemView will adapt to suit the given
@@ -120,6 +121,9 @@
 
   std::unique_ptr<AppListMenuModelAdapter> context_menu_;
 
+  // The index of this item in the search_result_tile_item_list_view, only used
+  // for logging.
+  int index_in_item_list_view_ = -1;
   base::WeakPtrFactory<SearchResultTileItemView> weak_ptr_factory_;
 
   DISALLOW_COPY_AND_ASSIGN(SearchResultTileItemView);
diff --git a/ash/app_list/views/search_result_view.cc b/ash/app_list/views/search_result_view.cc
index 383c460..9b5cfdf 100644
--- a/ash/app_list/views/search_result_view.cc
+++ b/ash/app_list/views/search_result_view.cc
@@ -387,13 +387,16 @@
 void SearchResultView::OnGestureEvent(ui::GestureEvent* event) {
   switch (event->type()) {
     case ui::ET_GESTURE_LONG_PRESS:
-      ScrollRectToVisible(GetLocalBounds());
-      NotifyAccessibilityEvent(ax::mojom::Event::kSelection, true);
-      SetBackgroundHighlighted(true);
-      confirm_remove_by_long_press_ = true;
-      OnSearchResultActionActivated(
-          ash::OmniBoxZeroStateAction::kRemoveSuggestion, event->flags());
-      event->SetHandled();
+      if (actions_view_->IsValidActionIndex(
+              ash::OmniBoxZeroStateAction::kRemoveSuggestion)) {
+        ScrollRectToVisible(GetLocalBounds());
+        NotifyAccessibilityEvent(ax::mojom::Event::kSelection, true);
+        SetBackgroundHighlighted(true);
+        confirm_remove_by_long_press_ = true;
+        OnSearchResultActionActivated(
+            ash::OmniBoxZeroStateAction::kRemoveSuggestion, event->flags());
+        event->SetHandled();
+      }
       break;
     default:
       break;
diff --git a/ash/assistant/assistant_controller.cc b/ash/assistant/assistant_controller.cc
index da404755..d25c32c 100644
--- a/ash/assistant/assistant_controller.cc
+++ b/ash/assistant/assistant_controller.cc
@@ -102,8 +102,18 @@
 }
 
 void AssistantController::StartSpeakerIdEnrollmentFlow() {
-  setup_controller()->StartOnboarding(false /* relaunch */,
-                                      mojom::FlowType::SPEAKER_ID_ENROLLMENT);
+  mojom::ConsentStatus consent_status =
+      Shell::Get()->voice_interaction_controller()->consent_status().value_or(
+          mojom::ConsentStatus::kUnknown);
+  if (consent_status == mojom::ConsentStatus::kActivityControlAccepted) {
+    // If activity control has been accepted, launch the enrollment flow.
+    setup_controller()->StartOnboarding(false /* relaunch */,
+                                        mojom::FlowType::SPEAKER_ID_ENROLLMENT);
+  } else {
+    // If activity control has not been accepted, launch the opt-in flow.
+    setup_controller()->StartOnboarding(false /* relaunch */,
+                                        mojom::FlowType::CONSENT_FLOW);
+  }
 }
 
 void AssistantController::DownloadImage(
diff --git a/ash/display/mirror_window_controller.cc b/ash/display/mirror_window_controller.cc
index cd62c8b..3ce1796 100644
--- a/ash/display/mirror_window_controller.cc
+++ b/ash/display/mirror_window_controller.cc
@@ -226,7 +226,7 @@
       host_info->ash_host->SetRootWindowTransformer(std::move(transformer));
       // The accelerated widget is created synchronously.
       DCHECK_NE(gfx::kNullAcceleratedWidget, host->GetAcceleratedWidget());
-      if (!base::FeatureList::IsEnabled(features::kVizDisplayCompositor)) {
+      if (!features::IsVizDisplayCompositorEnabled()) {
         mirror_window->SetBounds(host->window()->bounds());
         mirror_window->Show();
         if (reflector_) {
@@ -248,7 +248,7 @@
       host->SetBoundsInPixels(display_info.bounds_in_native());
     }
 
-    if (base::FeatureList::IsEnabled(features::kVizDisplayCompositor)) {
+    if (features::IsVizDisplayCompositorEnabled()) {
       // |mirror_size| is the size of the mirror source in physical pixels.
       // The RootWindowTransformer corrects the scale of the mirrored display
       // and the location of input events.
diff --git a/ash/public/cpp/BUILD.gn b/ash/public/cpp/BUILD.gn
index 9a42ce693..d7f4372 100644
--- a/ash/public/cpp/BUILD.gn
+++ b/ash/public/cpp/BUILD.gn
@@ -126,7 +126,7 @@
 
   deps = [
     "//ash/public/cpp/vector_icons",
-    "//chromeos:chromeos_constants",
+    "//chromeos/constants",
     "//chromeos/dbus:power_manager_proto",
     "//components/prefs",
     "//mojo/public/cpp/bindings",
diff --git a/ash/public/interfaces/app_list.mojom b/ash/public/interfaces/app_list.mojom
index de7c7c3..481be16 100644
--- a/ash/public/interfaces/app_list.mojom
+++ b/ash/public/interfaces/app_list.mojom
@@ -101,6 +101,15 @@
   kStatusSyncing,  // Syncing apps or installing synced apps.
 };
 
+// The UI component the user launched the app from. Must match
+// chrome/browser/ui/app_list/app_launch_event_logger.proto.
+enum AppListLaunchedFrom {
+  kLaunchedFromGrid = 1,
+  kLaunchedFromSuggestionChip,
+  kLaunchedFromShelf,
+  kLaunchedFromSearchBox,
+};
+
 // How the result should be displayed. Do not change the order of these as
 // they are used for metrics.
 enum SearchResultDisplayType {
@@ -320,7 +329,11 @@
   // Logs a click on the search result.
   // |result_id|: the id of the search result that was clicked on.
   // |suggestion_index|: the position of the result in the suggestion chip.
-  LogSearchClick(string result_id, int32 suggestion_index);
+  // |launched_from|: where the app was launched.
+  LogSearchClick(
+      string result_id,
+      int32 suggestion_index,
+      AppListLaunchedFrom launched_from);
   // Invokes a custom action on a result with |result_id|.
   // |action_index| corresponds to the index of an action on the search result,
   // for example, installing. They are stored in SearchResult::actions_.
diff --git a/ash/public/interfaces/shell_test_api.test-mojom b/ash/public/interfaces/shell_test_api.test-mojom
index 3dbd259..ab932ae 100644
--- a/ash/public/interfaces/shell_test_api.test-mojom
+++ b/ash/public/interfaces/shell_test_api.test-mojom
@@ -33,4 +33,8 @@
 
   // Set the minimum velocity to cause fling gesture.
   SetMinFlingVelocity(float velocity);
+
+  // Returns the number of child windows for the given container in the primary
+  // root window.
+  GetChildWindowCountInContainer(int32 container_id) => (int32 count);
 };
diff --git a/ash/shell_test_api.cc b/ash/shell_test_api.cc
index 9be0c02..c636176 100644
--- a/ash/shell_test_api.cc
+++ b/ash/shell_test_api.cc
@@ -126,4 +126,15 @@
   ui::GestureConfiguration::GetInstance()->set_min_fling_velocity(velocity);
 }
 
+void ShellTestApi::GetChildWindowCountInContainer(
+    int container_id,
+    GetChildWindowCountInContainerCallback cb) {
+  auto* container =
+      ash::Shell::GetPrimaryRootWindow()->GetChildById(container_id);
+  // Return an negative count to indicate that the container is invalid.
+  if (!container)
+    std::move(cb).Run(-1);
+  std::move(cb).Run(container->children().size());
+}
+
 }  // namespace ash
diff --git a/ash/shell_test_api.h b/ash/shell_test_api.h
index 97f815b..a68e97e 100644
--- a/ash/shell_test_api.h
+++ b/ash/shell_test_api.h
@@ -63,6 +63,9 @@
   void ToggleOverviewMode(ToggleOverviewModeCallback cb) override;
   void AddRemoveDisplay() override;
   void SetMinFlingVelocity(float velocity) override;
+  void GetChildWindowCountInContainer(
+      int container_id,
+      GetChildWindowCountInContainerCallback cb) override;
 
  private:
   Shell* shell_;  // not owned
diff --git a/ash/system/network/network_icon.cc b/ash/system/network/network_icon.cc
index e26c45f..020c4577 100644
--- a/ash/system/network/network_icon.cc
+++ b/ash/system/network/network_icon.cc
@@ -39,7 +39,7 @@
 // class used for maintaining a map of network state and images.
 class NetworkIconImpl {
  public:
-  NetworkIconImpl(const std::string& path,
+  NetworkIconImpl(const std::string& guid,
                   IconType icon_type,
                   NetworkType network_type);
 
@@ -106,14 +106,14 @@
 }
 
 void PurgeIconMap(IconType icon_type,
-                  const std::set<std::string>& network_paths) {
+                  const std::set<std::string>& network_guids) {
   NetworkIconMap* icon_map = GetIconMapInstance(icon_type, false);
   if (!icon_map)
     return;
   for (NetworkIconMap::iterator loop_iter = icon_map->begin();
        loop_iter != icon_map->end();) {
     NetworkIconMap::iterator cur_iter = loop_iter++;
-    if (network_paths.count(cur_iter->first) == 0) {
+    if (network_guids.count(cur_iter->first) == 0) {
       delete cur_iter->second;
       icon_map->erase(cur_iter);
     }
@@ -304,7 +304,7 @@
 }  // namespace
 
 NetworkIconState::NetworkIconState(const chromeos::NetworkState* network) {
-  path = network->path();
+  guid = network->guid();
   name = network->name();
 
   const std::string& network_type = network->type();
@@ -361,7 +361,7 @@
 //------------------------------------------------------------------------------
 // NetworkIconImpl
 
-NetworkIconImpl::NetworkIconImpl(const std::string& path,
+NetworkIconImpl::NetworkIconImpl(const std::string& guid,
                                  IconType icon_type,
                                  NetworkType network_type)
     : icon_type_(icon_type) {
@@ -471,11 +471,11 @@
   // Find or add the icon.
   NetworkIconMap* icon_map = GetIconMap(icon_type);
   NetworkIconImpl* icon;
-  NetworkIconMap::iterator iter = icon_map->find(network.path);
+  NetworkIconMap::iterator iter = icon_map->find(network.guid);
   if (iter == icon_map->end()) {
     VLOG(1) << "new NetworkIconImpl: " << network.name;
-    icon = new NetworkIconImpl(network.path, icon_type, network.type);
-    icon_map->insert(std::make_pair(network.path, icon));
+    icon = new NetworkIconImpl(network.guid, icon_type, network.type);
+    icon_map->insert(std::make_pair(network.guid, icon));
   } else {
     VLOG(1) << "found NetworkIconImpl: " << network.name;
     icon = iter->second;
@@ -644,12 +644,12 @@
   return base::UTF8ToUTF16(network.name);
 }
 
-void PurgeNetworkIconCache(const std::set<std::string>& network_paths) {
-  PurgeIconMap(ICON_TYPE_TRAY_OOBE, network_paths);
-  PurgeIconMap(ICON_TYPE_TRAY_REGULAR, network_paths);
-  PurgeIconMap(ICON_TYPE_DEFAULT_VIEW, network_paths);
-  PurgeIconMap(ICON_TYPE_LIST, network_paths);
-  PurgeIconMap(ICON_TYPE_MENU_LIST, network_paths);
+void PurgeNetworkIconCache(const std::set<std::string>& network_guids) {
+  PurgeIconMap(ICON_TYPE_TRAY_OOBE, network_guids);
+  PurgeIconMap(ICON_TYPE_TRAY_REGULAR, network_guids);
+  PurgeIconMap(ICON_TYPE_DEFAULT_VIEW, network_guids);
+  PurgeIconMap(ICON_TYPE_LIST, network_guids);
+  PurgeIconMap(ICON_TYPE_MENU_LIST, network_guids);
 }
 
 SignalStrength GetSignalStrengthForNetwork(
diff --git a/ash/system/network/network_icon.h b/ash/system/network/network_icon.h
index d5a754aa..65d7e410 100644
--- a/ash/system/network/network_icon.h
+++ b/ash/system/network/network_icon.h
@@ -46,7 +46,7 @@
   NetworkIconState& operator=(const NetworkIconState& other);
   ~NetworkIconState();
 
-  std::string path;
+  std::string guid;
   std::string name;
   NetworkType type;
   ConnectionStateType connection_state;
@@ -128,10 +128,10 @@
 ASH_EXPORT base::string16 GetLabelForNetwork(const NetworkIconState&,
                                              IconType icon_type);
 
-// Called periodically with the current list of network paths. Removes cached
+// Called periodically with the current list of network guids. Removes cached
 // entries that are no longer in the list.
 ASH_EXPORT void PurgeNetworkIconCache(
-    const std::set<std::string>& network_paths);
+    const std::set<std::string>& network_guids);
 
 // Called by ChromeVox to give a verbal indication of the network icon. Returns
 // the signal strength of |network|, if it is a network type with a signal
diff --git a/ash/system/network/network_icon_purger.cc b/ash/system/network/network_icon_purger.cc
index c6e45b5..44c6b7ad 100644
--- a/ash/system/network/network_icon_purger.cc
+++ b/ash/system/network/network_icon_purger.cc
@@ -21,12 +21,12 @@
   NetworkStateHandler::NetworkStateList networks;
   NetworkHandler::Get()->network_state_handler()->GetVisibleNetworkList(
       &networks);
-  std::set<std::string> network_paths;
+  std::set<std::string> network_guids;
   for (NetworkStateHandler::NetworkStateList::iterator iter = networks.begin();
        iter != networks.end(); ++iter) {
-    network_paths.insert((*iter)->path());
+    network_guids.insert((*iter)->guid());
   }
-  network_icon::PurgeNetworkIconCache(network_paths);
+  network_icon::PurgeNetworkIconCache(network_guids);
 }
 
 }  // namespace
diff --git a/ash/wm/wm_toplevel_window_event_handler.cc b/ash/wm/wm_toplevel_window_event_handler.cc
index ba5ea30..693727c 100644
--- a/ash/wm/wm_toplevel_window_event_handler.cc
+++ b/ash/wm/wm_toplevel_window_event_handler.cc
@@ -515,6 +515,13 @@
     ::wm::WindowMoveSource source,
     EndClosure end_closure,
     bool update_gesture_target) {
+  if (gesture_target_ != nullptr && update_gesture_target) {
+    DCHECK_EQ(source, ::wm::WINDOW_MOVE_SOURCE_TOUCH);
+    // Transfer events for gesture if switching to new target.
+    window->env()->gesture_recognizer()->TransferEventsTo(
+        gesture_target_, window, ui::TransferTouchesBehavior::kDontCancel);
+  }
+
   if (!PrepareForDrag(window, point_in_parent, window_component, source)) {
     // Treat failure to start as a revert.
     if (end_closure)
@@ -527,11 +534,6 @@
   // |gesture_target_| needs to be updated if the drag originated from a
   // client (i.e. |this| never handled ET_GESTURE_EVENT_BEGIN).
   if (in_gesture_drag_ && (!gesture_target_ || update_gesture_target)) {
-    if (gesture_target_ && gesture_target_ != window) {
-      // Transfer events for gesture if switching to new target.
-      window->env()->gesture_recognizer()->TransferEventsTo(
-          gesture_target_, window, ui::TransferTouchesBehavior::kDontCancel);
-    }
     UpdateGestureTarget(window);
   }
 
diff --git a/base/BUILD.gn b/base/BUILD.gn
index 4e1c41c..f7791c4 100644
--- a/base/BUILD.gn
+++ b/base/BUILD.gn
@@ -361,6 +361,8 @@
     "mac/mach_logging.h",
     "mac/mach_port_broker.h",
     "mac/mach_port_broker.mm",
+    "mac/mach_port_rendezvous.cc",
+    "mac/mach_port_rendezvous.h",
     "mac/mach_port_util.cc",
     "mac/mach_port_util.h",
     "mac/objc_release_properties.h",
@@ -476,8 +478,6 @@
     "metrics/dummy_histogram.h",
     "metrics/field_trial.cc",
     "metrics/field_trial.h",
-    "metrics/field_trial_memory_mac.cc",
-    "metrics/field_trial_memory_mac.h",
     "metrics/field_trial_param_associator.cc",
     "metrics/field_trial_param_associator.h",
     "metrics/field_trial_params.cc",
@@ -2391,6 +2391,7 @@
     "mac/foundation_util_unittest.mm",
     "mac/mac_util_unittest.mm",
     "mac/mach_port_broker_unittest.cc",
+    "mac/mach_port_rendezvous_unittest.cc",
     "mac/objc_release_properties_unittest.mm",
     "mac/scoped_nsobject_unittest.mm",
     "mac/scoped_objc_class_swizzler_unittest.mm",
@@ -2422,7 +2423,6 @@
     "message_loop/message_pump_unittest.cc",
     "message_loop/work_id_provider_unittest.cc",
     "metrics/bucket_ranges_unittest.cc",
-    "metrics/field_trial_memory_mac_unittest.cc",
     "metrics/field_trial_params_unittest.cc",
     "metrics/field_trial_unittest.cc",
     "metrics/histogram_base_unittest.cc",
diff --git a/base/android/content_uri_utils.cc b/base/android/content_uri_utils.cc
index a9bc395..7abcc89d 100644
--- a/base/android/content_uri_utils.cc
+++ b/base/android/content_uri_utils.cc
@@ -44,7 +44,9 @@
 
 bool MaybeGetFileDisplayName(const FilePath& content_uri,
                              base::string16* file_display_name) {
-  DCHECK(content_uri.IsContentUri());
+  if (!content_uri.IsContentUri())
+    return false;
+
   DCHECK(file_display_name);
 
   JNIEnv* env = base::android::AttachCurrentThread();
diff --git a/base/bind.h b/base/bind.h
index df287dc..3c3d5d3 100644
--- a/base/bind.h
+++ b/base/bind.h
@@ -337,31 +337,6 @@
   return internal::RetainedRefWrapper<T>(std::move(o));
 }
 
-// ConstRef() allows binding a constant reference to an argument rather
-// than a copy.
-//
-// EXAMPLE OF ConstRef():
-//
-//   void foo(int arg) { cout << arg << endl }
-//
-//   int n = 1;
-//   Closure no_ref = Bind(&foo, n);
-//   Closure has_ref = Bind(&foo, ConstRef(n));
-//
-//   no_ref.Run();  // Prints "1"
-//   has_ref.Run();  // Prints "1"
-//
-//   n = 2;
-//   no_ref.Run();  // Prints "1"
-//   has_ref.Run();  // Prints "2"
-//
-// Note that because ConstRef() takes a reference on |n|, |n| must outlive all
-// its bound callbacks.
-template <typename T>
-inline std::reference_wrapper<const T> ConstRef(const T& o) {
-  return std::cref(o);
-}
-
 // Owned() transfers ownership of an object to the Callback resulting from
 // bind; the object will be deleted when the Callback is deleted.
 //
diff --git a/base/bind_unittest.cc b/base/bind_unittest.cc
index 161927f..af5f13b 100644
--- a/base/bind_unittest.cc
+++ b/base/bind_unittest.cc
@@ -4,6 +4,7 @@
 
 #include "base/bind.h"
 
+#include <functional>
 #include <memory>
 #include <utility>
 #include <vector>
@@ -646,15 +647,16 @@
   EXPECT_EQ(2, std::move(normal_func_cb).Run(2));
 }
 
-// ConstRef() wrapper support.
-//   - Binding w/o ConstRef takes a copy.
-//   - Binding a ConstRef takes a reference.
-//   - Binding ConstRef to a function ConstRef does not copy on invoke.
-TEST_F(BindTest, ConstRefForRepeating) {
+// std::cref() wrapper support.
+//   - Binding w/o std::cref takes a copy.
+//   - Binding a std::cref takes a reference.
+//   - Binding std::cref to a function std::cref does not copy on invoke.
+TEST_F(BindTest, StdCrefForRepeating) {
   int n = 1;
 
   RepeatingCallback<int()> copy_cb = BindRepeating(&Identity, n);
-  RepeatingCallback<int()> const_ref_cb = BindRepeating(&Identity, ConstRef(n));
+  RepeatingCallback<int()> const_ref_cb =
+      BindRepeating(&Identity, std::cref(n));
   EXPECT_EQ(n, copy_cb.Run());
   EXPECT_EQ(n, const_ref_cb.Run());
   n++;
@@ -667,7 +669,7 @@
   int move_assigns = 0;
   CopyMoveCounter counter(&copies, &assigns, &move_constructs, &move_assigns);
   RepeatingCallback<int()> all_const_ref_cb =
-      BindRepeating(&GetCopies, ConstRef(counter));
+      BindRepeating(&GetCopies, std::cref(counter));
   EXPECT_EQ(0, all_const_ref_cb.Run());
   EXPECT_EQ(0, copies);
   EXPECT_EQ(0, assigns);
@@ -675,11 +677,11 @@
   EXPECT_EQ(0, move_assigns);
 }
 
-TEST_F(BindTest, ConstRefForOnce) {
+TEST_F(BindTest, StdCrefForOnce) {
   int n = 1;
 
   OnceCallback<int()> copy_cb = BindOnce(&Identity, n);
-  OnceCallback<int()> const_ref_cb = BindOnce(&Identity, ConstRef(n));
+  OnceCallback<int()> const_ref_cb = BindOnce(&Identity, std::cref(n));
   n++;
   EXPECT_EQ(n - 1, std::move(copy_cb).Run());
   EXPECT_EQ(n, std::move(const_ref_cb).Run());
@@ -690,7 +692,7 @@
   int move_assigns = 0;
   CopyMoveCounter counter(&copies, &assigns, &move_constructs, &move_assigns);
   OnceCallback<int()> all_const_ref_cb =
-      BindOnce(&GetCopies, ConstRef(counter));
+      BindOnce(&GetCopies, std::cref(counter));
   EXPECT_EQ(0, std::move(all_const_ref_cb).Run());
   EXPECT_EQ(0, copies);
   EXPECT_EQ(0, assigns);
@@ -1020,9 +1022,8 @@
   EXPECT_CALL(has_ref, HasAtLeastOneRef()).WillRepeatedly(Return(true));
 
   const scoped_refptr<HasRef> refptr(&has_ref);
-  CallbackType<TypeParam, int()> scoped_refptr_const_ref_cb =
-      TypeParam::Bind(&FunctionWithScopedRefptrFirstParam,
-                      base::ConstRef(refptr), 1);
+  CallbackType<TypeParam, int()> scoped_refptr_const_ref_cb = TypeParam::Bind(
+      &FunctionWithScopedRefptrFirstParam, std::cref(refptr), 1);
   EXPECT_EQ(1, std::move(scoped_refptr_const_ref_cb).Run());
 }
 
@@ -1501,7 +1502,7 @@
 
 TEST_F(BindTest, UnwrapConstRef) {
   int p = 0;
-  auto const_ref = ConstRef(p);
+  auto const_ref = std::cref(p);
   EXPECT_EQ(&p, &internal::Unwrap(const_ref));
   EXPECT_EQ(&p, &internal::Unwrap(std::move(const_ref)));
 }
diff --git a/base/fuchsia/service_provider_impl.h b/base/fuchsia/service_provider_impl.h
index e8ac85e..186b5d1d 100644
--- a/base/fuchsia/service_provider_impl.h
+++ b/base/fuchsia/service_provider_impl.h
@@ -37,6 +37,9 @@
   void SetOnLastClientDisconnectedClosure(
       base::OnceClosure on_last_client_disconnected);
 
+  // Returns true if one or more clients are connected.
+  bool has_clients() const { return bindings_.size() != 0; }
+
  private:
   // fuchsia::sys::ServiceProvider implementation.
   void ConnectToService(std::string service_name,
diff --git a/base/mac/dispatch_source_mach.h b/base/mac/dispatch_source_mach.h
index 336125f8..d5a90ed 100644
--- a/base/mac/dispatch_source_mach.h
+++ b/base/mac/dispatch_source_mach.h
@@ -40,6 +40,8 @@
   // be received.
   void Resume();
 
+  dispatch_queue_t queue() const { return queue_.get(); }
+
  private:
   // The dispatch queue used to service the source_.
   ScopedDispatchObject<dispatch_queue_t> queue_;
diff --git a/base/mac/mach_port_rendezvous.cc b/base/mac/mach_port_rendezvous.cc
new file mode 100644
index 0000000..5e71440
--- /dev/null
+++ b/base/mac/mach_port_rendezvous.cc
@@ -0,0 +1,387 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/mac/mach_port_rendezvous.h"
+
+#include <bsm/libbsm.h>
+#include <mach/mig.h>
+#include <servers/bootstrap.h>
+#include <unistd.h>
+
+#include <utility>
+
+#include "base/containers/buffer_iterator.h"
+#include "base/logging.h"
+#include "base/mac/foundation_util.h"
+#include "base/mac/mach_logging.h"
+#include "base/mac/scoped_mach_msg_destroy.h"
+#include "base/strings/stringprintf.h"
+
+namespace base {
+
+namespace {
+
+// The name to use in the bootstrap server, formatted with the BaseBundleID and
+// PID of the server.
+constexpr char kBootstrapNameFormat[] = "%s.MachPortRendezvousServer.%d";
+
+// This limit is arbitrary and can be safely increased in the future.
+constexpr size_t kMaximumRendezvousPorts = 5;
+
+enum MachRendezvousMsgId : mach_msg_id_t {
+  kMachRendezvousMsgIdRequest = 'mrzv',
+  kMachRendezvousMsgIdResponse = 'MRZV',
+};
+
+size_t CalculateResponseSize(size_t num_ports) {
+  return sizeof(mach_msg_base_t) +
+         (num_ports * sizeof(mach_msg_port_descriptor_t)) +
+         (num_ports * sizeof(MachPortsForRendezvous::key_type));
+}
+
+}  // namespace
+
+MachRendezvousPort::MachRendezvousPort(mach_port_t name,
+                                       mach_msg_type_name_t disposition)
+    : name_(name), disposition_(disposition) {
+  DCHECK(disposition == MACH_MSG_TYPE_MOVE_RECEIVE ||
+         disposition == MACH_MSG_TYPE_MOVE_SEND ||
+         disposition == MACH_MSG_TYPE_MOVE_SEND_ONCE ||
+         disposition == MACH_MSG_TYPE_COPY_SEND ||
+         disposition == MACH_MSG_TYPE_MAKE_SEND ||
+         disposition == MACH_MSG_TYPE_MAKE_SEND_ONCE);
+}
+
+MachRendezvousPort::MachRendezvousPort(mac::ScopedMachSendRight send_right)
+    : name_(send_right.release()), disposition_(MACH_MSG_TYPE_MOVE_SEND) {}
+
+MachRendezvousPort::MachRendezvousPort(
+    mac::ScopedMachReceiveRight receive_right)
+    : name_(receive_right.release()),
+      disposition_(MACH_MSG_TYPE_MOVE_RECEIVE) {}
+
+MachRendezvousPort::~MachRendezvousPort() = default;
+
+void MachRendezvousPort::Destroy() {
+  // Map the disposition to the type of right to deallocate.
+  mach_port_right_t right = 0;
+  switch (disposition_) {
+    case 0:
+      DCHECK(name_ == MACH_PORT_NULL);
+      return;
+    case MACH_MSG_TYPE_COPY_SEND:
+    case MACH_MSG_TYPE_MAKE_SEND:
+    case MACH_MSG_TYPE_MAKE_SEND_ONCE:
+      // Right is not owned, would be created by transit.
+      return;
+    case MACH_MSG_TYPE_MOVE_RECEIVE:
+      right = MACH_PORT_RIGHT_RECEIVE;
+      break;
+    case MACH_MSG_TYPE_MOVE_SEND:
+      right = MACH_PORT_RIGHT_SEND;
+      break;
+    case MACH_MSG_TYPE_MOVE_SEND_ONCE:
+      right = MACH_PORT_RIGHT_SEND_ONCE;
+      break;
+    default:
+      NOTREACHED() << "Leaking port name " << name_ << " with disposition "
+                   << disposition_;
+      return;
+  }
+  kern_return_t kr = mach_port_mod_refs(mach_task_self(), name_, right, -1);
+  MACH_DCHECK(kr == KERN_SUCCESS, kr)
+      << "Failed to drop ref on port name " << name_;
+
+  name_ = MACH_PORT_NULL;
+  disposition_ = 0;
+}
+
+// static
+MachPortRendezvousServer* MachPortRendezvousServer::GetInstance() {
+  static auto* instance = new MachPortRendezvousServer();
+  return instance;
+}
+
+void MachPortRendezvousServer::RegisterPortsForPid(
+    pid_t pid,
+    const MachPortsForRendezvous& ports) {
+  lock_.AssertAcquired();
+  DCHECK_LT(ports.size(), kMaximumRendezvousPorts);
+  DCHECK(!ports.empty());
+
+  ScopedDispatchObject<dispatch_source_t> exit_watcher(
+      dispatch_source_create(DISPATCH_SOURCE_TYPE_PROC, pid, DISPATCH_PROC_EXIT,
+                             dispatch_source_->queue()));
+  dispatch_source_set_event_handler(exit_watcher, ^{
+    OnClientExited(pid);
+  });
+  dispatch_resume(exit_watcher);
+
+  auto it =
+      client_data_.emplace(pid, ClientData{std::move(exit_watcher), ports});
+  DCHECK(it.second);
+}
+
+MachPortRendezvousServer::ClientData::ClientData(
+    ScopedDispatchObject<dispatch_source_t> exit_watcher,
+    MachPortsForRendezvous ports)
+    : exit_watcher(exit_watcher), ports(ports) {}
+
+MachPortRendezvousServer::ClientData::ClientData(ClientData&&) = default;
+
+MachPortRendezvousServer::ClientData::~ClientData() = default;
+
+MachPortRendezvousServer::MachPortRendezvousServer() {
+  std::string bootstrap_name =
+      StringPrintf(kBootstrapNameFormat, mac::BaseBundleID(), getpid());
+  kern_return_t kr = bootstrap_check_in(
+      bootstrap_port, bootstrap_name.c_str(),
+      mac::ScopedMachReceiveRight::Receiver(server_port_).get());
+  BOOTSTRAP_CHECK(kr == KERN_SUCCESS, kr)
+      << "bootstrap_check_in " << bootstrap_name;
+
+  dispatch_source_ = std::make_unique<DispatchSourceMach>(
+      bootstrap_name.c_str(), server_port_.get(), ^{
+        HandleRequest();
+      });
+  dispatch_source_->Resume();
+}
+
+MachPortRendezvousServer::~MachPortRendezvousServer() {}
+
+void MachPortRendezvousServer::HandleRequest() {
+  // Receive the request message, using the kernel audit token to ascertain the
+  // PID of the sender.
+  struct : mach_msg_header_t {
+    mach_msg_audit_trailer_t trailer;
+  } request{};
+  request.msgh_size = sizeof(request);
+  request.msgh_local_port = server_port_.get();
+
+  const mach_msg_option_t options =
+      MACH_RCV_MSG | MACH_RCV_TIMEOUT |
+      MACH_RCV_TRAILER_TYPE(MACH_MSG_TRAILER_FORMAT_0) |
+      MACH_RCV_TRAILER_ELEMENTS(MACH_RCV_TRAILER_AUDIT);
+
+  mach_msg_return_t mr = mach_msg(&request, options, 0, sizeof(request),
+                                  server_port_.get(), 0, MACH_PORT_NULL);
+  if (mr != KERN_SUCCESS) {
+    MACH_LOG(ERROR, mr) << "mach_msg receive";
+    return;
+  }
+
+  // Destroy the message in case of an early return, which will release
+  // any rights from a bad message. In the case of a disallowed sender,
+  // the destruction of the reply port will break them out of a mach_msg.
+  ScopedMachMsgDestroy scoped_message(&request);
+
+  if (request.msgh_id != kMachRendezvousMsgIdRequest ||
+      request.msgh_size != sizeof(mach_msg_header_t)) {
+    // Do not reply to messages that are unexpected.
+    return;
+  }
+
+  pid_t sender_pid = audit_token_to_pid(request.trailer.msgh_audit);
+  MachPortsForRendezvous ports_to_send = PortsForPid(sender_pid);
+  if (ports_to_send.empty()) {
+    return;
+  }
+
+  std::unique_ptr<uint8_t[]> response =
+      CreateReplyMessage(request.msgh_remote_port, ports_to_send);
+  auto* header = reinterpret_cast<mach_msg_header_t*>(response.get());
+
+  mr = mach_msg(header, MACH_SEND_MSG, header->msgh_size, 0, MACH_PORT_NULL,
+                MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);
+
+  if (mr == KERN_SUCCESS) {
+    scoped_message.Disarm();
+  } else {
+    MACH_LOG(ERROR, mr) << "mach_msg send";
+  }
+}
+
+MachPortsForRendezvous MachPortRendezvousServer::PortsForPid(pid_t pid) {
+  MachPortsForRendezvous ports_to_send;
+  AutoLock lock(lock_);
+  auto it = client_data_.find(pid);
+  if (it != client_data_.end()) {
+    ports_to_send = std::move(it->second.ports);
+    client_data_.erase(it);
+  }
+  return ports_to_send;
+}
+
+std::unique_ptr<uint8_t[]> MachPortRendezvousServer::CreateReplyMessage(
+    mach_port_t reply_port,
+    const MachPortsForRendezvous& ports) {
+  const size_t port_count = ports.size();
+  const size_t buffer_size = CalculateResponseSize(port_count);
+  auto buffer = std::make_unique<uint8_t[]>(buffer_size);
+  BufferIterator<uint8_t> iterator(buffer.get(), buffer_size);
+
+  auto* message = iterator.MutableObject<mach_msg_base_t>();
+  message->header.msgh_bits =
+      MACH_MSGH_BITS_REMOTE(MACH_MSG_TYPE_MOVE_SEND_ONCE) |
+      MACH_MSGH_BITS_COMPLEX;
+  message->header.msgh_size = buffer_size;
+  message->header.msgh_remote_port = reply_port;
+  message->header.msgh_id = kMachRendezvousMsgIdResponse;
+  message->body.msgh_descriptor_count = port_count;
+
+  auto descriptors =
+      iterator.MutableSpan<mach_msg_port_descriptor_t>(port_count);
+  auto port_identifiers =
+      iterator.MutableSpan<MachPortsForRendezvous::key_type>(port_count);
+
+  auto port_it = ports.begin();
+  for (size_t i = 0; i < port_count; ++i, ++port_it) {
+    const MachRendezvousPort& port_for_rendezvous = port_it->second;
+    mach_msg_port_descriptor_t* descriptor = &descriptors[i];
+    descriptor->name = port_for_rendezvous.name();
+    descriptor->disposition = port_for_rendezvous.disposition();
+    descriptor->type = MACH_MSG_PORT_DESCRIPTOR;
+
+    port_identifiers[i] = port_it->first;
+  }
+
+  return buffer;
+}
+
+void MachPortRendezvousServer::OnClientExited(pid_t pid) {
+  MachPortsForRendezvous ports = PortsForPid(pid);
+  for (auto& pair : ports) {
+    pair.second.Destroy();
+  }
+}
+
+// static
+MachPortRendezvousClient* MachPortRendezvousClient::GetInstance() {
+  static MachPortRendezvousClient* client = new MachPortRendezvousClient();
+  if (!client->did_acquire_ports()) {
+    bool ok = client->AcquirePorts();
+    DCHECK(ok);
+  }
+  return client;
+}
+
+mac::ScopedMachSendRight MachPortRendezvousClient::TakeSendRight(
+    MachPortsForRendezvous::key_type key) {
+  MachRendezvousPort port = PortForKey(key);
+  DCHECK(port.disposition() == 0 ||
+         port.disposition() == MACH_MSG_TYPE_PORT_SEND ||
+         port.disposition() == MACH_MSG_TYPE_PORT_SEND_ONCE);
+  return mac::ScopedMachSendRight(port.name());
+}
+
+mac::ScopedMachReceiveRight MachPortRendezvousClient::TakeReceiveRight(
+    MachPortsForRendezvous::key_type key) {
+  MachRendezvousPort port = PortForKey(key);
+  DCHECK(port.disposition() == 0 ||
+         port.disposition() == MACH_MSG_TYPE_PORT_RECEIVE);
+  return mac::ScopedMachReceiveRight(port.name());
+}
+
+size_t MachPortRendezvousClient::GetPortCount() {
+  AutoLock lock(lock_);
+  return ports_.size();
+}
+
+// static
+std::string MachPortRendezvousClient::GetBootstrapName() {
+  return StringPrintf(kBootstrapNameFormat, mac::BaseBundleID(), getppid());
+}
+
+bool MachPortRendezvousClient::AcquirePorts() {
+  AutoLock lock(lock_);
+
+  did_acquire_ports_ = true;
+
+  mac::ScopedMachSendRight server_port;
+  std::string bootstrap_name = GetBootstrapName();
+  kern_return_t kr = bootstrap_look_up(
+      bootstrap_port, const_cast<char*>(bootstrap_name.c_str()),
+      mac::ScopedMachSendRight::Receiver(server_port).get());
+  if (kr != KERN_SUCCESS) {
+    BOOTSTRAP_LOG(ERROR, kr) << "bootstrap_look_up " << bootstrap_name;
+    return false;
+  }
+
+  return SendRequest(std::move(server_port));
+}
+
+bool MachPortRendezvousClient::SendRequest(
+    mac::ScopedMachSendRight server_port) {
+  const size_t buffer_size = CalculateResponseSize(kMaximumRendezvousPorts) +
+                             sizeof(mach_msg_trailer_t);
+  auto buffer = std::make_unique<uint8_t[]>(buffer_size);
+  BufferIterator<uint8_t> iterator(buffer.get(), buffer_size);
+
+  // Perform a send and receive mach_msg.
+  auto* message = iterator.MutableObject<mach_msg_base_t>();
+  message->header.msgh_bits =
+      MACH_MSGH_BITS(MACH_MSG_TYPE_MOVE_SEND, MACH_MSG_TYPE_MAKE_SEND_ONCE);
+  // The |buffer_size| is used for receiving, since it includes space for the
+  // the entire reply and receiving trailer. But for the request being sent,
+  // the size is just an empty message.
+  message->header.msgh_size = sizeof(mach_msg_header_t);
+  message->header.msgh_remote_port = server_port.release();
+  message->header.msgh_local_port = mig_get_reply_port();
+  message->header.msgh_id = kMachRendezvousMsgIdRequest;
+
+  kern_return_t mr = mach_msg(&message->header, MACH_SEND_MSG | MACH_RCV_MSG,
+                              message->header.msgh_size, buffer_size,
+                              message->header.msgh_local_port,
+                              MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);
+  if (mr != KERN_SUCCESS) {
+    MACH_LOG(ERROR, mr) << "mach_msg";
+    return false;
+  }
+
+  if (message->header.msgh_id != kMachRendezvousMsgIdResponse) {
+    // Check if the response contains a rendezvous reply. If there were no
+    // ports for this client, then the send right would have been destroyed.
+    if (message->header.msgh_id == MACH_NOTIFY_SEND_ONCE) {
+      return true;
+    }
+    return false;
+  }
+
+  const size_t port_count = message->body.msgh_descriptor_count;
+
+  auto descriptors = iterator.Span<mach_msg_port_descriptor_t>(port_count);
+  auto port_identifiers =
+      iterator.Span<MachPortsForRendezvous::key_type>(port_count);
+
+  if (descriptors.size() != port_identifiers.size()) {
+    // Ensure that the descriptors and keys are of the same size.
+    return false;
+  }
+
+  for (size_t i = 0; i < port_count; ++i) {
+    MachRendezvousPort rendezvous_port(descriptors[i].name,
+                                       descriptors[i].disposition);
+    ports_.emplace(port_identifiers[i], rendezvous_port);
+  }
+
+  return true;
+}
+
+MachRendezvousPort MachPortRendezvousClient::PortForKey(
+    MachPortsForRendezvous::key_type key) {
+  AutoLock lock(lock_);
+  auto it = ports_.find(key);
+  MachRendezvousPort port;
+  if (it != ports_.end()) {
+    port = it->second;
+    ports_.erase(it);
+  }
+  return port;
+}
+
+MachPortRendezvousClient::MachPortRendezvousClient() = default;
+
+MachPortRendezvousClient::~MachPortRendezvousClient() = default;
+
+}  // namespace base
diff --git a/base/mac/mach_port_rendezvous.h b/base/mac/mach_port_rendezvous.h
new file mode 100644
index 0000000..5c1c61c
--- /dev/null
+++ b/base/mac/mach_port_rendezvous.h
@@ -0,0 +1,219 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_MAC_MACH_PORT_RENDEZVOUS_H_
+#define BASE_MAC_MACH_PORT_RENDEZVOUS_H_
+
+#include <dispatch/dispatch.h>
+#include <mach/mach.h>
+#include <stdint.h>
+#include <sys/types.h>
+
+#include <map>
+#include <memory>
+#include <string>
+
+#include "base/base_export.h"
+#include "base/mac/dispatch_source_mach.h"
+#include "base/mac/scoped_dispatch_object.h"
+#include "base/mac/scoped_mach_port.h"
+#include "base/macros.h"
+#include "base/synchronization/lock.h"
+
+namespace base {
+
+// Mach Port Rendezvous is a technique to exchange Mach port rights across
+// child process creation. macOS does not provide a way to inherit Mach port
+// rights, unlike what is possible with file descriptors. Port rendezvous
+// enables a parent process to register Mach port rights for a nascent child,
+// which the child can then retrieve using Mach IPC by looking up the endpoint
+// in launchd's bootstrap namespace.
+//
+// When launching a child process, the parent process' rendezvous server lets
+// calling code register a collection of ports for the new child. In order to
+// acquire the ports, a child looks up the rendezvous server in the bootstrap
+// namespace, and it sends an IPC message to the server, the reply to which
+// contains the registered ports.
+//
+// Port rendezvous is only permitted between a parent and its direct child
+// process descendants.
+
+// A MachRendezvousPort contains a single Mach port to pass to the child
+// process. The associated disposition controls how the reference count will
+// be manipulated.
+class BASE_EXPORT MachRendezvousPort {
+ public:
+  MachRendezvousPort() = default;
+  // Creates a rendezvous port that allows specifying the specific disposition.
+  MachRendezvousPort(mach_port_t name, mach_msg_type_name_t disposition);
+  // Creates a rendezvous port for MACH_MSG_TYPE_MOVE_SEND.
+  explicit MachRendezvousPort(mac::ScopedMachSendRight send_right);
+  // Creates a rendezvous port for MACH_MSG_TYPE_MOVE_RECEIVE.
+  explicit MachRendezvousPort(mac::ScopedMachReceiveRight receive_right);
+
+  // Note that the destructor does not call Destroy() explicitly.
+  // To avoid leaking ports, either use dispositions that create rights during
+  // transit (MAKE or COPY), or use base::LaunchProcess, which will destroy
+  // rights on failure.
+  ~MachRendezvousPort();
+
+  // Destroys the Mach port right type conveyed |disposition| named by |name|.
+  void Destroy();
+
+  mach_port_t name() const { return name_; }
+
+  mach_msg_type_name_t disposition() const { return disposition_; }
+
+ private:
+  mach_port_t name_ = MACH_PORT_NULL;
+  mach_msg_type_name_t disposition_ = 0;
+  // Copy and assign allowed.
+};
+
+// The collection of ports to pass to a child process. There are no restrictions
+// regarding the keys of the map. Clients are responsible for avoiding
+// collisions with other clients.
+using MachPortsForRendezvous = std::map<uint32_t, MachRendezvousPort>;
+
+// Class that runs a Mach message server, from which client processes can
+// acquire Mach port rights registered for them.
+class BASE_EXPORT MachPortRendezvousServer {
+ public:
+  // Returns the instance of the server. Upon the first call to this method,
+  // the server is created, which registers an endpoint in the Mach bootstrap
+  // namespace.
+  static MachPortRendezvousServer* GetInstance();
+
+  // Registers a collection of Mach ports |ports| to be acquirable by the
+  // process known by |pid|. This cannot be called again for the same |pid|
+  // until the process known by |pid| has either acquired the ports or died.
+  //
+  // This must be called with the lock from GetLock() held.
+  void RegisterPortsForPid(pid_t pid, const MachPortsForRendezvous& ports);
+
+  // Returns a lock on the internal port registration map. The parent process
+  // should hold this lock for the duration of launching a process, including
+  // after calling RegisterPortsForPid(). This ensures that a child process
+  // cannot race acquiring ports before they are registered. The lock should
+  // be released after the child process is launched and the ports are
+  // registered.
+  Lock& GetLock() { return lock_; }
+
+ private:
+  friend class MachPortRendezvousServerTest;
+
+  struct ClientData {
+    ClientData(ScopedDispatchObject<dispatch_source_t> exit_watcher,
+               MachPortsForRendezvous ports);
+    ClientData(ClientData&&);
+    ~ClientData();
+
+    // A DISPATCH_SOURCE_TYPE_PROC / DISPATCH_PROC_EXIT dispatch source. When
+    // the source is triggered, it calls OnClientExited().
+    ScopedDispatchObject<dispatch_source_t> exit_watcher;
+
+    MachPortsForRendezvous ports;
+  };
+
+  MachPortRendezvousServer();
+  ~MachPortRendezvousServer();
+
+  // The server-side Mach message handler. Called by |dispatch_source_| when a
+  // message is received.
+  void HandleRequest();
+
+  // Returns the registered collection of ports for the specified |pid|. An
+  // empty collection indicates no ports were found, as it is invalid to
+  // register with an empty collection. This claims the collection of ports
+  // and removes the entry from |client_data_|.
+  MachPortsForRendezvous PortsForPid(pid_t pid);
+
+  // Returns a buffer containing a well-formed Mach message, destined for
+  // |reply_port| containing descriptors for the specified |ports|.
+  std::unique_ptr<uint8_t[]> CreateReplyMessage(
+      mach_port_t reply_port,
+      const MachPortsForRendezvous& ports);
+
+  // Called by the ClientData::exit_watcher dispatch sources when a process
+  // for which ports have been registered exits. This releases port rights
+  // that are strongly owned, in the event that the child has not claimed them.
+  void OnClientExited(pid_t pid);
+
+  // The Mach receive right for the server. A send right to this is port is
+  // registered in the bootstrap server.
+  mac::ScopedMachReceiveRight server_port_;
+
+  // Mach message dispatch source for |server_port_|.
+  std::unique_ptr<DispatchSourceMach> dispatch_source_;
+
+  // Lock that protects |client_data_|.
+  Lock lock_;
+  // Association of pid-to-ports.
+  std::map<pid_t, ClientData> client_data_;
+
+  DISALLOW_COPY_AND_ASSIGN(MachPortRendezvousServer);
+};
+
+// Client class for accessing the memory object exposed by the
+// MachPortRendezvousServer.
+class BASE_EXPORT MachPortRendezvousClient {
+ public:
+  // Connects to the MachPortRendezvousServer and requests any registered Mach
+  // ports. This only performs the rendezvous once. Subsequent calls to this
+  // method return the same instance.
+  static MachPortRendezvousClient* GetInstance();
+
+  // Returns the Mach send right that was registered with |key|. If no such
+  // right exists, or it was already taken, returns an invalid right. Safe to
+  // call from any thread. DCHECKs if the right referenced by |key| is not a
+  // send or send-once right.
+  mac::ScopedMachSendRight TakeSendRight(MachPortsForRendezvous::key_type key);
+
+  // Returns the Mach receive right that was registered with |key|. If no such
+  // right exists, or it was already taken, returns an invalid right. Safe to
+  // call from any thread. DCHECKs if the right referenced by |key| is not a
+  // receive right.
+  mac::ScopedMachReceiveRight TakeReceiveRight(
+      MachPortsForRendezvous::key_type key);
+
+  // Returns the number of ports in the client. After PerformRendezvous(), this
+  // reflects the number of ports acquired. But as rights are taken, this
+  // only reflects the number of remaining rights.
+  size_t GetPortCount();
+
+  // Returns the name of the server to look up in the bootstrap namespace.
+  static std::string GetBootstrapName();
+
+ private:
+  MachPortRendezvousClient();
+  ~MachPortRendezvousClient();
+
+  // Helper method to look up the server in the bootstrap namespace and send
+  // the acquisition request message.
+  bool AcquirePorts();
+
+  // Sends the actual IPC message to |server_port| and parses the reply.
+  bool SendRequest(mac::ScopedMachSendRight server_port);
+
+  // Returns a MachRendezvousPort for a given key and removes it from the
+  // |ports_| map. If an entry does not exist for that key, then a
+  // MachRendezvousPort with MACH_PORT_NULL is returned.
+  MachRendezvousPort PortForKey(MachPortsForRendezvous::key_type key);
+
+  bool did_acquire_ports() { return did_acquire_ports_; }
+
+  // Lock for the below data members.
+  Lock lock_;
+  // Flag for if the client has attempted to acquire ports. If the client
+  // experienced an error in doing so, this will still be true.
+  bool did_acquire_ports_ = false;
+  // The collection of ports that was acquired.
+  MachPortsForRendezvous ports_;
+
+  DISALLOW_COPY_AND_ASSIGN(MachPortRendezvousClient);
+};
+
+}  // namespace base
+
+#endif  // BASE_MAC_MACH_PORT_RENDEZVOUS_H_
diff --git a/base/mac/mach_port_rendezvous_unittest.cc b/base/mac/mach_port_rendezvous_unittest.cc
new file mode 100644
index 0000000..5af2a86
--- /dev/null
+++ b/base/mac/mach_port_rendezvous_unittest.cc
@@ -0,0 +1,209 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/mac/mach_port_rendezvous.h"
+
+#include <mach/mach.h>
+
+#include <utility>
+
+#include "base/at_exit.h"
+#include "base/mac/mach_logging.h"
+#include "base/strings/stringprintf.h"
+#include "base/test/multiprocess_test.h"
+#include "base/test/test_timeouts.h"
+#include "testing/multiprocess_func_list.h"
+
+namespace base {
+
+namespace {
+
+constexpr MachPortsForRendezvous::key_type kTestPortKey = 'port';
+
+}  // namespace
+
+class MachPortRendezvousServerTest : public MultiProcessTest {
+ public:
+  void SetUp() override {}
+
+  std::map<pid_t, MachPortRendezvousServer::ClientData>& client_data() {
+    return MachPortRendezvousServer::GetInstance()->client_data_;
+  }
+
+ private:
+  ShadowingAtExitManager at_exit_;
+};
+
+MULTIPROCESS_TEST_MAIN(TakeSendRight) {
+  auto* rendezvous_client = MachPortRendezvousClient::GetInstance();
+  CHECK(rendezvous_client);
+
+  CHECK_EQ(1u, rendezvous_client->GetPortCount());
+
+  mac::ScopedMachSendRight port =
+      rendezvous_client->TakeSendRight(kTestPortKey);
+  CHECK(port.is_valid());
+
+  mach_msg_base_t msg{};
+  msg.header.msgh_bits = MACH_MSGH_BITS_REMOTE(MACH_MSG_TYPE_COPY_SEND);
+  msg.header.msgh_size = sizeof(msg);
+  msg.header.msgh_remote_port = port.get();
+  msg.header.msgh_id = 'good';
+
+  kern_return_t kr =
+      mach_msg(&msg.header, MACH_SEND_MSG, msg.header.msgh_size, 0,
+               MACH_PORT_NULL, MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);
+  MACH_CHECK(kr == KERN_SUCCESS, kr) << "mach_msg";
+
+  return 0;
+}
+
+TEST_F(MachPortRendezvousServerTest, SendRight) {
+  auto* server = MachPortRendezvousServer::GetInstance();
+  ASSERT_TRUE(server);
+
+  mac::ScopedMachReceiveRight port;
+  kern_return_t kr =
+      mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE,
+                         mac::ScopedMachReceiveRight::Receiver(port).get());
+  ASSERT_EQ(kr, KERN_SUCCESS);
+
+  MachRendezvousPort rendezvous_port(port.get(), MACH_MSG_TYPE_MAKE_SEND);
+
+  Process child;
+  {
+    AutoLock lock(server->GetLock());
+    child = SpawnChild("TakeSendRight");
+    server->RegisterPortsForPid(
+        child.Pid(), {std::make_pair(kTestPortKey, rendezvous_port)});
+  }
+
+  struct : mach_msg_base_t {
+    mach_msg_trailer_t trailer;
+  } msg{};
+  kr = mach_msg(&msg.header, MACH_RCV_MSG | MACH_RCV_TIMEOUT, 0, sizeof(msg),
+                port.get(), TestTimeouts::action_timeout().InMilliseconds(),
+                MACH_PORT_NULL);
+
+  EXPECT_EQ(kr, KERN_SUCCESS) << mach_error_string(kr);
+  EXPECT_EQ(msg.header.msgh_id, 'good');
+
+  int exit_code;
+  ASSERT_TRUE(WaitForMultiprocessTestChildExit(
+      child, TestTimeouts::action_timeout(), &exit_code));
+
+  EXPECT_EQ(0, exit_code);
+}
+
+MULTIPROCESS_TEST_MAIN(NoRights) {
+  auto* rendezvous_client = MachPortRendezvousClient::GetInstance();
+  CHECK(rendezvous_client);
+  CHECK_EQ(0u, rendezvous_client->GetPortCount());
+  return 0;
+}
+
+TEST_F(MachPortRendezvousServerTest, NoRights) {
+  auto* server = MachPortRendezvousServer::GetInstance();
+  ASSERT_TRUE(server);
+
+  Process child = SpawnChild("NoRights");
+
+  int exit_code;
+  ASSERT_TRUE(WaitForMultiprocessTestChildExit(
+      child, TestTimeouts::action_timeout(), &exit_code));
+
+  EXPECT_EQ(0, exit_code);
+}
+
+MULTIPROCESS_TEST_MAIN(Exit42) {
+  _exit(42);
+}
+
+TEST_F(MachPortRendezvousServerTest, CleanupIfNoRendezvous) {
+  auto* server = MachPortRendezvousServer::GetInstance();
+  ASSERT_TRUE(server);
+
+  mac::ScopedMachReceiveRight port;
+  kern_return_t kr =
+      mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE,
+                         mac::ScopedMachReceiveRight::Receiver(port).get());
+  ASSERT_EQ(kr, KERN_SUCCESS);
+
+  MachRendezvousPort rendezvous_port(port.get(), MACH_MSG_TYPE_MAKE_SEND);
+
+  Process child;
+  {
+    AutoLock lock(server->GetLock());
+    child = SpawnChild("Exit42");
+    server->RegisterPortsForPid(
+        child.Pid(), {std::make_pair(kTestPortKey, rendezvous_port)});
+
+    EXPECT_EQ(1u, client_data().size());
+  }
+
+  int exit_code;
+  ASSERT_TRUE(WaitForMultiprocessTestChildExit(
+      child, TestTimeouts::action_timeout(), &exit_code));
+
+  EXPECT_EQ(42, exit_code);
+
+  EXPECT_EQ(0u, client_data().size());
+}
+
+TEST_F(MachPortRendezvousServerTest, DestroyRight) {
+  const struct {
+    // How to create the port.
+    bool insert_send_right;
+
+    // Disposition for MachRendezvousPort.
+    mach_port_right_t disposition;
+
+    // After calling DestroyRight.
+    bool is_dead_name;
+    mach_port_urefs_t send_rights;
+  } kCases[] = {
+      {true, MACH_MSG_TYPE_MOVE_RECEIVE, true, 0},
+      {true, MACH_MSG_TYPE_MOVE_SEND, false, 0},
+      {true, MACH_MSG_TYPE_COPY_SEND, false, 1},
+      {true, MACH_MSG_TYPE_MAKE_SEND, false, 1},
+      {false, MACH_MSG_TYPE_MAKE_SEND, false, 0},
+      {true, MACH_MSG_TYPE_MAKE_SEND_ONCE, false, 1},
+      // It's not possible to test MOVE_SEND_ONCE since one cannot
+      // insert_right MAKE_SEND_ONCE.
+  };
+
+  for (size_t i = 0; i < base::size(kCases); ++i) {
+    SCOPED_TRACE(base::StringPrintf("case %zu", i).c_str());
+    const auto& test = kCases[i];
+
+    // This test deliberately leaks Mach port rights.
+    mach_port_t port;
+    kern_return_t kr =
+        mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &port);
+    ASSERT_EQ(kr, KERN_SUCCESS);
+
+    if (test.insert_send_right) {
+      kr = mach_port_insert_right(mach_task_self(), port, port,
+                                  MACH_MSG_TYPE_MAKE_SEND);
+      ASSERT_EQ(kr, KERN_SUCCESS);
+    }
+
+    MachRendezvousPort rendezvous_port(port, test.disposition);
+    rendezvous_port.Destroy();
+
+    mach_port_type_t type = 0;
+    kr = mach_port_type(mach_task_self(), port, &type);
+    ASSERT_EQ(kr, KERN_SUCCESS);
+
+    EXPECT_EQ(type == MACH_PORT_TYPE_DEAD_NAME, test.is_dead_name) << type;
+
+    mach_port_urefs_t refs = 0;
+    kr =
+        mach_port_get_refs(mach_task_self(), port, MACH_PORT_RIGHT_SEND, &refs);
+    ASSERT_EQ(kr, KERN_SUCCESS);
+    EXPECT_EQ(refs, test.send_rights);
+  }
+}
+
+}  // namespace base
diff --git a/base/mac/sdk_forward_declarations.h b/base/mac/sdk_forward_declarations.h
index 4ad015c..6a4cd64 100644
--- a/base/mac/sdk_forward_declarations.h
+++ b/base/mac/sdk_forward_declarations.h
@@ -16,7 +16,6 @@
 #import <CoreWLAN/CoreWLAN.h>
 #import <IOBluetooth/IOBluetooth.h>
 #import <ImageCaptureCore/ImageCaptureCore.h>
-#import <LocalAuthentication/LocalAuthentication.h>
 #import <QuartzCore/QuartzCore.h>
 #include <stdint.h>
 
@@ -70,22 +69,6 @@
 
 #endif  // MAC_OS_X_VERSION_10_12
 
-#if !defined(MAC_OS_X_VERSION_10_13_2) || \
-    MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_13_2
-
-enum {
-  LABiometryTypeNone = 0,
-  LABiometryTypeFaceID = 1,
-  LABiometryTypeTouchID = 2
-};
-typedef NSInteger LABiometryType;
-
-@interface LAContext (HighSierraPointTwoSDK)
-@property(nonatomic, readonly) LABiometryType biometryType;
-@end
-
-#endif  // MAC_OS_X_VERSION_10_13_2
-
 // ----------------------------------------------------------------------------
 // Define NSStrings only available in newer versions of the OSX SDK to force
 // them to be statically linked.
diff --git a/base/metrics/field_trial.cc b/base/metrics/field_trial.cc
index c8b7a255..4522e72 100644
--- a/base/metrics/field_trial.cc
+++ b/base/metrics/field_trial.cc
@@ -26,9 +26,7 @@
 #include "base/unguessable_token.h"
 
 // On POSIX, the fd is shared using the mapping in GlobalDescriptors.
-#if defined(OS_MACOSX) && !defined(OS_IOS)
-#include "base/metrics/field_trial_memory_mac.h"
-#elif defined(OS_POSIX) && !defined(OS_NACL)
+#if defined(OS_POSIX) && !defined(OS_NACL)
 #include "base/posix/global_descriptors.h"
 #endif
 
@@ -57,6 +55,10 @@
 // processes and possibly causing crashes (see crbug.com/661617).
 const size_t kFieldTrialAllocationSize = 128 << 10;  // 128 KiB
 
+#if defined(OS_MACOSX) && !defined(OS_IOS)
+constexpr MachPortsForRendezvous::key_type kFieldTrialRendezvousKey = 'fldt';
+#endif
+
 // Writes out string1 and then string2 to pickle.
 void WriteStringPair(Pickle* pickle,
                      const StringPiece& string1,
@@ -868,12 +870,18 @@
 // TODO(fuchsia): Implement shared-memory configuration (crbug.com/752368).
 #elif defined(OS_MACOSX) && !defined(OS_IOS)
 // static
-FieldTrialMemoryServer* FieldTrialList::GetFieldTrialMemoryServer() {
-  if (global_) {
-    InstantiateFieldTrialAllocatorIfNeeded();
-    return global_->field_trial_server_.get();
+void FieldTrialList::InsertFieldTrialHandleIfNeeded(
+    MachPortsForRendezvous* rendezvous_ports) {
+  if (!global_)
+    return;
+  InstantiateFieldTrialAllocatorIfNeeded();
+  if (global_->readonly_allocator_region_.IsValid()) {
+    rendezvous_ports->emplace(
+        kFieldTrialRendezvousKey,
+        MachRendezvousPort(
+            global_->readonly_allocator_region_.GetPlatformHandle(),
+            MACH_MSG_TYPE_COPY_SEND));
   }
-  return nullptr;
 }
 #elif defined(OS_POSIX) && !defined(OS_NACL)
 // static
@@ -1195,7 +1203,7 @@
 #elif defined(OS_MACOSX) && !defined(OS_IOS)
   // The handle on Mac is looked up directly by the child, rather than being
   // transferred to the child over the command line.
-  ss << "0,";
+  ss << kFieldTrialRendezvousKey << ",";
 #elif !defined(OS_POSIX)
 #error Unsupported OS
 #endif
@@ -1241,9 +1249,10 @@
   }
   win::ScopedHandle scoped_handle(handle);
 #elif defined(OS_MACOSX) && !defined(OS_IOS)
+  auto* rendezvous = MachPortRendezvousClient::GetInstance();
   mac::ScopedMachSendRight scoped_handle =
-      FieldTrialMemoryClient::AcquireMemoryObject();
-  if (scoped_handle == MACH_PORT_NULL)
+      rendezvous->TakeSendRight(field_trial_handle);
+  if (!scoped_handle.is_valid())
     return base::ReadOnlySharedMemoryRegion();
 #endif
 
@@ -1407,13 +1416,6 @@
 #if !defined(OS_NACL)
   global_->readonly_allocator_region_ = std::move(shm.region);
 #endif
-
-#if defined(OS_MACOSX) && !defined(OS_IOS)
-  global_->field_trial_server_ = std::make_unique<FieldTrialMemoryServer>(
-      global_->readonly_allocator_region_.GetPlatformHandle());
-  bool ok = global_->field_trial_server_->Start();
-  DCHECK(ok);
-#endif
 }
 
 // static
diff --git a/base/metrics/field_trial.h b/base/metrics/field_trial.h
index cb854cb..8e75a9a 100644
--- a/base/metrics/field_trial.h
+++ b/base/metrics/field_trial.h
@@ -82,10 +82,13 @@
 #include "base/time/time.h"
 #include "build/build_config.h"
 
+#if defined(OS_MACOSX) && !defined(OS_IOS)
+#include "base/mac/mach_port_rendezvous.h"
+#endif
+
 namespace base {
 
 class FieldTrialList;
-class FieldTrialMemoryServer;
 
 class BASE_EXPORT FieldTrial : public RefCounted<FieldTrial> {
  public:
@@ -592,7 +595,8 @@
 #elif defined(OS_MACOSX) && !defined(OS_IOS)
   // On Mac, the field trial shared memory is accessed via a Mach server, which
   // the child looks up directly.
-  static FieldTrialMemoryServer* GetFieldTrialMemoryServer();
+  static void InsertFieldTrialHandleIfNeeded(
+      MachPortsForRendezvous* rendezvous_ports);
 #elif defined(OS_POSIX) && !defined(OS_NACL)
   // On POSIX, we also need to explicitly pass down this file descriptor that
   // should be shared with the child process. Returns -1 if it was not
@@ -806,12 +810,6 @@
   // AppendFieldTrialHandleIfNeeded().
   base::ReadOnlySharedMemoryRegion readonly_allocator_region_;
 
-#if defined(OS_MACOSX) && !defined(OS_IOS)
-  // Mach message server that handles requests to acquire the shared memory
-  // object.
-  std::unique_ptr<FieldTrialMemoryServer> field_trial_server_;
-#endif
-
   // Tracks whether CreateTrialsFromCommandLine() has been called.
   bool create_trials_from_command_line_called_ = false;
 
diff --git a/base/metrics/field_trial_memory_mac.cc b/base/metrics/field_trial_memory_mac.cc
deleted file mode 100644
index be1adb2..0000000
--- a/base/metrics/field_trial_memory_mac.cc
+++ /dev/null
@@ -1,206 +0,0 @@
-// Copyright 2018 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "base/metrics/field_trial_memory_mac.h"
-
-#include <bsm/libbsm.h>
-#include <libproc.h>
-#include <mach/mig.h>
-#include <servers/bootstrap.h>
-#include <unistd.h>
-
-#include "base/logging.h"
-#include "base/mac/foundation_util.h"
-#include "base/mac/mach_logging.h"
-#include "base/mac/scoped_mach_msg_destroy.h"
-#include "base/strings/stringprintf.h"
-
-namespace base {
-
-namespace {
-
-// The name to use in the bootstrap server, formatted with the BaseBundleID and
-// PID of the server.
-const char kBootstrapNameFormat[] = "%s.FieldTrialMemoryServer.%d";
-
-enum FieldTrialMsgId : mach_msg_id_t {
-  kFieldTrialMsgIdRequest = 'FTrq',
-  kFieldTrialMsgIdResponse = 'FTsp',
-};
-
-// Message received by the server for handling lookup lookup requests.
-struct FieldTrialMemoryRequestMessage : public mach_msg_base_t {
-  // The size of the message excluding the trailer, used for msgh_size.
-  static const mach_msg_size_t kSendSize;
-
-  mach_msg_audit_trailer_t trailer;
-};
-
-const mach_msg_size_t FieldTrialMemoryRequestMessage::kSendSize =
-    sizeof(FieldTrialMemoryRequestMessage) - sizeof(trailer);
-
-// Message used for sending and receiving the memory object handle.
-struct FieldTrialMemoryResponseMessage : public mach_msg_base_t {
-  // The size of the message excluding the trailer, used for msgh_size.
-  static const mach_msg_size_t kSendSize;
-
-  mach_msg_port_descriptor_t port;
-  mach_msg_trailer_t trailer;
-};
-
-const mach_msg_size_t FieldTrialMemoryResponseMessage::kSendSize =
-    sizeof(FieldTrialMemoryResponseMessage) - sizeof(trailer);
-
-}  // namespace
-
-FieldTrialMemoryServer::FieldTrialMemoryServer(mach_port_t memory_object)
-    : memory_object_(memory_object), server_pid_(getpid()) {
-  DCHECK(memory_object != MACH_PORT_NULL);
-}
-
-FieldTrialMemoryServer::~FieldTrialMemoryServer() {}
-
-bool FieldTrialMemoryServer::Start() {
-  std::string bootstrap_name = GetBootstrapName();
-  kern_return_t kr = bootstrap_check_in(
-      bootstrap_port, bootstrap_name.c_str(),
-      mac::ScopedMachReceiveRight::Receiver(server_port_).get());
-  if (kr != KERN_SUCCESS) {
-    BOOTSTRAP_LOG(ERROR, kr) << "bootstrap_check_in " << bootstrap_name;
-    return false;
-  }
-
-  dispatch_source_ = std::make_unique<DispatchSourceMach>(
-      "org.chromium.base.FieldTrialMemoryServer", server_port_.get(), ^{
-        HandleRequest();
-      });
-  dispatch_source_->Resume();
-  return true;
-}
-
-// static
-std::string FieldTrialMemoryServer::GetBootstrapName() {
-  return StringPrintf(kBootstrapNameFormat, mac::BaseBundleID(), getpid());
-}
-
-void FieldTrialMemoryServer::HandleRequest() {
-  // Receive the request message, using the kernel audit token to ascertain the
-  // PID of the sender.
-  FieldTrialMemoryRequestMessage request{};
-  request.header.msgh_size = sizeof(request);
-  request.header.msgh_local_port = server_port_.get();
-
-  const mach_msg_option_t options =
-      MACH_RCV_MSG | MACH_RCV_TRAILER_TYPE(MACH_MSG_TRAILER_FORMAT_0) |
-      MACH_RCV_TRAILER_ELEMENTS(MACH_RCV_TRAILER_AUDIT);
-
-  kern_return_t kr =
-      mach_msg(&request.header, options, 0, sizeof(request), server_port_.get(),
-               MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);
-  if (kr != KERN_SUCCESS) {
-    MACH_LOG(ERROR, kr) << "mach_msg receive";
-    return;
-  }
-
-  // Destroy the message in case of an early return, which will release
-  // any rights from a bad message. In the case of a disallowed sender,
-  // the destruction of the reply port will break them out of a mach_msg.
-  ScopedMachMsgDestroy scoped_message(&request.header);
-
-  if (request.header.msgh_id != kFieldTrialMsgIdRequest ||
-      request.header.msgh_size != request.kSendSize) {
-    // Do not reply to messages that are unexpected.
-    return;
-  }
-
-  // A client is allowed to look up the object if the sending process is a
-  // direct child of this server's process.
-  pid_t sender_pid = audit_token_to_pid(request.trailer.msgh_audit);
-  proc_bsdshortinfo sender{};
-  int rv = proc_pidinfo(sender_pid, PROC_PIDT_SHORTBSDINFO, 0, &sender,
-                        PROC_PIDT_SHORTBSDINFO_SIZE);
-  if (rv != PROC_PIDT_SHORTBSDINFO_SIZE ||
-      sender.pbsi_ppid != static_cast<uint32_t>(server_pid_)) {
-    return;
-  }
-
-  FieldTrialMemoryResponseMessage response{};
-  response.header.msgh_bits =
-      MACH_MSGH_BITS_REMOTE(MACH_MSG_TYPE_MOVE_SEND_ONCE) |
-      MACH_MSGH_BITS_COMPLEX;
-  response.header.msgh_size = response.kSendSize;
-  response.header.msgh_remote_port = request.header.msgh_remote_port;
-  response.header.msgh_id = kFieldTrialMsgIdResponse;
-  response.body.msgh_descriptor_count = 1;
-  response.port.name = memory_object_;
-  response.port.disposition = MACH_MSG_TYPE_COPY_SEND;
-  response.port.type = MACH_MSG_PORT_DESCRIPTOR;
-
-  scoped_message.Disarm();
-
-  kr = mach_msg(&response.header, MACH_SEND_MSG, response.header.msgh_size, 0,
-                MACH_PORT_NULL, MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);
-  MACH_LOG_IF(ERROR, kr != KERN_SUCCESS, kr) << "mach_msg send";
-}
-
-// static
-mac::ScopedMachSendRight FieldTrialMemoryClient::AcquireMemoryObject() {
-  mac::ScopedMachSendRight server_port;
-  std::string bootstrap_name = GetBootstrapName();
-  kern_return_t kr = bootstrap_look_up(
-      bootstrap_port, const_cast<char*>(bootstrap_name.c_str()),
-      mac::ScopedMachSendRight::Receiver(server_port).get());
-  if (kr != KERN_SUCCESS) {
-    BOOTSTRAP_LOG(ERROR, kr) << "bootstrap_look_up " << bootstrap_name;
-    return mac::ScopedMachSendRight();
-  }
-
-  return ChildSendRequest(std::move(server_port));
-}
-
-// static
-std::string FieldTrialMemoryClient::GetBootstrapName() {
-  return StringPrintf(kBootstrapNameFormat, mac::BaseBundleID(), getppid());
-}
-
-// static
-mac::ScopedMachSendRight FieldTrialMemoryClient::ChildSendRequest(
-    mac::ScopedMachSendRight server_port) {
-  // Perform a send and receive mach_msg.
-  union {
-    FieldTrialMemoryRequestMessage request;
-    FieldTrialMemoryResponseMessage response;
-  } msg{};
-  msg.request.header.msgh_bits =
-      MACH_MSGH_BITS(MACH_MSG_TYPE_MOVE_SEND, MACH_MSG_TYPE_MAKE_SEND_ONCE);
-  // The size of |msg| is used for receiving since it includes space for the
-  // trailer, but for the request being sent, the size is just the base message.
-  msg.request.header.msgh_size = msg.request.kSendSize;
-  msg.request.header.msgh_remote_port = server_port.release();
-  msg.request.header.msgh_local_port = mig_get_reply_port();
-  msg.request.header.msgh_id = kFieldTrialMsgIdRequest;
-
-  kern_return_t kr =
-      mach_msg(&msg.request.header, MACH_SEND_MSG | MACH_RCV_MSG,
-               msg.request.header.msgh_size, sizeof(msg.response),
-               msg.request.header.msgh_local_port, MACH_MSG_TIMEOUT_NONE,
-               MACH_PORT_NULL);
-  if (kr != KERN_SUCCESS) {
-    MACH_LOG(ERROR, kr) << "mach_msg";
-    return mac::ScopedMachSendRight();
-  }
-
-  if (msg.response.header.msgh_id != kFieldTrialMsgIdResponse ||
-      msg.response.header.msgh_size != msg.response.kSendSize) {
-    return mac::ScopedMachSendRight();
-  }
-
-  return mac::ScopedMachSendRight(msg.response.port.name);
-}
-
-FieldTrialMemoryClient::FieldTrialMemoryClient() = default;
-
-FieldTrialMemoryClient::~FieldTrialMemoryClient() = default;
-
-}  // namespace base
diff --git a/base/metrics/field_trial_memory_mac.h b/base/metrics/field_trial_memory_mac.h
deleted file mode 100644
index 3a323760..0000000
--- a/base/metrics/field_trial_memory_mac.h
+++ /dev/null
@@ -1,85 +0,0 @@
-// Copyright 2018 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef BASE_METRICS_FIELD_TRIAL_MEMORY_MAC_H_
-#define BASE_METRICS_FIELD_TRIAL_MEMORY_MAC_H_
-
-#include <mach/port.h>
-#include <sys/types.h>
-
-#include <memory>
-#include <string>
-
-#include "base/base_export.h"
-#include "base/mac/dispatch_source_mach.h"
-#include "base/mac/scoped_mach_port.h"
-#include "base/macros.h"
-
-namespace base {
-
-// FieldTrialMemoryServer services requests for the FieldTrial shared memory
-// region on Mac. Shared memory on Mac uses Mach ports, which cannot be
-// transferred across process creation. Instead, this class publishes an
-// endpoint in the bootstrap server. Child processes look up the server and then
-// send requests to acquire the shared memory object. Only processes that are
-// direct children of the process that is running this server are allowed to
-// acquire the memory object send right.
-class BASE_EXPORT FieldTrialMemoryServer {
- public:
-  // Creates a server that will vend access to the passed |memory_object|.
-  // This does not change the user refcount of the object. Start() must be
-  // called before requests will be processed.
-  explicit FieldTrialMemoryServer(mach_port_t memory_object);
-  ~FieldTrialMemoryServer();
-
-  // Starts processing requests for the server. Returns false if the server
-  // could not be started and true on success.
-  bool Start();
-
- private:
-  friend class FieldTrialMemoryServerTest;
-
-  // Exposed for testing.
-  void set_server_pid(pid_t pid) { server_pid_ = pid; }
-
-  // Returns the name of the server to publish in the bootstrap namespace.
-  static std::string GetBootstrapName();
-
-  // The server-side Mach message handler.
-  void HandleRequest();
-
-  mach_port_t memory_object_;  // weak
-  pid_t server_pid_;           // PPID used for access control checks.
-  mac::ScopedMachReceiveRight server_port_;
-  std::unique_ptr<DispatchSourceMach> dispatch_source_;
-
-  DISALLOW_COPY_AND_ASSIGN(FieldTrialMemoryServer);
-};
-
-// Client class for accessing the memory object exposed by the
-// FieldTrialMemoryServer.
-class BASE_EXPORT FieldTrialMemoryClient {
- public:
-  // Called by children of the process running the FieldTrialMemoryServer, this
-  // attempts to acquire the port for the |memory_object_|. Returns the port
-  // on success or MACH_PORT_NULL on error or failure.
-  static mac::ScopedMachSendRight AcquireMemoryObject();
-
-  // Returns the name of the server to look up in the bootstrap namespace.
-  static std::string GetBootstrapName();
-
- private:
-  // Sends the Mach message to |server_port| to acquire the memory object.
-  static mac::ScopedMachSendRight ChildSendRequest(
-      mac::ScopedMachSendRight server_port);
-
-  FieldTrialMemoryClient();
-  ~FieldTrialMemoryClient();
-
-  DISALLOW_COPY_AND_ASSIGN(FieldTrialMemoryClient);
-};
-
-}  // namespace base
-
-#endif  // BASE_METRICS_FIELD_TRIAL_MEMORY_MAC_H_
diff --git a/base/metrics/field_trial_memory_mac_unittest.cc b/base/metrics/field_trial_memory_mac_unittest.cc
deleted file mode 100644
index 536aa3a..0000000
--- a/base/metrics/field_trial_memory_mac_unittest.cc
+++ /dev/null
@@ -1,118 +0,0 @@
-// Copyright 2018 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "base/metrics/field_trial_memory_mac.h"
-
-#include <mach/mach.h>
-#include <mach/mach_vm.h>
-
-#include "base/mac/mach_logging.h"
-#include "base/mac/scoped_mach_vm.h"
-#include "base/test/multiprocess_test.h"
-#include "base/test/test_timeouts.h"
-#include "testing/multiprocess_func_list.h"
-
-namespace base {
-
-namespace {
-
-enum ChildExitCode {
-  kChildExitInvalid,
-  kChildExitNoPort,
-  kChildExitMapFailed,
-  kChildExitBadPattern,
-  kChildExitSuccess,
-};
-
-constexpr char kMemoryTestPattern[] = "Hello there, bear";
-
-constexpr mach_vm_size_t kMemoryAllocationSize = 1024;
-
-}  // namespace
-
-class FieldTrialMemoryServerTest : public MultiProcessTest {
- public:
-  void SetUp() override {
-    mach_vm_address_t address = 0;
-    mach_vm_size_t size = mach_vm_round_page(kMemoryAllocationSize);
-    kern_return_t kr =
-        mach_vm_allocate(mach_task_self(), &address, size, VM_FLAGS_ANYWHERE);
-    ASSERT_EQ(kr, KERN_SUCCESS) << "mach_vm_allocate";
-    memory_.reset(address, size);
-
-    kr = mach_make_memory_entry_64(
-        mach_task_self(), &size, address, VM_PROT_READ,
-        mac::ScopedMachSendRight::Receiver(memory_object_).get(),
-        MACH_PORT_NULL);
-    ASSERT_EQ(kr, KERN_SUCCESS) << "mach_make_memory_entry_64";
-
-    memcpy(reinterpret_cast<void*>(address), kMemoryTestPattern,
-           sizeof(kMemoryTestPattern));
-  }
-
-  void SetServerPid(FieldTrialMemoryServer* server, pid_t server_pid) {
-    server->set_server_pid(server_pid);
-  }
-
-  mach_port_t memory_object() { return memory_object_.get(); }
-
- private:
-  mac::ScopedMachVM memory_;
-  mac::ScopedMachSendRight memory_object_;
-};
-
-MULTIPROCESS_TEST_MAIN(AcquireMemoryObjectAndMap) {
-  mac::ScopedMachSendRight memory_object =
-      FieldTrialMemoryClient::AcquireMemoryObject();
-  if (memory_object == MACH_PORT_NULL)
-    return kChildExitNoPort;
-
-  mach_vm_address_t address = 0;
-  kern_return_t kr =
-      mach_vm_map(mach_task_self(), &address, kMemoryAllocationSize, 0,
-                  VM_FLAGS_ANYWHERE, memory_object.get(), 0, false,
-                  VM_PROT_READ, VM_PROT_READ, VM_INHERIT_NONE);
-  if (kr != KERN_SUCCESS) {
-    MACH_LOG(ERROR, kr) << "mach_vm_map";
-    return kChildExitMapFailed;
-  }
-
-  if (memcmp(kMemoryTestPattern, reinterpret_cast<void*>(address),
-             sizeof(kMemoryTestPattern)) != 0) {
-    return kChildExitBadPattern;
-  }
-
-  return kChildExitSuccess;
-}
-
-TEST_F(FieldTrialMemoryServerTest, AllowedPid) {
-  FieldTrialMemoryServer server(memory_object());
-  ASSERT_TRUE(server.Start());
-
-  Process child = SpawnChild("AcquireMemoryObjectAndMap");
-
-  int exit_code;
-  ASSERT_TRUE(WaitForMultiprocessTestChildExit(
-      child, TestTimeouts::action_timeout(), &exit_code));
-
-  EXPECT_EQ(kChildExitSuccess, exit_code);
-}
-
-TEST_F(FieldTrialMemoryServerTest, BlockedPid) {
-  FieldTrialMemoryServer server(memory_object());
-  // Override the server's PID so that the request does not look like it is
-  // coming from a process that is the child of the server.
-  SetServerPid(&server, 1);
-  ASSERT_TRUE(server.Start());
-
-  Process child = SpawnChild("AcquireMemoryObjectAndMap");
-
-  int exit_code;
-  ASSERT_TRUE(WaitForMultiprocessTestChildExit(
-      child, TestTimeouts::action_timeout(), &exit_code));
-
-  EXPECT_EQ(kChildExitNoPort, exit_code);
-}
-
-}  // namespace base
diff --git a/base/metrics/field_trial_unittest.cc b/base/metrics/field_trial_unittest.cc
index 66eb89d2..f8e619be 100644
--- a/base/metrics/field_trial_unittest.cc
+++ b/base/metrics/field_trial_unittest.cc
@@ -33,7 +33,7 @@
 #endif
 
 #if defined(OS_MACOSX) && !defined(OS_IOS)
-#include "base/metrics/field_trial_memory_mac.h"
+#include "base/mac/mach_port_rendezvous.h"
 #endif
 
 namespace base {
@@ -1477,16 +1477,16 @@
       base::ReadOnlySharedMemoryRegion::Create(4 << 10);
   ASSERT_TRUE(shm.IsValid());
 
-#if defined(OS_MACOSX)
-  FieldTrialMemoryServer mach_server(shm.region.GetPlatformHandle());
-  ASSERT_TRUE(mach_server.Start());
-#endif
-
   std::string serialized =
       FieldTrialList::SerializeSharedMemoryRegionMetadata(shm.region);
 
   LaunchOptions options;
-#if defined(OS_POSIX) && !defined(OS_MACOSX)
+
+#if defined(OS_MACOSX) && !defined(OS_IOS)
+  options.mach_ports_for_rendezvous.insert(
+      std::make_pair('fldt', MachRendezvousPort{shm.region.GetPlatformHandle(),
+                                                MACH_MSG_TYPE_COPY_SEND}));
+#elif defined(OS_POSIX)
 
 #if defined(OS_ANDROID)
   int shm_fd = shm.region.GetPlatformHandle();
@@ -1496,7 +1496,7 @@
 
   // Pick an arbitrary FD number to use for the shmem FD in the child.
   options.fds_to_remap.emplace_back(std::make_pair(shm_fd, 42));
-#endif  // defined(OS_POSIX) && !defined(OS_MACOSX)
+#endif  // defined(OS_POSIX)
   CommandLine cmd_line = GetMultiProcessTestChildBaseCommandLine();
   cmd_line.AppendSwitchASCII("field_trials", serialized);
   cmd_line.AppendSwitchASCII("guid", shm.region.GetGUID().ToString());
diff --git a/base/process/launch.h b/base/process/launch.h
index 61db1bcc..5b84b16 100644
--- a/base/process/launch.h
+++ b/base/process/launch.h
@@ -32,6 +32,10 @@
 #include "base/posix/file_descriptor_shuffle.h"
 #endif
 
+#if defined(OS_MACOSX) && !defined(OS_IOS)
+#include "base/mac/mach_port_rendezvous.h"
+#endif
+
 namespace base {
 
 class CommandLine;
@@ -190,6 +194,18 @@
   bool kill_on_parent_death = false;
 #endif  // defined(OS_LINUX)
 
+#if defined(OS_MACOSX) && !defined(OS_IOS)
+  // Mach ports that will be accessible to the child process. These are not
+  // directly inherited across process creation, but they are stored by a Mach
+  // IPC server that a child process can communicate with to retrieve them.
+  //
+  // After calling LaunchProcess(), any rights that were transferred with MOVE
+  // dispositions will be consumed, even on failure.
+  //
+  // See base/mac/mach_port_rendezvous.h for details.
+  MachPortsForRendezvous mach_ports_for_rendezvous;
+#endif
+
 #if defined(OS_FUCHSIA)
   // If valid, launches the application in that job object.
   zx_handle_t job_handle = ZX_HANDLE_INVALID;
diff --git a/base/process/launch_mac.cc b/base/process/launch_mac.cc
index c12789f..a72c9f1 100644
--- a/base/process/launch_mac.cc
+++ b/base/process/launch_mac.cc
@@ -252,10 +252,37 @@
     }
   }
 
-  // Use posix_spawnp as some callers expect to have PATH consulted.
+  int rv;
   pid_t pid;
-  int rv = posix_spawnp(&pid, executable_path, file_actions.get(), attr.get(),
-                        &argv_cstr[0], new_environ);
+  {
+    Optional<AutoLock> rendezvous_lock;
+    if (!options.mach_ports_for_rendezvous.empty()) {
+      // The server's lock must be held for the duration of posix_spawn so that
+      // new child's PID can be recorded with the set of ports.
+      rendezvous_lock.emplace(
+          MachPortRendezvousServer::GetInstance()->GetLock());
+    }
+
+    // Use posix_spawnp as some callers expect to have PATH consulted.
+    rv = posix_spawnp(&pid, executable_path, file_actions.get(), attr.get(),
+                      &argv_cstr[0], new_environ);
+
+    if (!options.mach_ports_for_rendezvous.empty()) {
+      auto* rendezvous = MachPortRendezvousServer::GetInstance();
+      if (rv == 0) {
+        rendezvous->RegisterPortsForPid(pid, options.mach_ports_for_rendezvous);
+      } else {
+        // Because |options| is const-ref, the collection has to be copied here.
+        // The caller expects to relinquish ownership of any strong rights if
+        // LaunchProcess() were to succeed, so these rights should be manually
+        // destroyed on failure.
+        MachPortsForRendezvous ports = options.mach_ports_for_rendezvous;
+        for (auto& port : ports) {
+          port.second.Destroy();
+        }
+      }
+    }
+  }
 
   // Restore the thread's working directory if it was changed.
   if (!options.current_directory.empty()) {
diff --git a/base/run_loop_unittest.cc b/base/run_loop_unittest.cc
index 3f17678..90a6b71 100644
--- a/base/run_loop_unittest.cc
+++ b/base/run_loop_unittest.cc
@@ -4,6 +4,7 @@
 
 #include "base/run_loop.h"
 
+#include <functional>
 #include <utility>
 
 #include "base/bind.h"
@@ -619,14 +620,15 @@
 
   // Post a task that will fail if it runs inside the nested run loop.
   ThreadTaskRunnerHandle::Get()->PostTask(
-      FROM_HERE, BindOnce(
-                     [](const bool& nested_run_loop_ended,
-                        OnceClosure continuation_callback) {
-                       EXPECT_TRUE(nested_run_loop_ended);
-                       EXPECT_FALSE(RunLoop::IsNestedOnCurrentThread());
-                       std::move(continuation_callback).Run();
-                     },
-                     ConstRef(nested_run_loop_ended), main_loop.QuitClosure()));
+      FROM_HERE,
+      BindOnce(
+          [](const bool& nested_run_loop_ended,
+             OnceClosure continuation_callback) {
+            EXPECT_TRUE(nested_run_loop_ended);
+            EXPECT_FALSE(RunLoop::IsNestedOnCurrentThread());
+            std::move(continuation_callback).Run();
+          },
+          std::cref(nested_run_loop_ended), main_loop.QuitClosure()));
 
   // Post a task flipping the boolean bit for extra verification right before
   // quitting |nested_run_loop|.
diff --git a/base/sampling_heap_profiler/module_cache_win.cc b/base/sampling_heap_profiler/module_cache_win.cc
index 30d4d15..a192054 100644
--- a/base/sampling_heap_profiler/module_cache_win.cc
+++ b/base/sampling_heap_profiler/module_cache_win.cc
@@ -132,8 +132,6 @@
   FilePath pdb_name;
   std::string build_id;
   GetDebugInfoForModule(module_handle, &build_id, &pdb_name);
-  if (build_id.empty())
-    return nullptr;
 
   MODULEINFO module_info;
   if (!::GetModuleInformation(GetCurrentProcessHandle(), module_handle,
diff --git a/base/task/sequence_manager/task_queue_impl.cc b/base/task/sequence_manager/task_queue_impl.cc
index ad5f0a7c..850f177 100644
--- a/base/task/sequence_manager/task_queue_impl.cc
+++ b/base/task/sequence_manager/task_queue_impl.cc
@@ -235,6 +235,7 @@
     }
   }
 
+  bool should_schedule_work = false;
   {
     // TODO(alexclarke): Maybe add a main thread only immediate_incoming_queue
     // See https://crbug.com/901800
@@ -255,11 +256,24 @@
     // addition it may need to schedule a DoWork if this queue isn't blocked.
     if (was_immediate_incoming_queue_empty && immediate_work_queue_empty_) {
       empty_queues_to_reload_handle_.SetActive(true);
-      if (post_immediate_task_should_schedule_work_)
-        sequence_manager_->ScheduleWork();
+      should_schedule_work = post_immediate_task_should_schedule_work_;
     }
   }
 
+  // On windows it's important to call this outside of a lock because calling a
+  // pump while holding a lock can result in priority inversions. See
+  // http://shortn/_ntnKNqjDQT for a discussion.
+  //
+  // Calling ScheduleWork outside the lock should be safe, only the main thread
+  // can mutate |post_immediate_task_should_schedule_work_|. If it transitions
+  // to false we call ScheduleWork redundantly that's harmless. If it
+  // transitions to true, the side effect of
+  // |empty_queues_to_reload_handle_SetActive(true)| is guaranteed to be picked
+  // up by the ThreadController's call to SequenceManagerImpl::DelayTillNextTask
+  // when it computes what continuation (if any) is needed.
+  if (should_schedule_work)
+    sequence_manager_->ScheduleWork();
+
   TraceQueueSize();
 }
 
@@ -375,18 +389,13 @@
 }
 
 void TaskQueueImpl::TakeImmediateIncomingQueueTasks(TaskDeque* queue) {
+  AutoLock immediate_incoming_queue_lock(immediate_incoming_queue_lock_);
   DCHECK(queue->empty());
+  queue->swap(immediate_incoming_queue_);
 
-  {
-    AutoLock immediate_incoming_queue_lock(immediate_incoming_queue_lock_);
-    queue->swap(immediate_incoming_queue_);
-
-    // Since |immediate_incoming_queue| is empty, now is a good time to consider
-    // reducing it's capacity if we're wasting memory.
-    immediate_incoming_queue_.MaybeShrinkQueue();
-
-    UpdateCrossThreadQueueStateLocked();
-  }
+  // Since |immediate_incoming_queue| is empty, now is a good time to consider
+  // reducing it's capacity if we're wasting memory.
+  immediate_incoming_queue_.MaybeShrinkQueue();
 
   // Activate delayed fence if necessary. This is ideologically similar to
   // ActivateDelayedFenceIfNeeded, but due to immediate tasks being posted
@@ -405,11 +414,12 @@
             main_thread_only().current_fence);
         main_thread_only().delayed_work_queue->InsertFenceSilently(
             main_thread_only().current_fence);
-        UpdateCrossThreadQueueState();
         break;
       }
     }
   }
+
+  UpdateCrossThreadQueueStateLocked();
 }
 
 bool TaskQueueImpl::IsEmpty() const {
@@ -686,16 +696,18 @@
   task_unblocked |=
       main_thread_only().delayed_work_queue->InsertFence(current_fence);
 
-  if (!task_unblocked && previous_fence && previous_fence < current_fence) {
+  {
     AutoLock lock(immediate_incoming_queue_lock_);
-    if (!immediate_incoming_queue_.empty() &&
-        immediate_incoming_queue_.front().enqueue_order() > previous_fence &&
-        immediate_incoming_queue_.front().enqueue_order() < current_fence) {
-      task_unblocked = true;
+    if (!task_unblocked && previous_fence && previous_fence < current_fence) {
+      if (!immediate_incoming_queue_.empty() &&
+          immediate_incoming_queue_.front().enqueue_order() > previous_fence &&
+          immediate_incoming_queue_.front().enqueue_order() < current_fence) {
+        task_unblocked = true;
+      }
     }
-  }
 
-  UpdateCrossThreadQueueState();
+    UpdateCrossThreadQueueStateLocked();
+  }
 
   if (IsQueueEnabled() && task_unblocked)
     sequence_manager_->ScheduleWork();
@@ -719,15 +731,17 @@
   bool task_unblocked = main_thread_only().immediate_work_queue->RemoveFence();
   task_unblocked |= main_thread_only().delayed_work_queue->RemoveFence();
 
-  if (!task_unblocked && previous_fence) {
+  {
     AutoLock lock(immediate_incoming_queue_lock_);
-    if (!immediate_incoming_queue_.empty() &&
-        immediate_incoming_queue_.front().enqueue_order() > previous_fence) {
-      task_unblocked = true;
+    if (!task_unblocked && previous_fence) {
+      if (!immediate_incoming_queue_.empty() &&
+          immediate_incoming_queue_.front().enqueue_order() > previous_fence) {
+        task_unblocked = true;
+      }
     }
-  }
 
-  UpdateCrossThreadQueueState();
+    UpdateCrossThreadQueueStateLocked();
+  }
 
   if (IsQueueEnabled() && task_unblocked)
     sequence_manager_->ScheduleWork();
@@ -816,10 +830,16 @@
   LazyNow lazy_now = main_thread_only().time_domain->CreateLazyNow();
   UpdateDelayedWakeUp(&lazy_now);
 
-  UpdateCrossThreadQueueState();
+  bool has_pending_immediate_work;
+
+  {
+    AutoLock lock(immediate_incoming_queue_lock_);
+    UpdateCrossThreadQueueStateLocked();
+    has_pending_immediate_work = HasPendingImmediateWorkLocked();
+  }
 
   if (enable) {
-    if (HasPendingImmediateWork() &&
+    if (has_pending_immediate_work &&
         !main_thread_only().on_next_wake_up_changed_callback.is_null()) {
       // Delayed work notification will be issued via time domain.
       main_thread_only().on_next_wake_up_changed_callback.Run(TimeTicks());
@@ -833,11 +853,6 @@
   }
 }
 
-void TaskQueueImpl::UpdateCrossThreadQueueState() {
-  AutoLock lock(immediate_incoming_queue_lock_);
-  UpdateCrossThreadQueueStateLocked();
-}
-
 void TaskQueueImpl::UpdateCrossThreadQueueStateLocked() {
   immediate_work_queue_empty_ =
       main_thread_only().immediate_work_queue->Empty();
@@ -963,6 +978,13 @@
   return !immediate_incoming_queue_.empty();
 }
 
+bool TaskQueueImpl::HasPendingImmediateWorkLocked() {
+  immediate_incoming_queue_lock_.AssertAcquired();
+  return !main_thread_only().delayed_work_queue->Empty() ||
+         !main_thread_only().immediate_work_queue->Empty() ||
+         !immediate_incoming_queue_.empty();
+}
+
 void TaskQueueImpl::SetOnTaskStartedHandler(
     TaskQueueImpl::OnTaskStartedHandler handler) {
   DCHECK(should_notify_observers_ || handler.is_null());
diff --git a/base/task/sequence_manager/task_queue_impl.h b/base/task/sequence_manager/task_queue_impl.h
index c4f3e2e..24af883 100644
--- a/base/task/sequence_manager/task_queue_impl.h
+++ b/base/task/sequence_manager/task_queue_impl.h
@@ -145,6 +145,8 @@
   // Check for available tasks in immediate work queues.
   // Used to check if we need to generate notifications about delayed work.
   bool HasPendingImmediateWork();
+  bool HasPendingImmediateWorkLocked()
+      EXCLUSIVE_LOCKS_REQUIRED(immediate_incoming_queue_lock_);
 
   bool has_pending_high_resolution_tasks() const {
     return main_thread_only()
@@ -408,7 +410,6 @@
   void ActivateDelayedFenceIfNeeded(TimeTicks now);
 
   // Updates state protected by immediate_incoming_queue_lock_.
-  void UpdateCrossThreadQueueState();
   void UpdateCrossThreadQueueStateLocked()
       EXCLUSIVE_LOCKS_REQUIRED(immediate_incoming_queue_lock_);
 
diff --git a/base/task/task_scheduler/platform_native_worker_pool_win.cc b/base/task/task_scheduler/platform_native_worker_pool_win.cc
index 2aa1633e..af18077 100644
--- a/base/task/task_scheduler/platform_native_worker_pool_win.cc
+++ b/base/task/task_scheduler/platform_native_worker_pool_win.cc
@@ -4,8 +4,10 @@
 
 #include "base/task/task_scheduler/platform_native_worker_pool_win.h"
 
+#include <algorithm>
 #include <utility>
 
+#include "base/system/sys_info.h"
 #include "base/task/task_scheduler/task_tracker.h"
 
 namespace base {
@@ -121,5 +123,20 @@
   ::SubmitThreadpoolWork(work_);
 }
 
+size_t PlatformNativeWorkerPoolWin::GetMaxConcurrentNonBlockedTasksDeprecated()
+    const {
+  // The Windows Thread Pool API gives us no control over the number of workers
+  // that are active at one time. Consequently, we cannot report a true value
+  // here. Instead, the values were chosen to match
+  // TaskScheduler::StartWithDefaultParams.
+  const int num_cores = SysInfo::NumberOfProcessors();
+  return std::max(3, num_cores - 1);
+}
+
+void PlatformNativeWorkerPoolWin::ReportHeartbeatMetrics() const {
+  // Windows Thread Pool API does not provide the capability to determine the
+  // number of worker threads created.
+}
+
 }  // namespace internal
 }  // namespace base
diff --git a/base/task/task_scheduler/platform_native_worker_pool_win.h b/base/task/task_scheduler/platform_native_worker_pool_win.h
index 59b041e..c2c59ae8 100644
--- a/base/task/task_scheduler/platform_native_worker_pool_win.h
+++ b/base/task/task_scheduler/platform_native_worker_pool_win.h
@@ -43,6 +43,8 @@
   void JoinForTesting() override;
   void ReEnqueueSequenceChangingPool(
       SequenceAndTransaction sequence_and_transaction) override;
+  size_t GetMaxConcurrentNonBlockedTasksDeprecated() const override;
+  void ReportHeartbeatMetrics() const override;
 
  private:
   // Callback that gets run by |pool_|. It runs a task off the next sequence on
diff --git a/base/task/task_scheduler/scheduler_worker_pool.cc b/base/task/task_scheduler/scheduler_worker_pool.cc
index 98bd8b6..f37acf93 100644
--- a/base/task/task_scheduler/scheduler_worker_pool.cc
+++ b/base/task/task_scheduler/scheduler_worker_pool.cc
@@ -4,6 +4,8 @@
 
 #include "base/task/task_scheduler/scheduler_worker_pool.h"
 
+#include <utility>
+
 #include "base/bind.h"
 #include "base/bind_helpers.h"
 #include "base/lazy_instance.h"
@@ -74,5 +76,19 @@
   }
 }
 
+void SchedulerWorkerPool::UpdateSortKey(
+    SequenceAndTransaction sequence_and_transaction) {
+  // TODO(fdoray): A worker should be woken up when the priority of a
+  // BEST_EFFORT task is increased and |num_running_best_effort_tasks_| is
+  // equal to |max_best_effort_tasks_|.
+  AutoSchedulerLock auto_lock(lock_);
+  priority_queue_.UpdateSortKey(std::move(sequence_and_transaction));
+}
+
+bool SchedulerWorkerPool::RemoveSequence(scoped_refptr<Sequence> sequence) {
+  AutoSchedulerLock auto_lock(lock_);
+  return priority_queue_.RemoveSequence(std::move(sequence));
+}
+
 }  // namespace internal
 }  // namespace base
diff --git a/base/task/task_scheduler/scheduler_worker_pool.h b/base/task/task_scheduler/scheduler_worker_pool.h
index c4b02de5..bd13a85 100644
--- a/base/task/task_scheduler/scheduler_worker_pool.h
+++ b/base/task/task_scheduler/scheduler_worker_pool.h
@@ -55,6 +55,15 @@
   // Returns true if the worker pool is registered in TLS.
   bool IsBoundToCurrentThread() const;
 
+  // Updates the position of the Sequence in |sequence_and_transaction| in
+  // |shared_priority_queue| based on the Sequence's current traits.
+  void UpdateSortKey(SequenceAndTransaction sequence_and_transaction);
+
+  // Removes |sequence| from |priority_queue_|. Returns true if successful, or
+  // false if |sequence| is not currently in |priority_queue_|, such as when a
+  // worker is running a task from it.
+  bool RemoveSequence(scoped_refptr<Sequence> sequence);
+
   // Prevents new tasks from starting to run and waits for currently running
   // tasks to complete their execution. It is guaranteed that no thread will do
   // work on behalf of this SchedulerWorkerPool after this returns. It is
@@ -74,6 +83,15 @@
   virtual void OnCanScheduleSequence(
       SequenceAndTransaction sequence_and_transaction) = 0;
 
+  // Returns the maximum number of non-blocked tasks that can run concurrently
+  // in this pool.
+  //
+  // TODO(fdoray): Remove this method. https://crbug.com/687264
+  virtual size_t GetMaxConcurrentNonBlockedTasksDeprecated() const = 0;
+
+  // Reports relevant metrics per implementation.
+  virtual void ReportHeartbeatMetrics() const = 0;
+
  protected:
   SchedulerWorkerPool(TrackedRef<TaskTracker> task_tracker,
                       TrackedRef<Delegate> delegate);
diff --git a/base/task/task_scheduler/scheduler_worker_pool_impl.cc b/base/task/task_scheduler/scheduler_worker_pool_impl.cc
index 619a1f8..3aaba18 100644
--- a/base/task/task_scheduler/scheduler_worker_pool_impl.cc
+++ b/base/task/task_scheduler/scheduler_worker_pool_impl.cc
@@ -535,20 +535,6 @@
                                      idle_workers_stack_.Size());
 }
 
-void SchedulerWorkerPoolImpl::UpdateSortKey(
-    SequenceAndTransaction sequence_and_transaction) {
-  // TODO(fdoray): A worker should be woken up when the priority of a
-  // BEST_EFFORT task is increased and |num_running_best_effort_tasks_| is
-  // equal to |max_best_effort_tasks_|.
-  AutoSchedulerLock auto_lock(lock_);
-  priority_queue_.UpdateSortKey(std::move(sequence_and_transaction));
-}
-
-bool SchedulerWorkerPoolImpl::RemoveSequence(scoped_refptr<Sequence> sequence) {
-  AutoSchedulerLock auto_lock(lock_);
-  return priority_queue_.RemoveSequence(std::move(sequence));
-}
-
 SchedulerWorkerPoolImpl::SchedulerWorkerDelegateImpl::
     SchedulerWorkerDelegateImpl(TrackedRef<SchedulerWorkerPoolImpl> outer)
     : outer_(std::move(outer)) {
diff --git a/base/task/task_scheduler/scheduler_worker_pool_impl.h b/base/task/task_scheduler/scheduler_worker_pool_impl.h
index b181ac4..181d0ca 100644
--- a/base/task/task_scheduler/scheduler_worker_pool_impl.h
+++ b/base/task/task_scheduler/scheduler_worker_pool_impl.h
@@ -101,6 +101,8 @@
   void JoinForTesting() override;
   void ReEnqueueSequenceChangingPool(
       SequenceAndTransaction sequence_and_transaction) override;
+  size_t GetMaxConcurrentNonBlockedTasksDeprecated() const override;
+  void ReportHeartbeatMetrics() const override;
 
   const HistogramBase* num_tasks_before_detach_histogram() const {
     return num_tasks_before_detach_histogram_;
@@ -114,12 +116,6 @@
     return num_workers_histogram_;
   }
 
-  // Returns the maximum number of non-blocked tasks that can run concurrently
-  // in this pool.
-  //
-  // TODO(fdoray): Remove this method. https://crbug.com/687264
-  size_t GetMaxConcurrentNonBlockedTasksDeprecated() const;
-
   // Waits until at least |n| workers are idle. Note that while workers are
   // disallowed from cleaning up during this call: tests using a custom
   // |suggested_reclaim_time_| need to be careful to invoke this swiftly after
@@ -148,18 +144,6 @@
   // Returns the number of workers that are idle (i.e. not running tasks).
   size_t NumberOfIdleWorkersForTesting() const;
 
-  // Records number of worker and active workers.
-  void ReportHeartbeatMetrics() const;
-
-  // Updates the position of the Sequence in |sequence_and_transaction| in
-  // |shared_priority_queue| based on the Sequence's current traits.
-  void UpdateSortKey(SequenceAndTransaction sequence_and_transaction);
-
-  // Removes |sequence| from |priority_queue_|. Returns true if successful, or
-  // false if |sequence| is not currently in |priority_queue_|, such as when a
-  // worker is running a task from it.
-  bool RemoveSequence(scoped_refptr<Sequence> sequence);
-
  private:
   class SchedulerWorkerActionExecutor;
   class SchedulerWorkerDelegateImpl;
diff --git a/base/task/task_scheduler/task_scheduler_impl.cc b/base/task/task_scheduler/task_scheduler_impl.cc
index 8b8f3c0b..292c05b 100644
--- a/base/task/task_scheduler/task_scheduler_impl.cc
+++ b/base/task/task_scheduler/task_scheduler_impl.cc
@@ -208,7 +208,7 @@
   // This method does not support getting the maximum number of BEST_EFFORT
   // tasks that can run concurrently in a pool.
   DCHECK_NE(traits.priority(), TaskPriority::BEST_EFFORT);
-  return GetWorkerPoolImplForTraits(traits)
+  return GetWorkerPoolForTraits(traits)
       ->GetMaxConcurrentNonBlockedTasksDeprecated();
 }
 
@@ -284,7 +284,7 @@
 
 bool TaskSchedulerImpl::IsRunningPoolWithTraits(
     const TaskTraits& traits) const {
-  return GetWorkerPoolImplForTraits(traits)->IsBoundToCurrentThread();
+  return GetWorkerPoolForTraits(traits)->IsBoundToCurrentThread();
 }
 
 void TaskSchedulerImpl::UpdatePriority(scoped_refptr<Sequence> sequence,
@@ -292,11 +292,11 @@
   auto sequence_and_transaction =
       SequenceAndTransaction::FromSequence(std::move(sequence));
 
-  SchedulerWorkerPoolImpl* const current_worker_pool =
-      GetWorkerPoolImplForTraits(sequence_and_transaction.transaction.traits());
+  SchedulerWorkerPool* const current_worker_pool =
+      GetWorkerPoolForTraits(sequence_and_transaction.transaction.traits());
   sequence_and_transaction.transaction.UpdatePriority(priority);
-  SchedulerWorkerPoolImpl* const new_worker_pool =
-      GetWorkerPoolImplForTraits(sequence_and_transaction.transaction.traits());
+  SchedulerWorkerPool* const new_worker_pool =
+      GetWorkerPoolForTraits(sequence_and_transaction.transaction.traits());
 
   if (new_worker_pool == current_worker_pool) {
     // |sequence|'s position needs to be updated within its current pool.
@@ -314,7 +314,12 @@
   }
 }
 
-SchedulerWorkerPoolImpl* TaskSchedulerImpl::GetWorkerPoolImplForTraits(
+const SchedulerWorkerPool* TaskSchedulerImpl::GetWorkerPoolForTraits(
+    const TaskTraits& traits) const {
+  return const_cast<TaskSchedulerImpl*>(this)->GetWorkerPoolForTraits(traits);
+}
+
+SchedulerWorkerPool* TaskSchedulerImpl::GetWorkerPoolForTraits(
     const TaskTraits& traits) {
   if (traits.priority() == TaskPriority::BEST_EFFORT &&
       background_pool_.has_value()) {
@@ -323,11 +328,6 @@
   return &foreground_pool_.value();
 }
 
-SchedulerWorkerPool* TaskSchedulerImpl::GetWorkerPoolForTraits(
-    const TaskTraits& traits) {
-  return GetWorkerPoolImplForTraits(traits);
-}
-
 TaskTraits TaskSchedulerImpl::SetUserBlockingPriorityIfNeeded(
     const TaskTraits& traits) const {
   return all_tasks_user_blocking_.IsSet()
diff --git a/base/task/task_scheduler/task_scheduler_impl.h b/base/task/task_scheduler/task_scheduler_impl.h
index 63d85dba..528101d 100644
--- a/base/task/task_scheduler/task_scheduler_impl.h
+++ b/base/task/task_scheduler/task_scheduler_impl.h
@@ -97,23 +97,15 @@
       const TaskTraits& traits);
 
  private:
-  // Returns the worker pool that runs Tasks with |traits|.
-  // TODO(fdoray): Move all methods used by TaskSchedulerImpl to the
-  // SchedulerWorkerPool interface and remove the SchedulerWorkerPool*Impl*
-  // accessors.
-  SchedulerWorkerPoolImpl* GetWorkerPoolImplForTraits(const TaskTraits& traits);
-  const SchedulerWorkerPoolImpl* GetWorkerPoolImplForTraits(
-      const TaskTraits& traits) const {
-    return const_cast<TaskSchedulerImpl*>(this)->GetWorkerPoolImplForTraits(
-        traits);
-  }
-
   // Returns |traits|, with priority set to TaskPriority::USER_BLOCKING if
   // |all_tasks_user_blocking_| is set.
   TaskTraits SetUserBlockingPriorityIfNeeded(const TaskTraits& traits) const;
 
   void ReportHeartbeatMetrics() const;
 
+  const SchedulerWorkerPool* GetWorkerPoolForTraits(
+      const TaskTraits& traits) const;
+
   // SchedulerWorkerPool::Delegate:
   SchedulerWorkerPool* GetWorkerPoolForTraits(
       const TaskTraits& traits) override;
diff --git a/base/threading/thread_restrictions.h b/base/threading/thread_restrictions.h
index c2f767d..a8748af 100644
--- a/base/threading/thread_restrictions.h
+++ b/base/threading/thread_restrictions.h
@@ -148,6 +148,7 @@
 class SynchronousCompositorHost;
 class SynchronousCompositorSyncCallBridge;
 class TextInputClientMac;
+class WebContentsViewMac;
 }  // namespace content
 namespace cronet {
 class CronetPrefsManager;
@@ -319,6 +320,7 @@
   friend class android_webview::ScopedAllowInitGLBindings;
   friend class content::BrowserProcessSubThread;
   friend class content::GpuProcessTransportFactory;
+  friend class content::WebContentsViewMac;
   friend class cronet::CronetPrefsManager;
   friend class cronet::CronetURLRequestContext;
   friend class mojo::CoreLibraryInitializer;
diff --git a/build/android/pylib/local/machine/local_machine_junit_test_run.py b/build/android/pylib/local/machine/local_machine_junit_test_run.py
index 18c6678..411995fe8 100644
--- a/build/android/pylib/local/machine/local_machine_junit_test_run.py
+++ b/build/android/pylib/local/machine/local_machine_junit_test_run.py
@@ -39,17 +39,17 @@
       # extract zipfiles when they change.
       def extract_resource_zip(resource_zip):
         def helper():
-          extract_dest = os.path.join(
-              temp_dir, os.path.splitext(os.path.basename(resource_zip))[0])
+          # Flatten name to avoid needing to create directories.
+          extract_dest = os.path.join(temp_dir,
+                                      resource_zip.replace(os.path.sep, '_'))
           with zipfile.ZipFile(resource_zip, 'r') as zf:
             zf.extractall(extract_dest)
           return extract_dest
         return helper
 
       resource_dirs = reraiser_thread.RunAsync(
-          [extract_resource_zip(resource_zip)
-           for resource_zip in self._test_instance.resource_zips
-           if os.path.exists(resource_zip)])
+          extract_resource_zip(resource_zip)
+          for resource_zip in self._test_instance.resource_zips)
 
       java_script = os.path.join(
           constants.GetOutDirectory(), 'bin', 'helper',
@@ -99,19 +99,19 @@
         elif not os.path.isdir(self._test_instance.coverage_dir):
           raise Exception('--coverage-dir takes a directory, not file path.')
         if self._test_instance.jacoco:
-            jacoco_coverage_file = os.path.join(
-                self._test_instance.coverage_dir,
-                '%s.exec' % self._test_instance.suite)
-            jacoco_agent_path = os.path.join(host_paths.DIR_SOURCE_ROOT,
-                                             'third_party', 'jacoco',
-                                             'lib', 'jacocoagent.jar')
-            jacoco_args = '-javaagent:{}=destfile={},includes=org.chromium.*'
-            jvm_args.append(jacoco_args.format(jacoco_agent_path,
-                                                 jacoco_coverage_file))
+          jacoco_coverage_file = os.path.join(
+              self._test_instance.coverage_dir,
+              '%s.exec' % self._test_instance.suite)
+          jacoco_agent_path = os.path.join(host_paths.DIR_SOURCE_ROOT,
+                                           'third_party', 'jacoco', 'lib',
+                                           'jacocoagent.jar')
+          jacoco_args = '-javaagent:{}=destfile={},includes=org.chromium.*'
+          jvm_args.append(
+              jacoco_args.format(jacoco_agent_path, jacoco_coverage_file))
         else:
-            jvm_args.append('-Demma.coverage.out.file=%s' % os.path.join(
-                            self._test_instance.coverage_dir,
-                            '%s.ec' % self._test_instance.suite))
+          jvm_args.append('-Demma.coverage.out.file=%s' % os.path.join(
+              self._test_instance.coverage_dir,
+              '%s.ec' % self._test_instance.suite))
 
       if jvm_args:
         command.extend(['--jvm-args', '"%s"' % ' '.join(jvm_args)])
diff --git a/build/android/stacktrace/crashpad_stackwalker.py b/build/android/stacktrace/crashpad_stackwalker.py
index fb20ccb..eedb7e55 100755
--- a/build/android/stacktrace/crashpad_stackwalker.py
+++ b/build/android/stacktrace/crashpad_stackwalker.py
@@ -52,6 +52,10 @@
 
 
 def _ChooseLatestCrashpadDump(device, crashpad_dump_path):
+  if not device.PathExists(crashpad_dump_path):
+    logging.warning('Crashpad dump directory does not exist: %s',
+                    crashpad_dump_path)
+    return None
   latest = None
   latest_timestamp = 0
   for crashpad_file in device.ListDirectory(crashpad_dump_path):
diff --git a/build/fuchsia/linux.sdk.sha1 b/build/fuchsia/linux.sdk.sha1
index 5b801255..135cdf3 100644
--- a/build/fuchsia/linux.sdk.sha1
+++ b/build/fuchsia/linux.sdk.sha1
@@ -1 +1 @@
-a510a7616b644b63b0bb02ad5733223265d35476
\ No newline at end of file
+882938bbc85eb6781490026cad45e29448c26cf1
\ No newline at end of file
diff --git a/build/fuchsia/mac.sdk.sha1 b/build/fuchsia/mac.sdk.sha1
index 9e2f989..d45cd81 100644
--- a/build/fuchsia/mac.sdk.sha1
+++ b/build/fuchsia/mac.sdk.sha1
@@ -1 +1 @@
-cc4b8c0ec9983680bf9a75f24c7c47807c85464d
\ No newline at end of file
+1a7a29783cf061f4b571e4ebc8f56a7dc1a82935
\ No newline at end of file
diff --git a/build/toolchain/clang_code_coverage_wrapper.py b/build/toolchain/clang_code_coverage_wrapper.py
index eb493bfa..96978056 100755
--- a/build/toolchain/clang_code_coverage_wrapper.py
+++ b/build/toolchain/clang_code_coverage_wrapper.py
@@ -2,11 +2,19 @@
 # Copyright 2018 The Chromium Authors. All rights reserved.
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
-"""Adds code coverage flags to the invocations of the Clang C/C++ compiler.
+"""Removes code coverage flags from invocations of the Clang C/C++ compiler.
 
-This script is used to instrument a subset of the source files, and the list of
-files to instrument is specified by an input file that is passed to this script
-as a command-line argument.
+If the GN arg `use_clang_coverage=true`, this script will be invoked by default.
+GN will add coverage instrumentation flags to almost all source files.
+
+This script is used to remove instrumentation flags from a subset of the source
+files. By default, it will not remove flags from any files. If the option
+--files-to-instrument is passed, this script will remove flags from all files
+except the ones listed in --files-to-instrument.
+
+This script also contains hard-coded exclusion lists of files to never
+instrument, indexed by target operating system. Files in these lists have their
+flags removed in both modes. The OS can be selected with --target-os.
 
 The path to the coverage instrumentation input file should be relative to the
 root build directory, and the file consists of multiple lines where each line
@@ -37,6 +45,8 @@
 import sys
 
 # Flags used to enable coverage instrumentation.
+# Flags should be listed in the same order that they are added in
+# build/config/coverage/BUILD.gn
 _COVERAGE_FLAGS = [
     '-fprofile-instr-generate', '-fcoverage-mapping',
     # Following experimental flags remove unused header functions from the
@@ -46,6 +56,41 @@
     '-mllvm', '-limited-coverage-experimental=true'
 ]
 
+# Map of exclusion lists indexed by target OS.
+# If no target OS is defined, or one is defined that doesn't have a specific
+# entry, use the 'default' exclusion_list. Anything added to 'default' will
+# apply to all platforms that don't have their own specific list.
+_COVERAGE_EXCLUSION_LIST_MAP = {
+    'default': [],
+    'chromeos': [
+        # These files caused clang to crash while compiling them. They are
+        # excluded pending an investigation into the underlying compiler bug.
+        '../../third_party/webrtc/p2p/base/p2p_transport_channel.cc',
+        '../../third_party/icu/source/common/uts46.cpp',
+        '../../third_party/icu/source/common/ucnvmbcs.cpp',
+        '../../base/android/android_image_reader_compat.cc',
+    ]
+}
+
+
+def _remove_flags_from_command(command):
+  # We need to remove the coverage flags for this file, but we only want to
+  # remove them if we see the exact sequence defined in _COVERAGE_FLAGS.
+  # That ensures that we only remove the flags added by GN when
+  # "use_clang_coverage" is true. Otherwise, we would remove flags set by
+  # other parts of the build system.
+  start_flag = _COVERAGE_FLAGS[0]
+  num_flags = len(_COVERAGE_FLAGS)
+  start_idx = 0
+  try:
+    while True:
+      idx = command.index(start_flag, start_idx)
+      start_idx = idx + 1
+      if command[idx:idx+num_flags] == _COVERAGE_FLAGS:
+        del command[idx:idx+num_flags]
+        break
+  except ValueError:
+    pass
 
 def main():
   # TODO(crbug.com/898695): Make this wrapper work on Windows platform.
@@ -54,16 +99,23 @@
   arg_parser.add_argument(
       '--files-to-instrument',
       type=str,
-      required=True,
       help='Path to a file that contains a list of file names to instrument.')
+  arg_parser.add_argument(
+      '--target-os',
+      required=False,
+      help='The OS to compile for.')
   arg_parser.add_argument('args', nargs=argparse.REMAINDER)
   parsed_args = arg_parser.parse_args()
 
-  if not os.path.isfile(parsed_args.files_to_instrument):
+  if (parsed_args.files_to_instrument and
+      not os.path.isfile(parsed_args.files_to_instrument)):
     raise Exception('Path to the coverage instrumentation file: "%s" doesn\'t '
                     'exist.' % parsed_args.files_to_instrument)
 
   compile_command = parsed_args.args
+  if not any('clang' in s for s in compile_command):
+    return subprocess.call(compile_command)
+
   try:
     # The command is assumed to use Clang as the compiler, and the path to the
     # source file is behind the -c argument, and the path to the source path is
@@ -79,12 +131,19 @@
     raise Exception('Source file to be compiled is missing from the command.')
 
   compile_source_file = compile_command[index_dash_c + 1]
-  with open(parsed_args.files_to_instrument) as f:
-    if compile_source_file + '\n' in f.read():
-      compile_command.extend(_COVERAGE_FLAGS)
+  target_os = parsed_args.target_os
+  if target_os not in _COVERAGE_EXCLUSION_LIST_MAP:
+    target_os = 'default'
+  exclusion_list = _COVERAGE_EXCLUSION_LIST_MAP[target_os]
+
+  if compile_source_file in exclusion_list:
+    _remove_flags_from_command(compile_command)
+  elif parsed_args.files_to_instrument:
+    with open(parsed_args.files_to_instrument) as f:
+      if compile_source_file not in f.read():
+        _remove_flags_from_command(compile_command)
 
   return subprocess.call(compile_command)
 
-
 if __name__ == '__main__':
   sys.exit(main())
diff --git a/build/toolchain/mac/BUILD.gn b/build/toolchain/mac/BUILD.gn
index ea63b4d7..5f51473 100644
--- a/build/toolchain/mac/BUILD.gn
+++ b/build/toolchain/mac/BUILD.gn
@@ -186,6 +186,12 @@
     # If dSYMs are enabled, this flag will be added to the link tools.
     if (_enable_dsyms) {
       dsym_switch = " -Wcrl,dsym,{{root_out_dir}} "
+      if (is_mac) {
+        dsym_switch += "-Wcrl,dsymutilpath," +
+                          rebase_path("//tools/clang/dsymutil/bin/dsymutil",
+                                      root_build_dir) + " "
+      }
+
       dsym_output_dir =
           "{{root_out_dir}}/{{target_output_name}}{{output_extension}}.dSYM"
       dsym_output = [
diff --git a/build/toolchain/mac/linker_driver.py b/build/toolchain/mac/linker_driver.py
index 35de9d1..10bbda02 100755
--- a/build/toolchain/mac/linker_driver.py
+++ b/build/toolchain/mac/linker_driver.py
@@ -10,6 +10,8 @@
 import subprocess
 import sys
 
+DSYMUTIL_INVOKE = ['xcrun', 'dsymutil']
+
 # The linker_driver.py is responsible for forwarding a linker invocation to
 # the compiler driver, while processing special arguments itself.
 #
@@ -31,6 +33,10 @@
 #         "... -o out/gn/obj/foo/libbar.dylib ... -Wcrl,dsym,out/gn ..."
 #       The resulting dSYM would be out/gn/libbar.dylib.dSYM/.
 #
+#   -Wcrl,dsymutilpath,<dsymutil_path>
+#       Sets the path to the dsymutil to run with -Wcrl,dsym, in which case
+#       `xcrun` is not used to invoke it.
+#
 #   -Wcrl,unstripped,<unstripped_path_prefix>
 #       After invoking the linker, and before strip, this will save a copy of
 #       the unstripped linker output in the directory unstripped_path_prefix.
@@ -142,10 +148,29 @@
 
   # Remove old dSYMs before invoking dsymutil.
   _RemovePath(dsym_out)
-  subprocess.check_call(['xcrun', 'dsymutil', '-o', dsym_out, linker_out])
+  subprocess.check_call(DSYMUTIL_INVOKE + ['-o', dsym_out, linker_out])
   return [dsym_out]
 
 
+def SetDsymutilPath(dsymutil_path, full_args):
+  """Linker driver action for -Wcrl,dsymutilpath,<dsymutil_path>.
+
+  Sets the invocation command for dsymutil, which allows the caller to specify
+  an alternate dsymutil. This action is always processed before the RunDsymUtil
+  action.
+
+  Args:
+    dsymutil_path: string, The path to the dsymutil binary to run
+    full_args: list of string, Full argument list for the linker driver.
+
+  Returns:
+    No output - this step is run purely for its side-effect.
+  """
+  global DSYMUTIL_INVOKE
+  DSYMUTIL_INVOKE = [dsymutil_path]
+  return []
+
+
 def RunSaveUnstripped(unstripped_path_prefix, full_args):
   """Linker driver action for -Wcrl,unstripped,<unstripped_path_prefix>. Copies
   the linker output to |unstripped_path_prefix| before stripping.
@@ -219,6 +244,7 @@
 argument's -Wcrl,<sub_argument> and the second is the function to invoke.
 """
 _LINKER_DRIVER_ACTIONS = [
+    ('dsymutilpath,', SetDsymutilPath),
     ('dsym,', RunDsymUtil),
     ('unstripped,', RunSaveUnstripped),
     ('strip,', RunStrip),
diff --git a/cc/layers/heads_up_display_layer_impl.cc b/cc/layers/heads_up_display_layer_impl.cc
index c9c587d..6ede1c05 100644
--- a/cc/layers/heads_up_display_layer_impl.cc
+++ b/cc/layers/heads_up_display_layer_impl.cc
@@ -279,13 +279,13 @@
               ->settings()
               .resource_settings.use_gpu_memory_buffer_resources);
 
-      uint32_t flags = 0;
+      uint32_t flags = gpu::SHARED_IMAGE_USAGE_DISPLAY;
       if (use_oopr) {
-        flags = gpu::SHARED_IMAGE_USAGE_RASTER |
-                gpu::SHARED_IMAGE_USAGE_OOP_RASTERIZATION;
+        flags |= gpu::SHARED_IMAGE_USAGE_RASTER |
+                 gpu::SHARED_IMAGE_USAGE_OOP_RASTERIZATION;
       } else if (gpu_raster) {
-        flags = gpu::SHARED_IMAGE_USAGE_GLES2 |
-                gpu::SHARED_IMAGE_USAGE_GLES2_FRAMEBUFFER_HINT;
+        flags |= gpu::SHARED_IMAGE_USAGE_GLES2 |
+                 gpu::SHARED_IMAGE_USAGE_GLES2_FRAMEBUFFER_HINT;
       }
       if (backing->overlay_candidate)
         flags |= gpu::SHARED_IMAGE_USAGE_SCANOUT;
diff --git a/cc/raster/gpu_raster_buffer_provider.cc b/cc/raster/gpu_raster_buffer_provider.cc
index 2053c56d..23159f6 100644
--- a/cc/raster/gpu_raster_buffer_provider.cc
+++ b/cc/raster/gpu_raster_buffer_provider.cc
@@ -132,7 +132,8 @@
   if (mailbox->IsZero()) {
     DCHECK(!sync_token.HasData());
     auto* sii = context_provider->SharedImageInterface();
-    uint32_t flags = gpu::SHARED_IMAGE_USAGE_RASTER |
+    uint32_t flags = gpu::SHARED_IMAGE_USAGE_DISPLAY |
+                     gpu::SHARED_IMAGE_USAGE_RASTER |
                      gpu::SHARED_IMAGE_USAGE_OOP_RASTERIZATION;
     if (texture_is_overlay_candidate)
       flags |= gpu::SHARED_IMAGE_USAGE_SCANOUT;
@@ -187,7 +188,8 @@
   gpu::raster::RasterInterface* ri = context_provider->RasterInterface();
   if (mailbox->IsZero()) {
     auto* sii = context_provider->SharedImageInterface();
-    uint32_t flags = gpu::SHARED_IMAGE_USAGE_GLES2 |
+    uint32_t flags = gpu::SHARED_IMAGE_USAGE_DISPLAY |
+                     gpu::SHARED_IMAGE_USAGE_GLES2 |
                      gpu::SHARED_IMAGE_USAGE_GLES2_FRAMEBUFFER_HINT;
     if (texture_is_overlay_candidate)
       flags |= gpu::SHARED_IMAGE_USAGE_SCANOUT;
diff --git a/cc/raster/one_copy_raster_buffer_provider.cc b/cc/raster/one_copy_raster_buffer_provider.cc
index f134a48b..f16e9083 100644
--- a/cc/raster/one_copy_raster_buffer_provider.cc
+++ b/cc/raster/one_copy_raster_buffer_provider.cc
@@ -382,7 +382,8 @@
   }
 
   if (mailbox->IsZero()) {
-    uint32_t flags = gpu::SHARED_IMAGE_USAGE_RASTER;
+    uint32_t flags =
+        gpu::SHARED_IMAGE_USAGE_DISPLAY | gpu::SHARED_IMAGE_USAGE_RASTER;
     if (mailbox_texture_is_overlay_candidate)
       flags |= gpu::SHARED_IMAGE_USAGE_SCANOUT;
     *mailbox = sii->CreateSharedImage(resource_format, resource_size,
@@ -391,9 +392,11 @@
 
   // Create staging shared image.
   if (staging_buffer->mailbox.IsZero()) {
-    staging_buffer->mailbox = sii->CreateSharedImage(
-        staging_buffer->gpu_memory_buffer.get(), gpu_memory_buffer_manager_,
-        color_space, gpu::SHARED_IMAGE_USAGE_RASTER);
+    uint32_t flags =
+        gpu::SHARED_IMAGE_USAGE_DISPLAY | gpu::SHARED_IMAGE_USAGE_RASTER;
+    staging_buffer->mailbox =
+        sii->CreateSharedImage(staging_buffer->gpu_memory_buffer.get(),
+                               gpu_memory_buffer_manager_, color_space, flags);
   } else {
     sii->UpdateSharedImage(staging_buffer->sync_token, staging_buffer->mailbox);
   }
diff --git a/cc/test/fake_proxy.h b/cc/test/fake_proxy.h
index 2ebebfba2..ced533d 100644
--- a/cc/test/fake_proxy.h
+++ b/cc/test/fake_proxy.h
@@ -33,7 +33,8 @@
   bool RequestedAnimatePending() override;
   void NotifyInputThrottledUntilCommit() override {}
   void SetDeferMainFrameUpdate(bool defer_main_frame_update) override {}
-  void SetDeferCommits(bool defer_commits) override {}
+  void StartDeferringCommits(base::TimeDelta timeout) override {}
+  void StopDeferringCommits() override {}
   bool CommitRequested() const override;
   void Start() override {}
   void Stop() override {}
diff --git a/cc/test/pixel_test.cc b/cc/test/pixel_test.cc
index 9916553..9a51bec 100644
--- a/cc/test/pixel_test.cc
+++ b/cc/test/pixel_test.cc
@@ -286,7 +286,8 @@
   gpu_service_->InitializeWithHost(
       std::move(gpu_host_proxy), gpu::GpuProcessActivityFlags(),
       gl::init::CreateOffscreenGLSurface(gfx::Size()),
-      nullptr /* sync_point_manager */, nullptr /* shutdown_event */);
+      nullptr /* sync_point_manager */, nullptr /* shared_image_manager */,
+      nullptr /* shutdown_event */);
   task_executor_ = base::MakeRefCounted<gpu::GpuInProcessThreadService>(
       gpu_thread_->task_runner(), gpu_service_->scheduler(),
       gpu_service_->sync_point_manager(), gpu_service_->mailbox_manager(),
diff --git a/cc/trees/layer_tree_host.cc b/cc/trees/layer_tree_host.cc
index 1113f891..a220cf1 100644
--- a/cc/trees/layer_tree_host.cc
+++ b/cc/trees/layer_tree_host.cc
@@ -547,12 +547,12 @@
   return std::make_unique<ScopedDeferMainFrameUpdate>(this);
 }
 
-void LayerTreeHost::StartDeferringCommits() {
-  proxy_->SetDeferCommits(true);
+void LayerTreeHost::StartDeferringCommits(base::TimeDelta timeout) {
+  proxy_->StartDeferringCommits(timeout);
 }
 
 void LayerTreeHost::StopDeferringCommits() {
-  proxy_->SetDeferCommits(false);
+  proxy_->StopDeferringCommits();
 }
 
 DISABLE_CFI_PERF
diff --git a/cc/trees/layer_tree_host.h b/cc/trees/layer_tree_host.h
index b7713f32..ec6298d 100644
--- a/cc/trees/layer_tree_host.h
+++ b/cc/trees/layer_tree_host.h
@@ -238,8 +238,13 @@
   std::unique_ptr<ScopedDeferMainFrameUpdate> DeferMainFrameUpdate();
 
   // Prevents the proxy from committing the layer tree to the compositor,
-  // while still allowing main frame lifecycle updates.
-  void StartDeferringCommits();
+  // while still allowing main frame lifecycle updates. |timeout|
+  // is the interval after which commits will restart if nothing stops
+  // deferring sooner. If multiple calls are made to StartDeferringCommits
+  // while deferal is active, the first timeout continues to apply.
+  void StartDeferringCommits(base::TimeDelta timeout);
+
+  // Stop deferring commits immediately.
   void StopDeferringCommits();
 
   // Returns whether there are any outstanding ScopedDeferMainFrameUpdate,
diff --git a/cc/trees/proxy.h b/cc/trees/proxy.h
index b9474b2..305d3c6 100644
--- a/cc/trees/proxy.h
+++ b/cc/trees/proxy.h
@@ -66,10 +66,14 @@
   // reset. It is only supported when using a scheduler.
   virtual void SetDeferMainFrameUpdate(bool defer_main_frame_update) = 0;
 
-  // Defers commits until reset, but continues to update the document
-  // lifecycle in LayerTreeHost::BeginMainFrameUpdate. It is only supported
-  // when using a scheduler.
-  virtual void SetDeferCommits(bool defer_commits) = 0;
+  // Defers commits until at most the given |timeout| period has passed,
+  // but continues to update the document lifecycle in
+  // LayerTreeHost::BeginMainFrameUpdate. If multiple calls are made when
+  // deferal is active the first |timeout| continues to apply.
+  virtual void StartDeferringCommits(base::TimeDelta timeout) = 0;
+
+  // Immediately stop deferring commits.
+  virtual void StopDeferringCommits() = 0;
 
   virtual bool CommitRequested() const = 0;
 
diff --git a/cc/trees/proxy_main.cc b/cc/trees/proxy_main.cc
index 7218721..c859f043 100644
--- a/cc/trees/proxy_main.cc
+++ b/cc/trees/proxy_main.cc
@@ -230,6 +230,11 @@
   // what this does.
   layer_tree_host_->RequestMainFrameUpdate();
 
+  // Check now if we should stop deferring commits
+  if (defer_commits_ && base::TimeTicks::Now() > commits_restart_time_) {
+    StopDeferringCommits();
+  }
+
   // At this point the main frame may have deferred commits to avoid committing
   // right now, or we may be deferring commits but not deferring main
   // frame updates.
@@ -463,16 +468,26 @@
                                 defer_main_frame_update));
 }
 
-void ProxyMain::SetDeferCommits(bool defer_commits) {
-  DCHECK(IsMainThread());
-  if (defer_commits_ == defer_commits)
+void ProxyMain::StartDeferringCommits(base::TimeDelta timeout) {
+  DCHECK(task_runner_provider_->IsMainThread());
+
+  // Do nothing if already deferring. The timeout remains as it was from when
+  // we most recently began deferring.
+  if (defer_commits_)
     return;
 
-  defer_commits_ = defer_commits;
-  if (defer_commits_)
-    TRACE_EVENT_ASYNC_BEGIN0("cc", "ProxyMain::SetDeferCommits", this);
-  else
-    TRACE_EVENT_ASYNC_END0("cc", "ProxyMain::SetDeferCommits", this);
+  TRACE_EVENT_ASYNC_BEGIN0("cc", "ProxyMain::SetDeferCommits", this);
+
+  defer_commits_ = true;
+  commits_restart_time_ = base::TimeTicks::Now() + timeout;
+}
+
+void ProxyMain::StopDeferringCommits() {
+  if (!defer_commits_)
+    return;
+  defer_commits_ = false;
+  commits_restart_time_ = base::TimeTicks();
+  TRACE_EVENT_ASYNC_END0("cc", "ProxyMain::SetDeferCommits", this);
 }
 
 bool ProxyMain::CommitRequested() const {
diff --git a/cc/trees/proxy_main.h b/cc/trees/proxy_main.h
index 6e28fcb1..468bad8 100644
--- a/cc/trees/proxy_main.h
+++ b/cc/trees/proxy_main.h
@@ -89,7 +89,8 @@
   bool RequestedAnimatePending() override;
   void NotifyInputThrottledUntilCommit() override;
   void SetDeferMainFrameUpdate(bool defer_main_frame_update) override;
-  void SetDeferCommits(bool defer_commits) override;
+  void StartDeferringCommits(base::TimeDelta timeout) override;
+  void StopDeferringCommits() override;
   bool CommitRequested() const override;
   void Start() override;
   void Stop() override;
@@ -150,6 +151,9 @@
   bool defer_main_frame_update_;
   bool defer_commits_;
 
+  // Only used when defer_commits_ is active and must be set in such cases.
+  base::TimeTicks commits_restart_time_;
+
   // ProxyImpl is created and destroyed on the impl thread, and should only be
   // accessed on the impl thread.
   // It is safe to use base::Unretained to post tasks to ProxyImpl on the impl
diff --git a/cc/trees/single_thread_proxy.cc b/cc/trees/single_thread_proxy.cc
index 21e0bd8d..84f7627 100644
--- a/cc/trees/single_thread_proxy.cc
+++ b/cc/trees/single_thread_proxy.cc
@@ -286,18 +286,26 @@
   scheduler_on_impl_thread_->SetDeferBeginMainFrame(defer_main_frame_update_);
 }
 
-void SingleThreadProxy::SetDeferCommits(bool defer_commits) {
+void SingleThreadProxy::StartDeferringCommits(base::TimeDelta timeout) {
   DCHECK(task_runner_provider_->IsMainThread());
 
-  if (defer_commits_ == defer_commits)
+  // Do nothing if already deferring. The timeout remains as it was from when
+  // we most recently began deferring.
+  if (defer_commits_)
     return;
 
-  if (defer_commits)
-    TRACE_EVENT_ASYNC_BEGIN0("cc", "SingleThreadProxy::SetDeferCommits", this);
-  else
-    TRACE_EVENT_ASYNC_END0("cc", "SingleThreadProxy::SetDeferCommits", this);
+  TRACE_EVENT_ASYNC_BEGIN0("cc", "SingleThreadProxy::SetDeferCommits", this);
 
-  defer_commits_ = defer_commits;
+  defer_commits_ = true;
+  commits_restart_time_ = base::TimeTicks::Now() + timeout;
+}
+
+void SingleThreadProxy::StopDeferringCommits() {
+  if (!defer_commits_)
+    return;
+  defer_commits_ = false;
+  commits_restart_time_ = base::TimeTicks();
+  TRACE_EVENT_ASYNC_END0("cc", "SingleThreadProxy::SetDeferCommits", this);
 }
 
 bool SingleThreadProxy::CommitRequested() const {
@@ -803,6 +811,11 @@
   // New commits requested inside UpdateLayers should be respected.
   commit_requested_ = false;
 
+  // Check now if we should stop deferring commits
+  if (defer_commits_ && base::TimeTicks::Now() > commits_restart_time_) {
+    StopDeferringCommits();
+  }
+
   // At this point the main frame may have deferred commits to avoid committing
   // right now.
   if (defer_main_frame_update_ || defer_commits_ ||
diff --git a/cc/trees/single_thread_proxy.h b/cc/trees/single_thread_proxy.h
index 77a9384..107f91b2 100644
--- a/cc/trees/single_thread_proxy.h
+++ b/cc/trees/single_thread_proxy.h
@@ -52,7 +52,8 @@
   bool RequestedAnimatePending() override;
   void NotifyInputThrottledUntilCommit() override {}
   void SetDeferMainFrameUpdate(bool defer_main_frame_update) override;
-  void SetDeferCommits(bool defer_commits) override;
+  void StartDeferringCommits(base::TimeDelta timeout) override;
+  void StopDeferringCommits() override;
   bool CommitRequested() const override;
   void Start() override;
   void Stop() override;
@@ -177,6 +178,9 @@
   // Accessed from both threads.
   std::unique_ptr<Scheduler> scheduler_on_impl_thread_;
 
+  // Only used when defer_commits_ is active and must be set in such cases.
+  base::TimeTicks commits_restart_time_;
+
   bool next_frame_is_newly_committed_frame_;
 
 #if DCHECK_IS_ON()
diff --git a/chrome/VERSION b/chrome/VERSION
index 5a532a52..1b08393 100644
--- a/chrome/VERSION
+++ b/chrome/VERSION
@@ -1,4 +1,4 @@
 MAJOR=74
 MINOR=0
-BUILD=3726
+BUILD=3727
 PATCH=0
diff --git a/chrome/android/BUILD.gn b/chrome/android/BUILD.gn
index 1122699..44569fc 100644
--- a/chrome/android/BUILD.gn
+++ b/chrome/android/BUILD.gn
@@ -270,6 +270,7 @@
     "//components/background_task_scheduler:background_task_scheduler_java",
     "//components/background_task_scheduler:background_task_scheduler_task_ids_java",
     "//components/bookmarks/common/android:bookmarks_java",
+    "//components/content_capture/android:java",
     "//components/contextual_search/content:mojo_bindings_java",
     "//components/crash/android:java",
     "//components/dom_distiller/content/browser/android:dom_distiller_content_java",
@@ -2187,6 +2188,7 @@
     "java/src/org/chromium/chrome/browser/download/DownloadInfo.java",
     "java/src/org/chromium/chrome/browser/download/DownloadItem.java",
     "java/src/org/chromium/chrome/browser/download/DownloadLocationDialogBridge.java",
+    "java/src/org/chromium/chrome/browser/download/DownloadManagerBridge.java",
     "java/src/org/chromium/chrome/browser/download/DownloadManagerService.java",
     "java/src/org/chromium/chrome/browser/download/DownloadMediaData.java",
     "java/src/org/chromium/chrome/browser/download/DownloadMediaParserBridge.java",
@@ -2344,8 +2346,10 @@
     "java/src/org/chromium/chrome/browser/suggestions/SuggestionsEventReporterBridge.java",
     "java/src/org/chromium/chrome/browser/sync/ProfileSyncService.java",
     "java/src/org/chromium/chrome/browser/tab/Tab.java",
+    "java/src/org/chromium/chrome/browser/tab/TabFavicon.java",
     "java/src/org/chromium/chrome/browser/tab/TabState.java",
     "java/src/org/chromium/chrome/browser/tab/TabWebContentsDelegateAndroid.java",
+    "java/src/org/chromium/chrome/browser/tab/TrustedCdn.java",
     "java/src/org/chromium/chrome/browser/tabmodel/SingleTabModel.java",
     "java/src/org/chromium/chrome/browser/tabmodel/TabModelJniBridge.java",
     "java/src/org/chromium/chrome/browser/tabmodel/TabModelObserverJniBridge.java",
diff --git a/chrome/android/DEPS b/chrome/android/DEPS
index 52b14e0..980bd43 100644
--- a/chrome/android/DEPS
+++ b/chrome/android/DEPS
@@ -1,4 +1,5 @@
 include_rules = [
+  "+components/content_capture",
   "+components/download",
   "+components/favicon_base",
   "+components/invalidation",
diff --git a/chrome/android/java/res/drawable/ic_site_timer.xml b/chrome/android/java/res/drawable/ic_site_timer.xml
new file mode 100644
index 0000000..d780ce66
--- /dev/null
+++ b/chrome/android/java/res/drawable/ic_site_timer.xml
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright 2019 The Chromium Authors. All rights reserved.
+     Use of this source code is governed by a BSD-style license that can be
+     found in the LICENSE file. -->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        xmlns:tools="http://schemas.android.com/tools"
+        tools:targetApi="21"
+        android:width="36dp"
+        android:height="60dp"
+        android:viewportWidth="36.0"
+        android:viewportHeight="60.0">
+    <path
+        android:pathData="M0.03,18L12,30 0,42v18h36l-0.03,-17.97L36,42 24,30l12,-12V0H0l0.03,18zM30,6v9.51L19.77,25.74 18,27.51l-1.74,-1.74L6.03,15.51 6,6h24z"
+        android:fillColor="@color/default_icon_color"/>
+</vector>
diff --git a/chrome/android/java/res/layout/bottom_tab_grid_toolbar.xml b/chrome/android/java/res/layout/bottom_tab_grid_toolbar.xml
index 6ec0733..6f5bf508 100644
--- a/chrome/android/java/res/layout/bottom_tab_grid_toolbar.xml
+++ b/chrome/android/java/res/layout/bottom_tab_grid_toolbar.xml
@@ -3,7 +3,7 @@
      Use of this source code is governed by a BSD-style license that can be
      found in the LICENSE file. -->
 
-<org.chromium.chrome.browser.tasks.tab_list_ui.BottomTabGridSheetToolbarView
+<org.chromium.chrome.browser.tasks.tab_list_ui.BottomTabToolbarView
     xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:app="http://schemas.android.com/apk/res-auto"
     xmlns:tools="http://schemas.android.com/tools"
@@ -17,11 +17,12 @@
         android:gravity="center_vertical"
         android:background="@color/modern_primary_color">
         <org.chromium.chrome.browser.widget.ListMenuButton
-            android:id="@+id/collapse"
+            android:id="@+id/toolbar_left_button"
             android:layout_width="@dimen/bottom_tab_grid_toolbar_icon_size"
             android:layout_height="match_parent"
             android:src="@drawable/ic_expand_more_black_24dp"
             app:tint="@color/standard_mode_tint"
+            android:background="?attr/selectableItemBackground"
             android:contentDescription="@string/accessibility_collapse_section_header" />
          <TextView
              android:id="@+id/title"
@@ -34,11 +35,12 @@
              android:gravity="center"
              android:focusableInTouchMode="true" />
         <org.chromium.chrome.browser.widget.ListMenuButton
-            android:id="@+id/add"
+            android:id="@+id/toolbar_right_button"
             android:layout_width="@dimen/bottom_tab_grid_toolbar_icon_size"
             android:layout_height="match_parent"
             android:src="@drawable/plus"
             app:tint="@color/standard_mode_tint"
+            android:background="?attr/selectableItemBackground"
             android:contentDescription="@string/bottom_tab_grid_new_tab" />
    </LinearLayout>
-</org.chromium.chrome.browser.tasks.tab_list_ui.BottomTabGridSheetToolbarView>
+</org.chromium.chrome.browser.tasks.tab_list_ui.BottomTabToolbarView>
diff --git a/chrome/android/java/res/layout/bottom_tab_strip_toolbar.xml b/chrome/android/java/res/layout/bottom_tab_strip_toolbar.xml
new file mode 100644
index 0000000..ff84919
--- /dev/null
+++ b/chrome/android/java/res/layout/bottom_tab_strip_toolbar.xml
@@ -0,0 +1,45 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright 2018 The Chromium Authors. All rights reserved.
+     Use of this source code is governed by a BSD-style license that can be
+     found in the LICENSE file. -->
+
+<org.chromium.chrome.browser.tasks.tab_list_ui.BottomTabToolbarView
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
+    xmlns:tools="http://schemas.android.com/tools"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content" >
+    <LinearLayout
+        android:id="@+id/main_content"
+        android:layout_width="match_parent"
+        android:layout_height="@dimen/bottom_sheet_peek_height"
+        android:orientation="horizontal"
+        android:gravity="center_vertical"
+        android:background="@color/modern_primary_color">
+        <org.chromium.chrome.browser.widget.ListMenuButton
+            android:id="@+id/toolbar_left_button"
+            android:layout_width="@dimen/bottom_tab_grid_toolbar_icon_size"
+            android:layout_height="match_parent"
+            android:src="@drawable/ic_expand_less_black_24dp"
+            app:tint="@color/standard_mode_tint"
+            android:background="?attr/selectableItemBackground"
+            android:contentDescription="@string/accessibility_expand_section_header" />
+
+        <FrameLayout
+            android:id="@+id/toolbar_container_view"
+            android:layout_width="0dp"
+            android:layout_height="match_parent"
+            android:layout_weight="1"
+            android:background="@android:color/holo_orange_dark"
+            android:orientation="horizontal" />
+
+        <org.chromium.chrome.browser.widget.ListMenuButton
+            android:id="@+id/toolbar_right_button"
+            android:layout_width="@dimen/bottom_tab_grid_toolbar_icon_size"
+            android:layout_height="match_parent"
+            android:src="@drawable/plus"
+            app:tint="@color/standard_mode_tint"
+            android:background="?attr/selectableItemBackground"
+            android:contentDescription="@string/bottom_tab_grid_new_tab" />
+   </LinearLayout>
+</org.chromium.chrome.browser.tasks.tab_list_ui.BottomTabToolbarView>
diff --git a/chrome/android/java/res/layout/search_engine.xml b/chrome/android/java/res/layout/search_engine.xml
index a43c347..a9295da 100644
--- a/chrome/android/java/res/layout/search_engine.xml
+++ b/chrome/android/java/res/layout/search_engine.xml
@@ -9,6 +9,7 @@
     android:background="?android:attr/selectableItemBackground"
     android:gravity="center_vertical"
     android:orientation="horizontal"
+    android:focusable="true"
     android:padding="6dp">
 
     <android.widget.RadioButton
diff --git a/chrome/android/java/res/layout/suspended_tab.xml b/chrome/android/java/res/layout/suspended_tab.xml
new file mode 100644
index 0000000..e613cb3
--- /dev/null
+++ b/chrome/android/java/res/layout/suspended_tab.xml
@@ -0,0 +1,65 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     Copyright 2019 The Chromium Authors. All rights reserved.
+
+     Use of this source code is governed by a BSD-style license that can be
+     found in the LICENSE file.
+-->
+<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
+        xmlns:app="http://schemas.android.com/apk/res-auto"
+        xmlns:tools="http://schemas.android.com/tools"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:fillViewport="true">
+    <LinearLayout
+        android:background="@color/modern_primary_color"
+        android:fillViewport="true"
+        android:layout_height="wrap_content"
+        android:layout_width="wrap_content"
+        android:paddingStart="24dp"
+        android:paddingEnd="24dp"
+        android:paddingTop="24dp"
+        android:paddingBottom="24dp"
+        android:orientation="vertical"
+        android:layout_gravity="center_horizontal"
+        android:gravity="start" >
+
+        <ImageView
+            android:id="@+id/suspended_tab_image"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:paddingStart="12dp"
+            android:paddingTop="40dp"
+            android:paddingBottom="40dp"
+            app:srcCompat="@drawable/ic_site_timer"
+            android:importantForAccessibility="no"
+            android:layout_gravity="start" />
+
+        <TextView
+            android:id="@+id/suspended_tab_title"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:paddingBottom="16dp"
+            android:textAppearance="@style/TextAppearance.BlackHeadline"
+            android:layout_gravity="start"
+            android:text="@string/usage_stats_site_paused" />
+
+        <TextView
+            android:id="@+id/suspended_tab_explanation"
+            android:layout_width="wrap_content"
+            android:layout_height="0dp"
+            android:layout_weight="1"
+            android:paddingBottom="16dp"
+            android:textAppearance="@style/TextAppearance.BlackBody"
+            android:layout_gravity="start" />
+
+        <org.chromium.ui.widget.ButtonCompat
+            android:id="@+id/suspended_tab_settings_button"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:layout_gravity="end"
+            android:gravity="center_horizontal"
+            android:text="@string/preferences"
+            style="@style/TextButton" />
+    </LinearLayout>
+</ScrollView>
\ No newline at end of file
diff --git a/chrome/android/java/res/values/dimens.xml b/chrome/android/java/res/values/dimens.xml
index 9af44e1..38e61a16 100644
--- a/chrome/android/java/res/values/dimens.xml
+++ b/chrome/android/java/res/values/dimens.xml
@@ -313,7 +313,7 @@
     <dimen name="omnibox_suggestion_text_vertical_padding">5dp</dimen>
     <dimen name="omnibox_suggestion_multiline_text_vertical_padding">10dp</dimen>
     <dimen name="omnibox_suggestion_refine_view_modern_end_padding">4dp</dimen>
-    <dimen name="omnibox_answer_suggestion_icon_margin_start">8dp</dimen>
+    <dimen name="omnibox_answer_suggestion_icon_margin_start">10dp</dimen>
 
     <!-- NTP dimensions -->
     <dimen name="tile_grid_layout_max_width">504dp</dimen>
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/BitmapCache.java b/chrome/android/java/src/org/chromium/chrome/browser/BitmapCache.java
index b65ae5c..d7b2d53 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/BitmapCache.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/BitmapCache.java
@@ -121,6 +121,11 @@
         sDeduplicationCache.put(key, new WeakReference<>(bitmap));
     }
 
+    /** @return The total number of bytes taken by the bitmaps in this cache. */
+    public int size() {
+        return getBitmapCache().size();
+    }
+
     private RecentlyUsedCache getBitmapCache() {
         RecentlyUsedCache bitmapCache = mBitmapCache.get();
         if (bitmapCache == null) {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/BluetoothChooserDialog.java b/chrome/android/java/src/org/chromium/chrome/browser/BluetoothChooserDialog.java
index 05147d4..9a08e1b 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/BluetoothChooserDialog.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/BluetoothChooserDialog.java
@@ -311,7 +311,8 @@
     }
 
     private NoUnderlineClickableSpan createLinkSpan(@LinkType int linkType) {
-        return new NoUnderlineClickableSpan((view) -> onBluetoothLinkClick(view, linkType));
+        return new NoUnderlineClickableSpan(
+                mActivity.getResources(), (view) -> onBluetoothLinkClick(view, linkType));
     }
 
     private void onBluetoothLinkClick(View view, @LinkType int linkType) {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ChromeActivity.java b/chrome/android/java/src/org/chromium/chrome/browser/ChromeActivity.java
index 1ccf837..52afb27 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/ChromeActivity.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/ChromeActivity.java
@@ -1589,7 +1589,7 @@
         boolean hasPermanentMenuKey = ViewConfiguration.get(this).hasPermanentMenuKey();
         getAppMenuHandler().showAppMenu(
                 hasPermanentMenuKey ? null : getToolbarManager().getMenuButton(), false,
-                getToolbarManager().isBottomToolbarEnabled());
+                getToolbarManager().isMenuButtonInBottomToolbar());
     }
 
     /**
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ChromeFeatureList.java b/chrome/android/java/src/org/chromium/chrome/browser/ChromeFeatureList.java
index ffa6962..4fe7e0a 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/ChromeFeatureList.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/ChromeFeatureList.java
@@ -219,6 +219,8 @@
     public static final String DOWNLOAD_HOME_V2 = "DownloadHomeV2";
     public static final String DOWNLOADS_FOREGROUND = "DownloadsForeground";
     public static final String DOWNLOADS_AUTO_RESUMPTION_NATIVE = "DownloadsAutoResumptionNative";
+    public static final String DOWNLOAD_OFFLINE_CONTENT_PROVIDER =
+            "UseDownloadOfflineContentProvider";
     public static final String DOWNLOADS_LOCATION_CHANGE = "DownloadsLocationChange";
     public static final String EPHEMERAL_TAB = "EphemeralTab";
     public static final String EXPERIMENTAL_APP_BANNERS = "ExperimentalAppBanners";
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ChromeTabbedActivity.java b/chrome/android/java/src/org/chromium/chrome/browser/ChromeTabbedActivity.java
index d141469..5e9216a 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/ChromeTabbedActivity.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/ChromeTabbedActivity.java
@@ -1652,6 +1652,11 @@
     @Override
     protected AppMenuPropertiesDelegate createAppMenuPropertiesDelegate() {
         return new AppMenuPropertiesDelegate(this) {
+            private boolean isMenuButtonInBottomToolbar() {
+                return getToolbarManager() != null
+                        && getToolbarManager().isMenuButtonInBottomToolbar();
+            }
+
             private boolean shouldShowDataSaverMenuItem() {
                 return DataReductionProxySettings.getInstance()
                         .shouldUseDataReductionMainMenuItem();
@@ -1659,7 +1664,7 @@
 
             @Override
             public int getFooterResourceId() {
-                if (FeatureUtilities.isBottomToolbarEnabled()) {
+                if (isMenuButtonInBottomToolbar()) {
                     return this.shouldShowPageMenu() ? R.layout.icon_row_menu_footer : 0;
                 }
                 return shouldShowDataSaverMenuItem() ? R.layout.data_reduction_main_menu_item : 0;
@@ -1675,7 +1680,7 @@
 
             @Override
             public int getHeaderResourceId() {
-                if (FeatureUtilities.isBottomToolbarEnabled()) {
+                if (isMenuButtonInBottomToolbar()) {
                     return shouldShowDataSaverMenuItem() ? R.layout.data_reduction_main_menu_item
                                                          : 0;
                 }
@@ -1691,7 +1696,7 @@
 
             @Override
             public boolean shouldShowFooter(int maxMenuHeight) {
-                if (FeatureUtilities.isBottomToolbarEnabled()) return true;
+                if (isMenuButtonInBottomToolbar()) return true;
                 if (shouldShowDataSaverMenuItem()) {
                     return canShowDataReductionItem(maxMenuHeight);
                 }
@@ -1700,7 +1705,7 @@
 
             @Override
             public boolean shouldShowHeader(int maxMenuHeight) {
-                if (!FeatureUtilities.isBottomToolbarEnabled()) {
+                if (!isMenuButtonInBottomToolbar()) {
                     return super.shouldShowHeader(maxMenuHeight);
                 }
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/UsbChooserDialog.java b/chrome/android/java/src/org/chromium/chrome/browser/UsbChooserDialog.java
index 5ca436f..02668b0 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/UsbChooserDialog.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/UsbChooserDialog.java
@@ -68,14 +68,15 @@
         String noneFound = activity.getString(R.string.usb_chooser_dialog_no_devices_found_prompt);
         SpannableString statusActive = SpanApplier.applySpans(
                 activity.getString(R.string.usb_chooser_dialog_footnote_text),
-                new SpanInfo("<link>", "</link>", new NoUnderlineClickableSpan((view) -> {
-                    if (mNativeUsbChooserDialogPtr == 0) return;
+                new SpanInfo("<link>", "</link>",
+                        new NoUnderlineClickableSpan(activity.getResources(), (view) -> {
+                            if (mNativeUsbChooserDialogPtr == 0) return;
 
-                    nativeLoadUsbHelpPage(mNativeUsbChooserDialogPtr);
+                            nativeLoadUsbHelpPage(mNativeUsbChooserDialogPtr);
 
-                    // Get rid of the highlight background on selection.
-                    view.invalidate();
-                })));
+                            // Get rid of the highlight background on selection.
+                            view.invalidate();
+                        })));
         SpannableString statusIdleNoneFound = statusActive;
         SpannableString statusIdleSomeFound = statusActive;
         String positiveButton = activity.getString(R.string.usb_chooser_dialog_connect_button_text);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/appmenu/AppMenu.java b/chrome/android/java/src/org/chromium/chrome/browser/appmenu/AppMenu.java
index 468e181a..13c2d71 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/appmenu/AppMenu.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/appmenu/AppMenu.java
@@ -168,12 +168,14 @@
      *                              Can be {@code null} if no item should be highlighted.  Note that
      *                              {@code 0} is dedicated to custom menu items and can be declared
      *                              by external apps.
+     * @param circleHighlightItem   Whether the highlighted item should use a circle highlight or
+     *                              not.
      * @param showFromBottom        Whether the appearance animation should run from the bottom up.
      */
     void show(Context context, final View anchorView, boolean isByPermanentButton,
             int screenRotation, Rect visibleDisplayFrame, int screenHeight,
             @IdRes int footerResourceId, @IdRes int headerResourceId, Integer highlightedItemId,
-            boolean showFromBottom) {
+            boolean circleHighlightItem, boolean showFromBottom) {
         mPopup = new PopupWindow(context);
         mPopup.setFocusable(true);
         mPopup.setInputMethodMode(PopupWindow.INPUT_METHOD_NOT_NEEDED);
@@ -257,10 +259,14 @@
                 (ViewGroup) LayoutInflater.from(context).inflate(R.layout.app_menu_layout, null);
         mListView = (ListView) contentView.findViewById(R.id.app_menu_list);
 
-        int footerHeight =
-                inflateFooter(footerResourceId, contentView, menuWidth, highlightedItemId);
+        int footerHeight = inflateFooter(footerResourceId, contentView, menuWidth);
         int headerHeight = inflateHeader(headerResourceId, contentView, menuWidth);
 
+        if (highlightedItemId != null) {
+            View viewToHighlight = contentView.findViewById(highlightedItemId);
+            ViewHighlighter.turnOnHighlight(viewToHighlight, circleHighlightItem);
+        }
+
         // Set the adapter after the header is added to avoid crashes on JellyBean.
         // See crbug.com/761726.
         mListView.setAdapter(mAdapter);
@@ -547,8 +553,7 @@
         highlightedView.sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_FOCUSED);
     }
 
-    private int inflateFooter(
-            int footerResourceId, View contentView, int menuWidth, Integer highlightedItemId) {
+    private int inflateFooter(int footerResourceId, View contentView, int menuWidth) {
         if (footerResourceId == 0) {
             mFooterView = null;
             return 0;
@@ -562,11 +567,6 @@
         int heightMeasureSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
         mFooterView.measure(widthMeasureSpec, heightMeasureSpec);
 
-        if (highlightedItemId != null) {
-            View viewToHighlight = mFooterView.findViewById(highlightedItemId);
-            ViewHighlighter.turnOnHighlight(viewToHighlight, viewToHighlight != mFooterView);
-        }
-
         if (mHandler != null) mHandler.onFooterViewInflated(mFooterView);
 
         return mFooterView.getMeasuredHeight();
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/appmenu/AppMenuHandler.java b/chrome/android/java/src/org/chromium/chrome/browser/appmenu/AppMenuHandler.java
index 012ccae..24610d0 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/appmenu/AppMenuHandler.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/appmenu/AppMenuHandler.java
@@ -44,6 +44,11 @@
     private Integer mHighlightMenuId;
 
     /**
+     *  Whether the highlighted item should use a circle highlight or not.
+     */
+    private boolean mCircleHighlight;
+
+    /**
      * Constructs an AppMenuHandler object.
      * @param activity Activity that is using the AppMenu.
      * @param delegate Delegate used to check the desired AppMenu properties on show.
@@ -73,15 +78,24 @@
     }
 
     /**
+     * Clears the menu highlight.
+     */
+    public void clearMenuHighlight() {
+        setMenuHighlight(null, false);
+    }
+
+    /**
      * Calls attention to this menu and a particular item in it.  The menu will only stay
      * highlighted for one menu usage.  After that the highlight will be cleared.
      * @param highlightItemId The id of a menu item to highlight or {@code null} to turn off the
      *                        highlight.
+     * @param circleHighlight Whether the highlighted item should use a circle highlight or not.
      */
-    public void setMenuHighlight(Integer highlightItemId) {
+    public void setMenuHighlight(Integer highlightItemId, boolean circleHighlight) {
         if (mHighlightMenuId == null && highlightItemId == null) return;
         if (mHighlightMenuId != null && mHighlightMenuId.equals(highlightItemId)) return;
         mHighlightMenuId = highlightItemId;
+        mCircleHighlight = circleHighlight;
         boolean highlighting = mHighlightMenuId != null;
         for (AppMenuObserver observer : mObservers) observer.onMenuHighlightChanged(highlighting);
     }
@@ -169,9 +183,10 @@
             headerResourceId = mDelegate.getHeaderResourceId();
         }
         mAppMenu.show(wrapper, anchorView, isByPermanentButton, rotation, appRect, pt.y,
-                footerResourceId, headerResourceId, mHighlightMenuId, showFromBottom);
+                footerResourceId, headerResourceId, mHighlightMenuId, mCircleHighlight,
+                showFromBottom);
         mAppMenuDragHelper.onShow(startDragging);
-        setMenuHighlight(null);
+        clearMenuHighlight();
         RecordUserAction.record("MobileMenuShow");
         return true;
     }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/appmenu/AppMenuPropertiesDelegate.java b/chrome/android/java/src/org/chromium/chrome/browser/appmenu/AppMenuPropertiesDelegate.java
index 92963b4..1c19a099 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/appmenu/AppMenuPropertiesDelegate.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/appmenu/AppMenuPropertiesDelegate.java
@@ -120,9 +120,9 @@
                             < DeviceFormFactor.getMinimumTabletWidthPx(
                                       mActivity.getWindowAndroid().getDisplay());
 
-            boolean bottomToolbarEnabled = mActivity.getToolbarManager() != null
-                    && mActivity.getToolbarManager().getBottomToolbarCoordinator() != null;
-            shouldShowIconRow &= !bottomToolbarEnabled;
+            final boolean bottomToolbarVisible = mActivity.getToolbarManager() != null
+                    && mActivity.getToolbarManager().isMenuButtonInBottomToolbar();
+            shouldShowIconRow &= !bottomToolbarVisible;
 
             // Update the icon row items (shown in narrow form factors).
             menu.findItem(R.id.icon_row_menu_id).setVisible(shouldShowIconRow);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/autofill/AutofillUiUtils.java b/chrome/android/java/src/org/chromium/chrome/browser/autofill/AutofillUiUtils.java
index f7bed5c..85bedb5 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/autofill/AutofillUiUtils.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/autofill/AutofillUiUtils.java
@@ -21,6 +21,7 @@
 import android.widget.TextView;
 
 import org.chromium.base.ApiCompatibilityUtils;
+import org.chromium.base.VisibleForTesting;
 import org.chromium.chrome.R;
 
 import java.lang.annotation.Retention;
@@ -28,7 +29,6 @@
 import java.util.Calendar;
 /**
  * Helper methods that can be used across multiple Autofill UIs.
- * TODO(crbug.com/934915) Add unit tests.
  */
 public class AutofillUiUtils {
     /**
@@ -113,16 +113,18 @@
         int thisMonth = calendar.get(Calendar.MONTH) + 1; // calendar month is 0-based
 
         int month = getMonth(monthInput);
-        if (month < 1 || month > 12) {
+        if (month == -1) {
             if (monthInput.getText().length() == EXPIRATION_FIELDS_LENGTH
                     || (!monthInput.isFocused() && didFocusOnMonth)) {
                 return ErrorType.EXPIRATION_MONTH;
             }
-            return ErrorType.NOT_ENOUGH_INFO;
+            if (!didFocusOnYear) {
+                return ErrorType.NOT_ENOUGH_INFO;
+            }
         }
 
         int year = getFourDigitYear(yearInput);
-        if (year < thisYear || year > thisYear + 10) {
+        if (year == -1) {
             if (yearInput.getText().length() == EXPIRATION_FIELDS_LENGTH
                     || (!yearInput.isFocused() && didFocusOnYear)) {
                 return ErrorType.EXPIRATION_YEAR;
@@ -141,7 +143,8 @@
      * @param yearInput EditText for the year field.
      * @return The expiration year the user entered.
      *         Two digit values (such as 17) will be converted to 4 digit years (such as 2017).
-     *         Returns -1 if the input is empty or otherwise not a valid year.
+     *         Returns -1 if the input is empty or otherwise not a valid year (previous year or
+     *         more than 10 years in the future).
      */
     public static int getFourDigitYear(EditText yearInput) {
         Calendar calendar = Calendar.getInstance();
@@ -150,6 +153,7 @@
             int year = Integer.parseInt(yearInput.getText().toString());
             if (year < 0) return -1;
             if (year < 100) year += thisYear - thisYear % 100;
+            if (year < thisYear || year > thisYear + 10) return -1;
             return year;
         } catch (NumberFormatException e) {
             return -1;
@@ -159,11 +163,16 @@
     /**
      * @param monthInput EditText for the month field.
      * @return The expiration month the user entered.
-     *         Returns -1 if the input is empty or not a number.
+     *         Returns -1 if not a valid month.
      */
-    private static int getMonth(EditText monthInput) {
+    @VisibleForTesting
+    static int getMonth(EditText monthInput) {
         try {
-            return Integer.parseInt(monthInput.getText().toString());
+            int month = Integer.parseInt(monthInput.getText().toString());
+            if (month < 1 || month > 12) {
+                return -1;
+            }
+            return month;
         } catch (NumberFormatException e) {
             return -1;
         }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/autofill/keyboard_accessory/KeyboardAccessoryData.java b/chrome/android/java/src/org/chromium/chrome/browser/autofill/keyboard_accessory/KeyboardAccessoryData.java
index d69dc98f..af62bae 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/autofill/keyboard_accessory/KeyboardAccessoryData.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/autofill/keyboard_accessory/KeyboardAccessoryData.java
@@ -347,12 +347,10 @@
     /**
      * Represents the contents of a accessory sheet tab below the keyboard accessory, which can
      * correspond to passwords, credit cards, or profiles data. Created natively.
-     *
-     * TODO(crbug.com/902425): Add a field to indicate if this corresponds to password, profile, or
-     *                         credit card data.
      */
     public final static class AccessorySheetData {
         private final String mTitle;
+        private final @FallbackSheetType int mSheetType;
         private final List<UserInfo> mUserInfoList = new ArrayList<>();
         private final List<FooterCommand> mFooterCommands = new ArrayList<>();
 
@@ -360,10 +358,15 @@
          * Creates the AccessorySheetData object.
          * @param title The title of accessory sheet tab.
          */
-        public AccessorySheetData(String title) {
+        public AccessorySheetData(@FallbackSheetType int sheetType, String title) {
+            mSheetType = sheetType;
             mTitle = title;
         }
 
+        public @FallbackSheetType int getSheetType() {
+            return mSheetType;
+        }
+
         /**
          * Returns the title of the accessory sheet. This text is also used for accessibility.
          */
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/autofill/keyboard_accessory/ManualFillingBridge.java b/chrome/android/java/src/org/chromium/chrome/browser/autofill/keyboard_accessory/ManualFillingBridge.java
index f9cb938f..613d8a89 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/autofill/keyboard_accessory/ManualFillingBridge.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/autofill/keyboard_accessory/ManualFillingBridge.java
@@ -101,8 +101,8 @@
     }
 
     @CalledByNative
-    private static Object createAccessorySheetData(String title) {
-        return new AccessorySheetData(title);
+    private static Object createAccessorySheetData(@FallbackSheetType int type, String title) {
+        return new AccessorySheetData(type, title);
     }
 
     @CalledByNative
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/autofill_assistant/AssistantOnboardingCoordinator.java b/chrome/android/java/src/org/chromium/chrome/browser/autofill_assistant/AssistantOnboardingCoordinator.java
index 0d666d1..1142f1cb 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/autofill_assistant/AssistantOnboardingCoordinator.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/autofill_assistant/AssistantOnboardingCoordinator.java
@@ -36,7 +36,7 @@
         String termsString = context.getApplicationContext().getString(
                 R.string.autofill_assistant_google_terms_description);
 
-        NoUnderlineClickableSpan termsSpan = new NoUnderlineClickableSpan(
+        NoUnderlineClickableSpan termsSpan = new NoUnderlineClickableSpan(context.getResources(),
                 (widget)
                         -> CustomTabActivity.showInfoPage(context.getApplicationContext(),
                                 context.getApplicationContext().getString(
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/autofill_assistant/overlay/TouchEventFilterView.java b/chrome/android/java/src/org/chromium/chrome/browser/autofill_assistant/overlay/TouchEventFilterView.java
index e0ff719..85e7b37 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/autofill_assistant/overlay/TouchEventFilterView.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/autofill_assistant/overlay/TouchEventFilterView.java
@@ -4,9 +4,12 @@
 
 package org.chromium.chrome.browser.autofill_assistant.overlay;
 
+import android.animation.TimeInterpolator;
+import android.animation.ValueAnimator;
 import android.annotation.SuppressLint;
 import android.content.Context;
 import android.graphics.Canvas;
+import android.graphics.Color;
 import android.graphics.Paint;
 import android.graphics.PorterDuff;
 import android.graphics.PorterDuffXfermode;
@@ -28,6 +31,7 @@
 import org.chromium.content_public.browser.GestureListenerManager;
 import org.chromium.content_public.browser.GestureStateListener;
 import org.chromium.content_public.browser.WebContents;
+import org.chromium.ui.interpolators.BakedBezierInterpolator;
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
@@ -55,6 +59,13 @@
  */
 public class TouchEventFilterView
         extends View implements ChromeFullscreenManager.FullscreenListener, GestureStateListener {
+    private static final int FADE_DURATION_MS = 250;
+
+    /** Alpha value of the background, used for animations. */
+    private static final int BACKGROUND_ALPHA = 0x42;
+
+    /** Width of the line drawn around the boxes. */
+    private static final int BOX_STROKE_WIDTH = 3;
 
     /**
      * Complain after there's been {@link TAP_TRACKING_COUNT} taps within
@@ -82,8 +93,11 @@
     private ChromeFullscreenManager mFullscreenManager;
     private GestureListenerManager mGestureListenerManager;
     private View mCompositorView;
-    private final Paint mGrayOut;
+
+    private final Paint mBackground;
     private final Paint mClear;
+    private final Paint mBoxStroke;
+    private final Paint mBoxFill;
 
     @AssistantOverlayState
     private int mCurrentState = AssistantOverlayState.HIDDEN;
@@ -167,6 +181,18 @@
     /** Current bottom margin of this view. */
     private int mMarginBottom;
 
+    /**
+     * Currently running animator for boxes fade in and out, either {@link #mFadeOutBoxesAnimator}
+     * or {@link #mFadeOutBoxesAnimator}.
+     */
+    private ValueAnimator mCurrentBoxesAnimator;
+
+    /** Animator created by {@link #fadeInBoxes}. Fades in box content. */
+    private ValueAnimator mFadeInBoxesAnimator;
+
+    /** Animator created by {@link #fadeOutBoxes}. Fades out box content. */
+    private ValueAnimator mFadeOutBoxesAnimator;
+
     public TouchEventFilterView(Context context) {
         this(context, null, 0);
     }
@@ -177,10 +203,25 @@
 
     public TouchEventFilterView(Context context, AttributeSet attributeSet, int defStyle) {
         super(context, attributeSet, defStyle);
-        mGrayOut = new Paint(Paint.ANTI_ALIAS_FLAG);
-        mGrayOut.setColor(
-                ApiCompatibilityUtils.getColor(context.getResources(), R.color.black_alpha_65));
-        mGrayOut.setStyle(Paint.Style.FILL);
+        mBackground = new Paint(Paint.ANTI_ALIAS_FLAG);
+        mBackground.setColor(Color.BLACK);
+        mBackground.setAlpha(BACKGROUND_ALPHA);
+        mBackground.setStyle(Paint.Style.FILL);
+
+        mClear = new Paint();
+        mClear.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));
+        mClear.setStyle(Paint.Style.FILL);
+
+        mBoxFill = new Paint(Paint.ANTI_ALIAS_FLAG);
+        mBoxFill.setColor(Color.BLACK);
+        mBoxFill.setStyle(Paint.Style.FILL);
+
+        mBoxStroke = new Paint(Paint.ANTI_ALIAS_FLAG);
+        mBoxStroke.setColor(
+                ApiCompatibilityUtils.getColor(context.getResources(), R.color.modern_blue_600));
+        mBoxStroke.setStyle(Paint.Style.STROKE);
+        mBoxStroke.setStrokeWidth(BOX_STROKE_WIDTH);
+        mBoxStroke.setStrokeCap(Paint.Cap.ROUND);
 
         mPaddingPx = TypedValue.applyDimension(
                 TypedValue.COMPLEX_UNIT_DIP, 2, context.getResources().getDisplayMetrics());
@@ -188,9 +229,6 @@
                 TypedValue.COMPLEX_UNIT_DIP, 8, context.getResources().getDisplayMetrics());
         // TODO(crbug.com/806868): Add support for XML attributes configuration.
 
-        mClear = new Paint();
-        mClear.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));
-
         mTapDetector = new GestureDetector(context, new SimpleOnGestureListener() {
             @Override
             public boolean onSingleTapUp(MotionEvent e) {
@@ -254,14 +292,35 @@
      * Set the current state of the overlay.
      */
     public void setState(@AssistantOverlayState int newState) {
+        if (AccessibilityUtil.isAccessibilityEnabled()
+                && newState == AssistantOverlayState.PARTIAL) {
+            // Touch exploration is fully disabled if there's an overlay in front. In this case, the
+            // overlay must be fully gone and filtering elements for touch exploration must happen
+            // at another level.
+            newState = AssistantOverlayState.HIDDEN;
+        }
+
+        if (mCurrentState == newState) return;
+
+        // Animate transitions
+        if (newState == AssistantOverlayState.HIDDEN) {
+            setVisibility(View.GONE);
+        } else if (mCurrentState == AssistantOverlayState.HIDDEN) {
+            setVisibility(View.VISIBLE);
+        } else if (mCurrentState == AssistantOverlayState.FULL
+                && newState == AssistantOverlayState.PARTIAL) {
+            if (!mTouchableArea.isEmpty()) fadeOutBoxes();
+        } else if (mCurrentState == AssistantOverlayState.PARTIAL
+                && newState == AssistantOverlayState.FULL) {
+            if (!mTouchableArea.isEmpty()) fadeInBoxes();
+        }
+
         mCurrentState = newState;
 
         // Reset tap counter each time we hide the overlay.
         if (mCurrentState == AssistantOverlayState.HIDDEN) {
             mUnexpectedTapTimes.clear();
         }
-
-        updateVisibility();
         invalidate();
     }
 
@@ -269,26 +328,18 @@
      * Set the touchable area. This only applies if current state is AssistantOverlayState.PARTIAL.
      */
     public void setTouchableArea(List<RectF> touchableArea) {
-        mTouchableArea = touchableArea;
-
-        clearOffsets();
         if (mCurrentState == AssistantOverlayState.PARTIAL) {
             invalidate();
+            boolean wasEmpty = mTouchableArea.isEmpty();
+            boolean isEmpty = touchableArea.isEmpty();
+            if (wasEmpty && !isEmpty) {
+                fadeOutBoxes();
+            } else if (!wasEmpty && isEmpty) {
+                fadeInBoxes();
+            }
         }
-    }
-
-    private void updateVisibility() {
-        if (AccessibilityUtil.isAccessibilityEnabled()) {
-            // Touch exploration is fully disabled if there's an overlay in front. In this case, the
-            // overlay must be fully gone and filtering elements for touch exploration must happen
-            // at another level.
-            //
-            // TODO(crbug.com/806868): filter elements available to touch exploration, when it
-            // is enabled.
-            setVisibility(mCurrentState != AssistantOverlayState.FULL ? View.GONE : View.VISIBLE);
-        }
-
-        setAlpha(mCurrentState == AssistantOverlayState.HIDDEN ? 0.0f : 1.0f);
+        mTouchableArea = touchableArea;
+        clearOffsets();
     }
 
     private void clearOffsets() {
@@ -481,7 +532,7 @@
         if (mCurrentState == AssistantOverlayState.HIDDEN) {
             return;
         }
-        canvas.drawPaint(mGrayOut);
+        canvas.drawPaint(mBackground);
 
         int width = canvas.getWidth();
         int yTop = getVisualViewportTop();
@@ -498,6 +549,13 @@
         }
 
         int height = yBottom - yTop;
+
+        float boxesAlpha = (mCurrentBoxesAnimator != null && mCurrentBoxesAnimator.isRunning())
+                ? ((float) mCurrentBoxesAnimator.getAnimatedValue())
+                : 0f;
+        mBoxStroke.setAlpha((int) (0xff * (1.0 - boxesAlpha)));
+        mBoxFill.setAlpha((int) (BACKGROUND_ALPHA * boxesAlpha));
+
         for (RectF rect : mTouchableArea) {
             mDrawRect.left = rect.left * width - mPaddingPx;
             mDrawRect.top =
@@ -509,8 +567,16 @@
                 // Rounded corners look strange in the case where the rectangle takes exactly the
                 // width of the screen.
                 canvas.drawRect(mDrawRect, mClear);
+                if (boxesAlpha > 0f) {
+                    canvas.drawRect(mDrawRect, mBoxFill);
+                }
+                canvas.drawRect(mDrawRect, mBoxStroke);
             } else {
                 canvas.drawRoundRect(mDrawRect, mCornerPx, mCornerPx, mClear);
+                if (boxesAlpha > 0f) {
+                    canvas.drawRoundRect(mDrawRect, mCornerPx, mCornerPx, mBoxFill);
+                }
+                canvas.drawRoundRect(mDrawRect, mCornerPx, mCornerPx, mBoxStroke);
             }
         }
     }
@@ -667,4 +733,40 @@
         params.setMargins(/* left= */ 0, /* top= */ top, /* right= */ 0, /* bottom= */ bottom);
         setLayoutParams(params);
     }
+
+    private void fadeInBoxes() {
+        if (mFadeInBoxesAnimator == null) {
+            mFadeInBoxesAnimator =
+                    createBoxesAnimator(0f, 1f, BakedBezierInterpolator.FADE_IN_CURVE);
+        }
+        runBoxesAnimation(mFadeInBoxesAnimator);
+    }
+
+    private void fadeOutBoxes() {
+        if (mFadeOutBoxesAnimator == null) {
+            mFadeOutBoxesAnimator =
+                    createBoxesAnimator(1f, 0f, BakedBezierInterpolator.FADE_OUT_CURVE);
+        }
+        runBoxesAnimation(mFadeOutBoxesAnimator);
+    }
+
+    private ValueAnimator createBoxesAnimator(
+            float start, float end, TimeInterpolator interpolator) {
+        ValueAnimator animator = ValueAnimator.ofFloat(start, end);
+        animator.setDuration(FADE_DURATION_MS);
+        animator.setInterpolator(interpolator);
+        animator.addUpdateListener((ignoredAnimator) -> invalidate());
+        return animator;
+    }
+
+    private void runBoxesAnimation(ValueAnimator animator) {
+        if (mCurrentBoxesAnimator != null && mCurrentBoxesAnimator.isRunning()) {
+            if (mCurrentBoxesAnimator == animator) {
+                return;
+            }
+            mCurrentBoxesAnimator.cancel();
+        }
+        animator.start();
+        mCurrentBoxesAnimator = animator;
+    }
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkItemRow.java b/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkItemRow.java
index ebb88f0..68c8fbe 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkItemRow.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkItemRow.java
@@ -34,7 +34,7 @@
         super(context, attrs);
         mMinIconSize = (int) getResources().getDimension(R.dimen.default_favicon_min_size);
         mDisplayedIconSize = getResources().getDimensionPixelSize(R.dimen.default_favicon_size);
-        mIconGenerator = ViewUtils.createDefaultRoundedIconGenerator(true);
+        mIconGenerator = ViewUtils.createDefaultRoundedIconGenerator(context.getResources(), true);
     }
 
     // BookmarkRow implementation.
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/bookmarkswidget/BookmarkWidgetService.java b/chrome/android/java/src/org/chromium/chrome/browser/bookmarkswidget/BookmarkWidgetService.java
index 5c1a80dc..0adacd5 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/bookmarkswidget/BookmarkWidgetService.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/bookmarkswidget/BookmarkWidgetService.java
@@ -176,7 +176,8 @@
                     Profile.getLastUsedProfile().getOriginalProfile());
             mMinIconSizeDp = (int) res.getDimension(R.dimen.default_favicon_min_size);
             mDisplayedIconSize = res.getDimensionPixelSize(R.dimen.default_favicon_size);
-            mIconGenerator = ViewUtils.createDefaultRoundedIconGenerator(false);
+            mIconGenerator =
+                    ViewUtils.createDefaultRoundedIconGenerator(context.getResources(), false);
 
             mRemainingTaskCount = 1;
             mBookmarkModel = new BookmarkModel();
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/compositor/LayerTitleCache.java b/chrome/android/java/src/org/chromium/chrome/browser/compositor/LayerTitleCache.java
index 4c53444..3c8e8c0f 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/compositor/LayerTitleCache.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/compositor/LayerTitleCache.java
@@ -20,6 +20,7 @@
 import org.chromium.chrome.browser.favicon.FaviconHelper.FaviconImageCallback;
 import org.chromium.chrome.browser.profiles.Profile;
 import org.chromium.chrome.browser.tab.Tab;
+import org.chromium.chrome.browser.tab.TabFavicon;
 import org.chromium.chrome.browser.tabmodel.TabModelSelector;
 import org.chromium.ui.base.DeviceFormFactor;
 import org.chromium.ui.resources.ResourceManager;
@@ -125,7 +126,7 @@
         boolean isHTSEnabled = !DeviceFormFactor.isNonMultiDisplayContextOnTablet(tab.getActivity())
                 && ChromeFeatureList.isEnabled(ChromeFeatureList.HORIZONTAL_TAB_SWITCHER_ANDROID);
         boolean isDarkTheme = tab.isIncognito() && !isHTSEnabled;
-        Bitmap originalFavicon = tab.getFavicon();
+        Bitmap originalFavicon = TabFavicon.getBitmap(tab);
         if (originalFavicon == null) {
             originalFavicon = mDefaultFaviconHelper.getDefaultFaviconBitmap(
                     mContext, tab.getUrl(), !isDarkTheme);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/compositor/bottombar/contextualsearch/ContextualSearchPromoControl.java b/chrome/android/java/src/org/chromium/chrome/browser/compositor/bottombar/contextualsearch/ContextualSearchPromoControl.java
index 46b1e95..318b6eb 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/compositor/bottombar/contextualsearch/ContextualSearchPromoControl.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/compositor/bottombar/contextualsearch/ContextualSearchPromoControl.java
@@ -359,7 +359,7 @@
         // Fill in text with link to Settings.
         TextView promoText = (TextView) view.findViewById(R.id.contextual_search_promo_text);
 
-        NoUnderlineClickableSpan settingsLink = new NoUnderlineClickableSpan(
+        NoUnderlineClickableSpan settingsLink = new NoUnderlineClickableSpan(view.getResources(),
                 (View ignored) -> ContextualSearchPromoControl.this.handleClickSettingsLink());
 
         promoText.setText(SpanApplier.applySpans(
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/ToolbarSwipeLayout.java b/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/ToolbarSwipeLayout.java
index bd497c7..e53bd926 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/ToolbarSwipeLayout.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/ToolbarSwipeLayout.java
@@ -73,6 +73,9 @@
     private ScrollingBottomViewSceneLayer mLeftBottomToolbarSceneLayer;
     private ScrollingBottomViewSceneLayer mRightBottomToolbarSceneLayer;
 
+    /** Whether the bottom bar scene layers should be shown. */
+    private boolean mShowBottomToolbarSceneLayers;
+
     /**
      * @param context             The current Android's context.
      * @param updateHost          The {@link LayoutUpdateHost} view for this layout.
@@ -304,8 +307,12 @@
             mLeftTab.setX(leftX);
             needUpdate = mLeftTab.updateSnap(dt) || needUpdate;
             if (mLeftBottomToolbarSceneLayer != null) {
-                mLeftBottomToolbarSceneLayer.setIsVisible(true);
-                mLeftBottomToolbarSceneLayer.setXOffset((int) (mLeftTab.getX() * mDpToPx));
+                if (mShowBottomToolbarSceneLayers) {
+                    mLeftBottomToolbarSceneLayer.setIsVisible(true);
+                    mLeftBottomToolbarSceneLayer.setXOffset((int) (mLeftTab.getX() * mDpToPx));
+                } else {
+                    mLeftBottomToolbarSceneLayer.setIsVisible(false);
+                }
             }
         } else if (mLeftBottomToolbarSceneLayer != null) {
             mLeftBottomToolbarSceneLayer.setIsVisible(false);
@@ -315,8 +322,12 @@
             mRightTab.setX(rightX);
             needUpdate = mRightTab.updateSnap(dt) || needUpdate;
             if (mRightBottomToolbarSceneLayer != null) {
-                mRightBottomToolbarSceneLayer.setIsVisible(true);
-                mRightBottomToolbarSceneLayer.setXOffset((int) (mRightTab.getX() * mDpToPx));
+                if (mShowBottomToolbarSceneLayers) {
+                    mRightBottomToolbarSceneLayer.setIsVisible(true);
+                    mRightBottomToolbarSceneLayer.setXOffset((int) (mRightTab.getX() * mDpToPx));
+                } else {
+                    mRightBottomToolbarSceneLayer.setIsVisible(false);
+                }
             }
         } else if (mRightBottomToolbarSceneLayer != null) {
             mRightBottomToolbarSceneLayer.setIsVisible(false);
@@ -330,12 +341,16 @@
      * in this layout.
      * @param left The toolbar to draw with the left tab.
      * @param right The toolbar to draw with the right tab.
+     * @param showBottomToolbarSceneLayers Whether to show the bottom bar scene layers.
      */
-    public void setBottomToolbarSceneLayers(
-            ScrollingBottomViewSceneLayer left, ScrollingBottomViewSceneLayer right) {
+    public void setBottomToolbarSceneLayers(ScrollingBottomViewSceneLayer left,
+            ScrollingBottomViewSceneLayer right, boolean showBottomToolbarSceneLayers) {
+        mShowBottomToolbarSceneLayers = showBottomToolbarSceneLayers;
         mLeftBottomToolbarSceneLayer = left;
+        mLeftBottomToolbarSceneLayer.setIsVisible(showBottomToolbarSceneLayers);
         addSceneOverlay(mLeftBottomToolbarSceneLayer);
         mRightBottomToolbarSceneLayer = right;
+        mRightBottomToolbarSceneLayer.setIsVisible(showBottomToolbarSceneLayers);
         addSceneOverlay(mRightBottomToolbarSceneLayer);
     }
 
@@ -388,4 +403,11 @@
         mSceneLayer.pushLayers(getContext(), contentViewport, contentViewport, this,
                 layerTitleCache, tabContentManager, resourceManager, fullscreenManager);
     }
+
+    /**
+     * @param showBottomToolbarSceneLayers Whether the bottom toolbar scene layers should be shown.
+     */
+    public void setBottomToolbarSceneLayersVisibility(boolean showBottomToolbarSceneLayers) {
+        mShowBottomToolbarSceneLayers = showBottomToolbarSceneLayers;
+    }
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchFieldTrial.java b/chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchFieldTrial.java
index ceb12a7..c58ff75 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchFieldTrial.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchFieldTrial.java
@@ -75,8 +75,6 @@
     private static final String DISABLE_AMP_AS_SEPARATE_TAB = "disable_amp_as_separate_tab";
     // Disable logging for Machine Learning
     private static final String DISABLE_UKM_RANKER_LOGGING = "disable_ukm_ranker_logging";
-    private static final String DISABLE_SUPPRESS_FOR_SMART_SELECTION =
-            "disable_suppress_for_smart_selection";
 
     // ----------------------
     // Privacy-related flags.
@@ -112,7 +110,6 @@
     private static Boolean sIsSendHomeCountryDisabled;
     private static Boolean sIsPageContentNotificationDisabled;
     private static Boolean sIsUkmRankerLoggingDisabled;
-    private static Boolean sIsSuppressForSmartSelectionDisabled;
     private static Integer sWaitAfterTapDelayMs;
     private static Integer sTapDurationThresholdMs;
     private static Integer sRecentScrollDurationMs;
@@ -369,21 +366,6 @@
     }
 
     /**
-     * Determines whether the Contextual Search UI is suppressed when Smart Select is active.
-     * This applies to long-press activation of a UI for Smart Select and/or Contextual Search. If
-     * this returns true, the Contextual Search Bar will be allowed to show in response to a
-     * long-press gesture on Android O even when the Smart Select UI may be active.
-     * @return Whether suppression our UI when Smart Select is active has been disabled.
-     */
-    static boolean isSuppressForSmartSelectionDisabled() {
-        if (sIsSuppressForSmartSelectionDisabled == null) {
-            sIsSuppressForSmartSelectionDisabled =
-                    getBooleanParam(DISABLE_SUPPRESS_FOR_SMART_SELECTION);
-        }
-        return sIsSuppressForSmartSelectionDisabled;
-    }
-
-    /**
      * Gets an amount to delay after a Tap gesture is recognized, in case some user gesture
      * immediately follows that would prevent the UI from showing.
      * The classic example is a scroll, which might be a signal that the previous tap was
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 1da7a5f..f5410e48 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
@@ -197,8 +197,8 @@
     // TODO(donnd): replace with a more systematic approach using the InternalStateController.
     private int mSelectWordAroundCaretCounter;
 
-    /** Whether ContextualSearch UI is suppressed for Smart Selection. */
-    private boolean mDoSuppressContextualSearchForSmartSelection;
+    /** Whether Smart Text Selection might be active.  If false, we know it's not active. */
+    private boolean mCouldSmartSelectionBeActive;
 
     /** An observer that reports selected context to GSA for search quality. */
     private ContextualSearchObserver mContextReportingObserver;
@@ -1290,9 +1290,13 @@
         return mContextualSearchSelectionClient;
     }
 
-    /** Notifies Contextual Search whether the UI should be suppressed for Smart Selection. */
-    void suppressContextualSearchForSmartSelection(boolean isSmartSelectionEnabled) {
-        mDoSuppressContextualSearchForSmartSelection = isSmartSelectionEnabled;
+    /**
+     * Notifies Contextual Search whether Smart Selection could be active.
+     * @param isSmartSelectionEnabledInChrome Whether Smart Selection is enabled in Chrome, and
+     *        therefore could be active.
+     */
+    void setCouldSmartSelectionBeActive(boolean isSmartSelectionEnabledInChrome) {
+        mCouldSmartSelectionBeActive = isSmartSelectionEnabledInChrome;
     }
 
     /**
@@ -1571,9 +1575,9 @@
                 };
 
                 boolean isTap = mSelectionController.getSelectionType() == SelectionType.TAP;
-                if (!isTap && mDoSuppressContextualSearchForSmartSelection && mContext != null) {
-                    // If Smart Selection is active we need to work around a race
-                    // condition gathering surrounding text.  See issue 773330.
+                if (!isTap && mCouldSmartSelectionBeActive && mContext != null) {
+                    // If Smart Selection might be active we need to work around a race
+                    // condition gathering surrounding text.  See https://crbug.com/773330.
                     // Instead we just return the selection which is good enough for the assistant.
                     mInternalStateController.notifyStartingWorkOn(
                             InternalState.GATHERING_SURROUNDINGS);
@@ -1717,18 +1721,7 @@
             public void showContextualSearchLongpressUi() {
                 mInternalStateController.notifyStartingWorkOn(
                         InternalState.SHOWING_LONGPRESS_SEARCH);
-                boolean suppressForSmartSelection = mDoSuppressContextualSearchForSmartSelection
-                        && !ContextualSearchFieldTrial.isSuppressForSmartSelectionDisabled();
-                if (suppressForSmartSelection) {
-                    // Make sure we close any existing UX since Smart Select has taken this action.
-                    // Specifically, this happens when the user taps on an existing tap-selection
-                    // and the selection-pins show: The original tap processing may still be in
-                    // progress or may have completed and the Bar is being shown.
-                    hideContextualSearch(StateChangeReason.UNKNOWN);
-                    RecordUserAction.record("ContextualSearch.SmartSelectionSuppressed");
-                } else {
-                    showContextualSearch(StateChangeReason.TEXT_SELECT_LONG_PRESS);
-                }
+                showContextualSearch(StateChangeReason.TEXT_SELECT_LONG_PRESS);
                 mInternalStateController.notifyFinishedWorkOn(
                         InternalState.SHOWING_LONGPRESS_SEARCH);
             }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchTabHelper.java b/chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchTabHelper.java
index 0ce6425..18af8fb9 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchTabHelper.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchTabHelper.java
@@ -332,7 +332,7 @@
             controller.setSelectionClient(
                     mSelectionClientManager.addContextualSearchSelectionClient(
                             contextualSearchManager.getContextualSearchSelectionClient()));
-            contextualSearchManager.suppressContextualSearchForSmartSelection(
+            contextualSearchManager.setCouldSmartSelectionBeActive(
                     mSelectionClientManager.isSmartSelectionEnabledInChrome());
             nativeInstallUnhandledTapNotifierIfNeeded(mNativeHelper, webContents, mPxToDp);
         }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadManagerBridge.java b/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadManagerBridge.java
index 34703fd..c200116 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadManagerBridge.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadManagerBridge.java
@@ -17,12 +17,15 @@
 import org.chromium.base.Callback;
 import org.chromium.base.ContextUtils;
 import org.chromium.base.Log;
+import org.chromium.base.StrictModeContext;
 import org.chromium.base.ThreadUtils;
+import org.chromium.base.annotations.CalledByNative;
 import org.chromium.base.task.AsyncTask;
 
 import java.io.File;
 import java.lang.reflect.InvocationTargetException;
 import java.lang.reflect.Method;
+import java.util.concurrent.RejectedExecutionException;
 
 /**
  * A wrapper for Android DownloadManager to provide utility functions.
@@ -90,7 +93,9 @@
         NotificationManagerCompat notificationManager =
                 NotificationManagerCompat.from(getContext());
         boolean useSystemNotification = !notificationManager.areNotificationsEnabled();
-        long downloadId = -1;
+        long downloadId = getDownloadIdForDownloadGuid(downloadGuid);
+        if (downloadId != DownloadItem.INVALID_DOWNLOAD_ID) return downloadId;
+
         if (Build.VERSION.SDK_INT > Build.VERSION_CODES.M) {
             Class<?> c = manager.getClass();
             try {
@@ -124,6 +129,7 @@
      * @param downloadGuid The GUID of the download.
      * @param externallyRemoved If download is externally removed in other application.
      */
+    @CalledByNative
     public static void removeCompletedDownload(String downloadGuid, boolean externallyRemoved) {
         long downloadId = removeDownloadIdMapping(downloadGuid);
 
@@ -201,6 +207,13 @@
         return result;
     }
 
+    /** @return The android DownloadManager's download ID for the given download. */
+    public static long getDownloadIdForDownloadGuid(String downloadGuid) {
+        try (StrictModeContext unused = StrictModeContext.allowDiskReads()) {
+            return getSharedPreferences().getLong(downloadGuid, INVALID_SYSTEM_DOWNLOAD_ID);
+        }
+    }
+
     /**
      * Inserts a new download ID mapping into the SharedPreferences
      * @param downloadId system download ID from Android DownloadManager.
@@ -235,11 +248,6 @@
         return downloadId;
     }
 
-    /** @return The android DownloadManager's download ID for the given download. */
-    private static long getDownloadIdForDownloadGuid(String downloadGuid) {
-        return getSharedPreferences().getLong(downloadGuid, INVALID_SYSTEM_DOWNLOAD_ID);
-    }
-
     /**
      * Lazily retrieve the SharedPreferences when needed. Since download operations are not very
      * frequent, no need to load all SharedPreference entries into a hashmap in the memory.
@@ -254,6 +262,31 @@
         return ContextUtils.getApplicationContext();
     }
 
+    @CalledByNative
+    private static void addCompletedDownload(String fileName, String description, String mimeType,
+            String filePath, long fileSizeBytes, String originalUrl, String referrer,
+            String downloadGuid, long callbackId) {
+        AsyncTask<Long> task = new AsyncTask<Long>() {
+            @Override
+            protected Long doInBackground() {
+                return addCompletedDownload(fileName, description, mimeType, filePath,
+                        fileSizeBytes, originalUrl, referrer, downloadGuid);
+            }
+
+            @Override
+            protected void onPostExecute(Long downloadId) {
+                nativeOnAddCompletedDownloadDone(callbackId, downloadId);
+            }
+        };
+        try {
+            task.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
+        } catch (RejectedExecutionException e) {
+            // Reaching thread limit, update will be reschduled for the next run.
+            Log.e(TAG, "Thread limit reached, reschedule notification update later.");
+            nativeOnAddCompletedDownloadDone(callbackId, DownloadItem.INVALID_DOWNLOAD_ID);
+        }
+    }
+
     private static int getDownloadStatus(int downloadManagerStatus) {
         switch (downloadManagerStatus) {
             case DownloadManager.STATUS_SUCCESSFUL:
@@ -390,4 +423,6 @@
             mCallback.onResult(enqueueResult);
         }
     }
+
+    private static native void nativeOnAddCompletedDownloadDone(long callbackId, long downloadId);
 }
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 a0dc919..72b0684d 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
@@ -36,6 +36,7 @@
 import org.chromium.base.task.PostTask;
 import org.chromium.base.task.TaskTraits;
 import org.chromium.chrome.R;
+import org.chromium.chrome.browser.ChromeFeatureList;
 import org.chromium.chrome.browser.download.DownloadManagerBridge.DownloadEnqueueRequest;
 import org.chromium.chrome.browser.download.DownloadManagerBridge.DownloadEnqueueResponse;
 import org.chromium.chrome.browser.download.DownloadMetrics.DownloadOpenSource;
@@ -329,6 +330,8 @@
         DownloadInfo newInfo =
                 DownloadInfo.Builder.fromDownloadInfo(downloadInfo).setMimeType(mimeType).build();
         DownloadItem downloadItem = new DownloadItem(false, newInfo);
+        downloadItem.setSystemDownloadId(
+                DownloadManagerBridge.getDownloadIdForDownloadGuid(downloadInfo.getDownloadGuid()));
         updateDownloadProgress(downloadItem, status);
     }
 
@@ -537,7 +540,9 @@
             public Pair<Boolean, Boolean> doInBackground() {
                 boolean success =
                         ContentUriUtils.isContentUri(item.getDownloadInfo().getFilePath());
-                if (!success) {
+                if (!success
+                        && !ChromeFeatureList.isEnabled(
+                                ChromeFeatureList.DOWNLOAD_OFFLINE_CONTENT_PROVIDER)) {
                     long systemDownloadId = DownloadManagerBridge.addCompletedDownload(
                             info.getFileName(), info.getDescription(), info.getMimeType(),
                             info.getFilePath(), info.getBytesReceived(), info.getOriginalUrl(),
@@ -1111,6 +1116,10 @@
             removeDownloadProgress(downloadGuid);
         });
 
+        if (ChromeFeatureList.isEnabled(ChromeFeatureList.DOWNLOAD_OFFLINE_CONTENT_PROVIDER)) {
+            return;
+        }
+
         PostTask.postTask(TaskTraits.BEST_EFFORT_MAY_BLOCK, () -> {
             DownloadManagerBridge.removeCompletedDownload(downloadGuid, externallyRemoved);
         });
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/download/home/filter/chips/ChipsCoordinator.java b/chrome/android/java/src/org/chromium/chrome/browser/download/home/filter/chips/ChipsCoordinator.java
index 46e27e49..1655f40 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/download/home/filter/chips/ChipsCoordinator.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/download/home/filter/chips/ChipsCoordinator.java
@@ -13,10 +13,13 @@
 import android.view.View;
 
 import org.chromium.chrome.R;
+import org.chromium.chrome.browser.download.home.metrics.UmaUtils;
 import org.chromium.ui.modelutil.ListModel;
 import org.chromium.ui.modelutil.RecyclerViewAdapter;
 import org.chromium.ui.modelutil.SimpleRecyclerViewMcp;
 
+import java.util.List;
+
 /**
  * The coordinator responsible for managing a list of chips.  To get the {@link View} that
  * represents this coordinator use {@link #getView()}.
@@ -64,7 +67,9 @@
     // ChipsProvider.Observer implementation.
     @Override
     public void onChipsChanged() {
-        mModel.set(mProvider.getChips());
+        List<Chip> chips = mProvider.getChips();
+        mModel.set(chips);
+        UmaUtils.recordChipStats(chips.size());
     }
 
     private static RecyclerView createView(Context context) {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/download/home/list/DateOrderedListMediator.java b/chrome/android/java/src/org/chromium/chrome/browser/download/home/list/DateOrderedListMediator.java
index 5e28f18..6c58bee 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/download/home/list/DateOrderedListMediator.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/download/home/list/DateOrderedListMediator.java
@@ -185,7 +185,8 @@
         mSearchFilter.addObserver(new EmptyStateObserver(mSearchFilter, dateOrderedListObserver));
         mThumbnailProvider = new ThumbnailProviderImpl(
                 ((ChromeApplication) ContextUtils.getApplicationContext()).getReferencePool(),
-                config.inMemoryThumbnailCacheSizeBytes);
+                config.inMemoryThumbnailCacheSizeBytes,
+                ThumbnailProviderImpl.ClientType.DOWNLOAD_HOME);
         mSelectionObserver = new MediatorSelectionObserver(selectionDelegate);
 
         mModel.getProperties().set(ListProperties.ENABLE_ITEM_ANIMATIONS, true);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/download/home/list/holder/OfflineItemViewHolder.java b/chrome/android/java/src/org/chromium/chrome/browser/download/home/list/holder/OfflineItemViewHolder.java
index bd702916..52bd19a 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/download/home/list/holder/OfflineItemViewHolder.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/download/home/list/holder/OfflineItemViewHolder.java
@@ -12,9 +12,11 @@
 import android.view.View;
 import android.widget.ImageView;
 
+import org.chromium.chrome.browser.download.home.filter.Filters;
 import org.chromium.chrome.browser.download.home.list.ListItem;
 import org.chromium.chrome.browser.download.home.list.ListProperties;
 import org.chromium.chrome.browser.download.home.list.view.AsyncImageView;
+import org.chromium.chrome.browser.download.home.metrics.UmaUtils;
 import org.chromium.chrome.browser.download.home.view.SelectionView;
 import org.chromium.chrome.browser.widget.ListMenuButton;
 import org.chromium.chrome.download.R;
@@ -94,7 +96,8 @@
 
         // Push 'thumbnail' state.
         if (mThumbnail != null) {
-            mThumbnail.setImageResizer(new BitmapResizer(mThumbnail));
+            mThumbnail.setImageResizer(
+                    new BitmapResizer(mThumbnail, Filters.fromOfflineItem(offlineItem)));
             mThumbnail.setAsyncImageDrawable((consumer, width, height) -> {
                 return properties.get(ListProperties.PROVIDER_VISUALS)
                         .getVisuals(offlineItem, width, height, (id, visuals) -> {
@@ -169,9 +172,12 @@
 
         private ImageView mImageView;
 
+        private @Filters.FilterType int mFilter;
+
         /** Constructor. */
-        public BitmapResizer(ImageView imageView) {
+        public BitmapResizer(ImageView imageView, @Filters.FilterType int filter) {
             mImageView = imageView;
+            mFilter = filter;
         }
 
         @Override
@@ -207,6 +213,7 @@
             float widthRatio = (float) mImageView.getWidth() / bitmap.getWidth();
             float heightRatio = (float) mImageView.getHeight() / bitmap.getHeight();
 
+            UmaUtils.recordImageViewRequiredStretch(widthRatio, heightRatio, mFilter);
             if (Math.max(widthRatio, heightRatio) < IMAGE_VIEW_MAX_SCALE_FACTOR) return 1.f;
 
             float minRequiredScale = Math.min(widthRatio, heightRatio);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/download/home/metrics/UmaUtils.java b/chrome/android/java/src/org/chromium/chrome/browser/download/home/metrics/UmaUtils.java
index 4dfe1b0..37a5c6a 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/download/home/metrics/UmaUtils.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/download/home/metrics/UmaUtils.java
@@ -240,4 +240,28 @@
                 return "Invalid";
         }
     }
+
+    /**
+     * Records the required stretch for each dimension before rendering the image.
+     * @param requiredWidthStretch Required stretch for width.
+     * @param requiredHeightStretch Required stretch for height.
+     * @param filter The filter type of the view being shown.
+     */
+    public static void recordImageViewRequiredStretch(float requiredWidthStretch,
+            float requiredHeightStretch, @Filters.FilterType int filter) {
+        float maxRequiredStretch = Math.max(requiredWidthStretch, requiredHeightStretch);
+        RecordHistogram.recordCustomCountHistogram(
+                "Android.DownloadManager.Thumbnail.MaxRequiredStretch."
+                        + getSuffixForFilter(filter),
+                (int) (maxRequiredStretch * 100), 10, 1000, 50);
+    }
+
+    /**
+     * Records the number of chips enabled whenever the chip row is changed.
+     * @param numEnabledChips The number of chips being shown.
+     */
+    public static void recordChipStats(int numEnabledChips) {
+        RecordHistogram.recordCustomCountHistogram(
+                "Android.DownloadManager.Chips.Enabled", numEnabledChips, 1, 10, 10);
+    }
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/download/ui/DownloadManagerUi.java b/chrome/android/java/src/org/chromium/chrome/browser/download/ui/DownloadManagerUi.java
index 110a3de..27433f3 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/download/ui/DownloadManagerUi.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/download/ui/DownloadManagerUi.java
@@ -69,7 +69,8 @@
 
         DownloadBackendProvider(DiscardableReferencePool referencePool, UIDelegate uiDelegate) {
             mSelectionDelegate = new DownloadItemSelectionDelegate();
-            mThumbnailProvider = new ThumbnailProviderImpl(referencePool);
+            mThumbnailProvider = new ThumbnailProviderImpl(
+                    referencePool, ThumbnailProviderImpl.ClientType.DOWNLOAD_HOME);
             mUIDelegate = uiDelegate;
         }
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/firstrun/LightweightFirstRunActivity.java b/chrome/android/java/src/org/chromium/chrome/browser/firstrun/LightweightFirstRunActivity.java
index ba5990a..3583d107 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/firstrun/LightweightFirstRunActivity.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/firstrun/LightweightFirstRunActivity.java
@@ -4,6 +4,7 @@
 
 package org.chromium.chrome.browser.firstrun;
 
+import android.content.res.Resources;
 import android.os.Bundle;
 import android.support.annotation.StringRes;
 import android.text.method.LinkMovementMethod;
@@ -59,12 +60,13 @@
         setContentView(LayoutInflater.from(LightweightFirstRunActivity.this)
                                .inflate(R.layout.lightweight_fre_tos, null));
 
+        final Resources resources = getResources();
         NoUnderlineClickableSpan clickableTermsSpan = new NoUnderlineClickableSpan(
-                (view) -> showInfoPage(R.string.chrome_terms_of_service_url));
+                resources, (view) -> showInfoPage(R.string.chrome_terms_of_service_url));
         NoUnderlineClickableSpan clickablePrivacySpan = new NoUnderlineClickableSpan(
-                (view) -> showInfoPage(R.string.chrome_privacy_notice_url));
+                resources, (view) -> showInfoPage(R.string.chrome_privacy_notice_url));
         NoUnderlineClickableSpan clickableFamilyLinkPrivacySpan = new NoUnderlineClickableSpan(
-                (view) -> showInfoPage(R.string.family_link_privacy_policy_url));
+                resources, (view) -> showInfoPage(R.string.family_link_privacy_policy_url));
         String associatedAppName =
                 IntentUtils.safeGetStringExtra(getIntent(), EXTRA_ASSOCIATED_APP_NAME);
         if (associatedAppName == null) {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/firstrun/ToSAndUMAFirstRunFragment.java b/chrome/android/java/src/org/chromium/chrome/browser/firstrun/ToSAndUMAFirstRunFragment.java
index c5e0772f..563c5d9 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/firstrun/ToSAndUMAFirstRunFragment.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/firstrun/ToSAndUMAFirstRunFragment.java
@@ -4,6 +4,7 @@
 
 package org.chromium.chrome.browser.firstrun;
 
+import android.content.res.Resources;
 import android.os.Bundle;
 import android.support.v4.app.Fragment;
 import android.support.v4.view.ViewCompat;
@@ -90,18 +91,21 @@
 
         mTosAndPrivacy.setMovementMethod(LinkMovementMethod.getInstance());
 
-        NoUnderlineClickableSpan clickableTermsSpan = new NoUnderlineClickableSpan((view1) -> {
-            if (!isAdded()) return;
-            getPageDelegate().showInfoPage(R.string.chrome_terms_of_service_url);
-        });
+        Resources resources = getResources();
+        NoUnderlineClickableSpan clickableTermsSpan =
+                new NoUnderlineClickableSpan(resources, (view1) -> {
+                    if (!isAdded()) return;
+                    getPageDelegate().showInfoPage(R.string.chrome_terms_of_service_url);
+                });
 
-        NoUnderlineClickableSpan clickablePrivacySpan = new NoUnderlineClickableSpan((view1) -> {
-            if (!isAdded()) return;
-            getPageDelegate().showInfoPage(R.string.chrome_privacy_notice_url);
-        });
+        NoUnderlineClickableSpan clickablePrivacySpan =
+                new NoUnderlineClickableSpan(resources, (view1) -> {
+                    if (!isAdded()) return;
+                    getPageDelegate().showInfoPage(R.string.chrome_privacy_notice_url);
+                });
 
         NoUnderlineClickableSpan clickableFamilyLinkPrivacySpan =
-                new NoUnderlineClickableSpan((view1) -> {
+                new NoUnderlineClickableSpan(resources, (view1) -> {
                     if (!isAdded()) return;
                     getPageDelegate().showInfoPage(R.string.family_link_privacy_policy_url);
                 });
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/history/HistoryAdapter.java b/chrome/android/java/src/org/chromium/chrome/browser/history/HistoryAdapter.java
index 8c22653..1161a33 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/history/HistoryAdapter.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/history/HistoryAdapter.java
@@ -16,7 +16,6 @@
 import android.widget.Button;
 import android.widget.TextView;
 
-import org.chromium.base.ContextUtils;
 import org.chromium.base.VisibleForTesting;
 import org.chromium.chrome.R;
 import org.chromium.chrome.browser.UrlConstants;
@@ -275,7 +274,8 @@
         TextView privacyDisclaimerTextView =
                 privacyDisclaimerContainer.findViewById(R.id.privacy_disclaimer);
         privacyDisclaimerTextView.setMovementMethod(LinkMovementMethod.getInstance());
-        privacyDisclaimerTextView.setText(getPrivacyDisclaimerText());
+        privacyDisclaimerTextView.setText(
+                getPrivacyDisclaimerText(privacyDisclaimerTextView.getResources()));
         mPrivacyDisclaimerBottomSpace =
                 privacyDisclaimerContainer.findViewById(R.id.privacy_disclaimer_bottom_space);
 
@@ -312,9 +312,8 @@
      * Create a {@SpannableString} for privacy disclaimer.
      * @return The {@SpannableString} with the privacy disclaimer string resource and url.
      */
-    private SpannableString getPrivacyDisclaimerText() {
-        final Resources resources = ContextUtils.getApplicationContext().getResources();
-        NoUnderlineClickableSpan link = new NoUnderlineClickableSpan((view) -> {
+    private SpannableString getPrivacyDisclaimerText(Resources resources) {
+        NoUnderlineClickableSpan link = new NoUnderlineClickableSpan(resources, (view) -> {
             mHistoryManager.openUrl(UrlConstants.MY_ACTIVITY_URL_IN_HISTORY, null, true);
         });
         return SpanApplier.applySpans(
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/history/HistoryItemView.java b/chrome/android/java/src/org/chromium/chrome/browser/history/HistoryItemView.java
index a7368ecf..7e27d90 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/history/HistoryItemView.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/history/HistoryItemView.java
@@ -50,7 +50,7 @@
 
         mMinIconSize = getResources().getDimensionPixelSize(R.dimen.default_favicon_min_size);
         mDisplayedIconSize = getResources().getDimensionPixelSize(R.dimen.default_favicon_size);
-        mIconGenerator = ViewUtils.createDefaultRoundedIconGenerator(true);
+        mIconGenerator = ViewUtils.createDefaultRoundedIconGenerator(getResources(), true);
         mEndPadding = context.getResources().getDimensionPixelSize(
                 R.dimen.selectable_list_layout_row_padding);
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/infobar/AdsBlockedInfoBar.java b/chrome/android/java/src/org/chromium/chrome/browser/infobar/AdsBlockedInfoBar.java
index 8afffee..9e0e857 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/infobar/AdsBlockedInfoBar.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/infobar/AdsBlockedInfoBar.java
@@ -65,7 +65,7 @@
             description.append(learnMore);
 
             NoUnderlineClickableSpan clickableSpan =
-                    new NoUnderlineClickableSpan((view) -> onLinkClicked());
+                    new NoUnderlineClickableSpan(layout.getResources(), (view) -> onLinkClicked());
             description.setSpan(clickableSpan, spanStart, description.length(),
                     Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
             layout.getMessageLayout().addDescription(description);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/infobar/GeneratedPasswordSavedInfoBar.java b/chrome/android/java/src/org/chromium/chrome/browser/infobar/GeneratedPasswordSavedInfoBar.java
index 9d41cb7..532fe37e 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/infobar/GeneratedPasswordSavedInfoBar.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/infobar/GeneratedPasswordSavedInfoBar.java
@@ -46,7 +46,8 @@
         super.createContent(layout);
         InfoBarControlLayout detailsMessageLayout = layout.addControlLayout();
         SpannableString detailsMessageWithLink = new SpannableString(mDetailsMessage);
-        detailsMessageWithLink.setSpan(new NoUnderlineClickableSpan((view) -> onLinkClicked()),
+        detailsMessageWithLink.setSpan(
+                new NoUnderlineClickableSpan(layout.getResources(), (view) -> onLinkClicked()),
                 mInlineLinkRangeStart, mInlineLinkRangeEnd, Spanned.SPAN_INCLUSIVE_INCLUSIVE);
         detailsMessageLayout.addDescription(detailsMessageWithLink);
     }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/infobar/InfoBarCompactLayout.java b/chrome/android/java/src/org/chromium/chrome/browser/infobar/InfoBarCompactLayout.java
index 9abf8ba..ff4d7c0 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/infobar/InfoBarCompactLayout.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/infobar/InfoBarCompactLayout.java
@@ -5,6 +5,7 @@
 package org.chromium.chrome.browser.infobar;
 
 import android.content.Context;
+import android.content.res.Resources;
 import android.graphics.Bitmap;
 import android.support.annotation.StringRes;
 import android.text.SpannableString;
@@ -131,9 +132,10 @@
         public MessageBuilder withLink(@StringRes int textResId, Callback<View> onTapCallback) {
             assert mLink == null;
 
-            String label = mLayout.getResources().getString(textResId);
+            final Resources resources = mLayout.getResources();
+            String label = resources.getString(textResId);
             SpannableString link = new SpannableString(label);
-            link.setSpan(new NoUnderlineClickableSpan(onTapCallback), 0, label.length(),
+            link.setSpan(new NoUnderlineClickableSpan(resources, onTapCallback), 0, label.length(),
                     Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
             mLink = link;
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/infobar/InfoBarLayout.java b/chrome/android/java/src/org/chromium/chrome/browser/infobar/InfoBarLayout.java
index 43e725a..b6de63eb 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/infobar/InfoBarLayout.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/infobar/InfoBarLayout.java
@@ -503,7 +503,7 @@
     }
 
     private NoUnderlineClickableSpan createClickableSpan() {
-        return new NoUnderlineClickableSpan((view) -> mInfoBarView.onLinkClicked());
+        return new NoUnderlineClickableSpan(getResources(), (view) -> mInfoBarView.onLinkClicked());
     }
 
     /**
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/infobar/SurveyInfoBar.java b/chrome/android/java/src/org/chromium/chrome/browser/infobar/SurveyInfoBar.java
index 1f86c94..f47645f 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/infobar/SurveyInfoBar.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/infobar/SurveyInfoBar.java
@@ -104,12 +104,13 @@
             }
         });
 
-        NoUnderlineClickableSpan clickableSpan = new NoUnderlineClickableSpan((widget) -> {
-            // Prevent double clicking on the text span.
-            if (mClicked) return;
-            showSurvey(tab);
-            mClosedByInteraction = true;
-        });
+        NoUnderlineClickableSpan clickableSpan =
+                new NoUnderlineClickableSpan(layout.getResources(), (widget) -> {
+                    // Prevent double clicking on the text span.
+                    if (mClicked) return;
+                    showSurvey(tab);
+                    mClosedByInteraction = true;
+                });
 
         CharSequence infoBarText = SpanApplier.applySpans(mDelegate.getSurveyPromptString(),
                 new SpanInfo("<LINK>", "</LINK>", clickableSpan));
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/locale/SogouPromoDialog.java b/chrome/android/java/src/org/chromium/chrome/browser/locale/SogouPromoDialog.java
index 389455c7..3a7bd0c 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/locale/SogouPromoDialog.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/locale/SogouPromoDialog.java
@@ -51,11 +51,7 @@
     private final Callback<Boolean> mOnDismissedCallback;
 
     private final LocaleManager mLocaleManager;
-    private final ClickableSpan mSpan = new NoUnderlineClickableSpan((widget) -> {
-        mChoice = UserChoice.SETTINGS;
-        PreferencesLauncher.launchSettingsPage(getContext(), SearchEnginePreference.class);
-        dismiss();
-    });
+    private final ClickableSpan mSpan;
 
     @UserChoice
     private int mChoice = UserChoice.BACK_KEY;
@@ -67,6 +63,11 @@
             @Nullable Callback<Boolean> onDismissed) {
         super(activity);
         mLocaleManager = localeManager;
+        mSpan = new NoUnderlineClickableSpan(activity.getResources(), (widget) -> {
+            mChoice = UserChoice.SETTINGS;
+            PreferencesLauncher.launchSettingsPage(getContext(), SearchEnginePreference.class);
+            dismiss();
+        });
         setOnDismissListener(this);
         setCanceledOnTouchOutside(false);
         mOnDismissedCallback = onDismissed;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/native_page/BasicNativePage.java b/chrome/android/java/src/org/chromium/chrome/browser/native_page/BasicNativePage.java
index 26e2e42..65095db 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/native_page/BasicNativePage.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/native_page/BasicNativePage.java
@@ -4,12 +4,11 @@
 
 package org.chromium.chrome.browser.native_page;
 
-import android.app.Activity;
-import android.content.res.Resources;
 import android.view.View;
 import android.widget.FrameLayout.LayoutParams;
 
 import org.chromium.chrome.browser.ChromeActivity;
+import org.chromium.chrome.browser.fullscreen.ChromeFullscreenManager;
 import org.chromium.chrome.browser.tab.EmptyTabObserver;
 import org.chromium.chrome.browser.tab.Tab;
 import org.chromium.chrome.browser.util.ColorUtils;
@@ -19,31 +18,25 @@
 /**
  * A basic implementation of a white {@link NativePage} that docks below the toolbar.
  */
-public abstract class BasicNativePage extends EmptyTabObserver implements NativePage {
-    private final Activity mActivity;
+public abstract class BasicNativePage
+        extends EmptyTabObserver implements NativePage, ChromeFullscreenManager.FullscreenListener {
     private final NativePageHost mHost;
+    private final ChromeFullscreenManager mFullscreenManager;
     private final int mBackgroundColor;
-    private int mTopMargin;
-    private int mBottomMargin;
 
     private String mUrl;
 
     public BasicNativePage(ChromeActivity activity, NativePageHost host) {
         initialize(activity, host);
-        mActivity = activity;
         mHost = host;
         mBackgroundColor = ColorUtils.getPrimaryBackgroundColor(activity.getResources(), false);
 
-        Resources res = mActivity.getResources();
+        mFullscreenManager = activity.getFullscreenManager();
+        mFullscreenManager.addListener(this);
 
-        mBottomMargin = activity.getFullscreenManager().getBottomControlsHeight();
-        mTopMargin = activity.getFullscreenManager().getTopControlsHeight();
+        updateMargins();
 
         if (host.getActiveTab() != null) host.getActiveTab().addObserver(this);
-
-        updateMargins(host.getActiveTab() != null
-                        ? host.getActiveTab().getBrowserControlsStateConstraints()
-                        : BrowserControlsState.SHOWN);
     }
 
     /**
@@ -54,7 +47,7 @@
     @Override
     public void onBrowserControlsConstraintsUpdated(
             Tab tab, @BrowserControlsState int constraints) {
-        updateMargins(constraints);
+        updateMargins();
     }
 
     @Override
@@ -84,6 +77,21 @@
     public void destroy() {
         if (mHost.getActiveTab() == null) return;
         mHost.getActiveTab().removeObserver(this);
+        mFullscreenManager.removeListener(this);
+    }
+
+    @Override
+    public void onContentOffsetChanged(int offset) {}
+
+    @Override
+    public void onControlsOffsetChanged(int topOffset, int bottomOffset, boolean needsAnimate) {}
+
+    @Override
+    public void onToggleOverlayVideoMode(boolean enabled) {}
+
+    @Override
+    public void onBottomControlsHeightChanged(int bottomControlsHeight) {
+        updateMargins();
     }
 
     /**
@@ -99,12 +107,14 @@
     }
 
     /**
-     * Updates the top and bottom margin depending on whether the browser controls are shown or
-     * hidden.
+     * Updates the top margin depending on whether the browser controls are shown or hidden.
      */
-    private void updateMargins(@BrowserControlsState int constraints) {
-        int topMargin = mTopMargin;
-        int bottomMargin = mBottomMargin;
+    private void updateMargins() {
+        final @BrowserControlsState int constraints = mHost.getActiveTab() != null
+                ? mHost.getActiveTab().getBrowserControlsStateConstraints()
+                : BrowserControlsState.SHOWN;
+        int topMargin = mFullscreenManager.getTopControlsHeight();
+        int bottomMargin = mFullscreenManager.getBottomControlsHeight();
         if (constraints == BrowserControlsState.HIDDEN) {
             topMargin = 0;
             bottomMargin = 0;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ntp/IncognitoNewTabPageView.java b/chrome/android/java/src/org/chromium/chrome/browser/ntp/IncognitoNewTabPageView.java
index 0e02ddb..361da50 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/ntp/IncognitoNewTabPageView.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/ntp/IncognitoNewTabPageView.java
@@ -341,7 +341,7 @@
         concatenatedText.append(getContext().getResources().getString(R.string.learn_more));
         SpannableString textWithLearnMoreLink = new SpannableString(concatenatedText.toString());
 
-        NoUnderlineClickableSpan span = new NoUnderlineClickableSpan(
+        NoUnderlineClickableSpan span = new NoUnderlineClickableSpan(getResources(),
                 R.color.modern_blue_300, (view) -> getManager().loadIncognitoLearnMore());
         textWithLearnMoreLink.setSpan(
                 span, subtitleText.length() + 1, textWithLearnMoreLink.length(), 0 /* flags */);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ntp/NewTabPage.java b/chrome/android/java/src/org/chromium/chrome/browser/ntp/NewTabPage.java
index d741626a..7cc6f82a 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/ntp/NewTabPage.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/ntp/NewTabPage.java
@@ -30,6 +30,7 @@
 import org.chromium.chrome.browser.UrlConstants;
 import org.chromium.chrome.browser.compositor.layouts.content.InvalidationAwareThumbnailProvider;
 import org.chromium.chrome.browser.download.DownloadManagerService;
+import org.chromium.chrome.browser.fullscreen.ChromeFullscreenManager;
 import org.chromium.chrome.browser.native_page.NativePage;
 import org.chromium.chrome.browser.native_page.NativePageHost;
 import org.chromium.chrome.browser.ntp.NewTabPageView.NewTabPageManager;
@@ -55,7 +56,6 @@
 import org.chromium.chrome.browser.tab.TabObserver;
 import org.chromium.chrome.browser.tabmodel.TabModelSelector;
 import org.chromium.chrome.browser.tabmodel.TabSelectionType;
-import org.chromium.chrome.browser.util.FeatureUtilities;
 import org.chromium.chrome.browser.util.UrlUtilities;
 import org.chromium.chrome.browser.vr.VrModuleProvider;
 import org.chromium.content_public.browser.NavigationController;
@@ -71,8 +71,9 @@
 /**
  * Provides functionality when the user interacts with the NTP.
  */
-public class NewTabPage
-        implements NativePage, InvalidationAwareThumbnailProvider, TemplateUrlServiceObserver {
+public class NewTabPage implements NativePage, InvalidationAwareThumbnailProvider,
+                                   TemplateUrlServiceObserver,
+                                   ChromeFullscreenManager.FullscreenListener {
     private static final String TAG = "NewTabPage";
 
     // Key for the scroll position data that may be stored in a navigation entry.
@@ -86,6 +87,7 @@
     protected final NewTabPageManagerImpl mNewTabPageManager;
     protected final TileGroup.Delegate mTileGroupDelegate;
     private final boolean mIsTablet;
+    private final ChromeFullscreenManager mFullscreenManager;
 
     /**
      * The {@link NewTabPageView} shown in this NewTabPageLayout. This may be null in sub-classes.
@@ -109,6 +111,20 @@
     // Whether destroy() has been called.
     private boolean mIsDestroyed;
 
+    @Override
+    public void onContentOffsetChanged(int offset) {}
+
+    @Override
+    public void onControlsOffsetChanged(int topOffset, int bottomOffset, boolean needsAnimate) {}
+
+    @Override
+    public void onToggleOverlayVideoMode(boolean enabled) {}
+
+    @Override
+    public void onBottomControlsHeightChanged(int bottomControlsHeight) {
+        updateMargins();
+    }
+
     /**
      * Allows clients to listen for updates to the scroll changes of the search box on the
      * NTP.
@@ -314,23 +330,26 @@
 
             @Override
             public void onBrowserControlsConstraintsUpdated(Tab tab, int constraints) {
-                updateMargins(constraints);
+                updateMargins();
             }
         };
         mTab.addObserver(mTabObserver);
         updateSearchProviderHasLogo();
 
         initializeMainView(activity);
+
+        mFullscreenManager = activity.getFullscreenManager();
         getView().addOnAttachStateChangeListener(new View.OnAttachStateChangeListener() {
             @Override
             public void onViewAttachedToWindow(View view) {
-                updateMargins(mTab.getBrowserControlsStateConstraints());
+                updateMargins();
                 getView().removeOnAttachStateChangeListener(this);
             }
 
             @Override
             public void onViewDetachedFromWindow(View view) {}
         });
+        mFullscreenManager.addListener(this);
 
         eventReporter.onSurfaceOpened();
 
@@ -383,8 +402,11 @@
                 index, NAVIGATION_ENTRY_SCROLL_POSITION_KEY, Integer.toString(scrollPosition));
     }
 
-    /** Update the margins for the content when browser controls constraints are changed. */
-    private void updateMargins(@BrowserControlsState int constraints) {
+    /**
+     * Update the margins for the content when browser controls constraints or bottom control
+     *  height are changed.
+     */
+    private void updateMargins() {
         // TODO(mdjones): can this be merged with BasicNativePage's updateMargins?
 
         View view = getView();
@@ -392,12 +414,17 @@
                 ((ViewGroup.MarginLayoutParams) view.getLayoutParams());
         if (layoutParams == null) return;
 
-        int bottomMargin = 0;
-        if (FeatureUtilities.isBottomToolbarEnabled()
-                && constraints != BrowserControlsState.HIDDEN) {
-            bottomMargin = mTab.getActivity().getFullscreenManager().getBottomControlsHeight();
-        }
-        layoutParams.bottomMargin = bottomMargin;
+        final @BrowserControlsState int constraints = mTab.getBrowserControlsStateConstraints();
+        layoutParams.bottomMargin = (constraints != BrowserControlsState.HIDDEN)
+                ? mFullscreenManager.getBottomControlsHeight()
+                : 0;
+
+        // Apply negative margin to the top of the N logo (which would otherwise be the height of
+        // the top toolbar) when Duet is enabled to remove some of the empty space.
+        layoutParams.topMargin = (layoutParams.bottomMargin == 0)
+                ? 0
+                : -view.getResources().getDimensionPixelSize(R.dimen.duet_ntp_logo_top_margin);
+        view.setLayoutParams(layoutParams);
     }
 
     /** @return The view container for the new tab page. */
@@ -604,6 +631,7 @@
         TemplateUrlService.getInstance().removeObserver(this);
         mTab.removeObserver(mTabObserver);
         mTabObserver = null;
+        mFullscreenManager.removeListener(this);
         mIsDestroyed = true;
     }
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ntp/NewTabPageLayout.java b/chrome/android/java/src/org/chromium/chrome/browser/ntp/NewTabPageLayout.java
index dfbef1b..881501c 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/ntp/NewTabPageLayout.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/ntp/NewTabPageLayout.java
@@ -194,13 +194,6 @@
             exploreStub.setLayoutResource(R.layout.experimental_explore_sites_section);
             mExploreSectionView = exploreStub.inflate();
         }
-
-        // Apply negative margin to the top of the N logo (which would otherwise be the height of
-        // the top toolbar) when Duet is enabled to remove some of the empty space.
-        if (FeatureUtilities.isBottomToolbarEnabled()) {
-            ((MarginLayoutParams) mSearchProviderLogoView.getLayoutParams()).topMargin =
-                    -getResources().getDimensionPixelSize(R.dimen.duet_ntp_logo_top_margin);
-        }
     }
 
     /**
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ntp/RecentTabsPage.java b/chrome/android/java/src/org/chromium/chrome/browser/ntp/RecentTabsPage.java
index e03d793..c3bddc4 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/ntp/RecentTabsPage.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/ntp/RecentTabsPage.java
@@ -24,6 +24,7 @@
 import org.chromium.chrome.browser.ChromeActivity;
 import org.chromium.chrome.browser.UrlConstants;
 import org.chromium.chrome.browser.compositor.layouts.content.InvalidationAwareThumbnailProvider;
+import org.chromium.chrome.browser.fullscreen.ChromeFullscreenManager;
 import org.chromium.chrome.browser.native_page.NativePage;
 import org.chromium.chrome.browser.util.ViewUtils;
 
@@ -37,8 +38,9 @@
                    ExpandableListView.OnGroupCollapseListener,
                    ExpandableListView.OnGroupExpandListener, RecentTabsManager.UpdatedCallback,
                    View.OnAttachStateChangeListener, View.OnCreateContextMenuListener,
-                   InvalidationAwareThumbnailProvider {
+                   InvalidationAwareThumbnailProvider, ChromeFullscreenManager.FullscreenListener {
     private final Activity mActivity;
+    private final ChromeFullscreenManager mFullscreenManager;
     private final ExpandableListView mListView;
     private final String mTitle;
     private final ViewGroup mView;
@@ -97,14 +99,9 @@
         ApplicationStatus.registerStateListenerForActivity(this, activity);
         // {@link #mInForeground} will be updated once the view is attached to the window.
 
-        View recentTabsRoot = mView.findViewById(R.id.recent_tabs_root);
-        if (activity.getFullscreenManager().getBottomControlsHeight() != 0) {
-            ViewCompat.setPaddingRelative(recentTabsRoot,
-                    ViewCompat.getPaddingStart(recentTabsRoot),
-                    activity.getFullscreenManager().getTopControlsHeight(),
-                    ViewCompat.getPaddingEnd(recentTabsRoot),
-                    activity.getFullscreenManager().getBottomControlsHeight());
-        }
+        mFullscreenManager = activity.getFullscreenManager();
+        mFullscreenManager.addListener(this);
+        onBottomControlsHeightChanged(mFullscreenManager.getBottomControlsHeight());
 
         onUpdated();
     }
@@ -174,6 +171,7 @@
 
         mView.removeOnAttachStateChangeListener(this);
         ApplicationStatus.unregisterActivityStateListener(this);
+        mFullscreenManager.removeListener(this);
     }
 
     @Override
@@ -288,4 +286,21 @@
         mSnapshotWidth = mView.getWidth();
         mSnapshotHeight = mView.getHeight();
     }
+
+    @Override
+    public void onContentOffsetChanged(int offset) {}
+
+    @Override
+    public void onControlsOffsetChanged(int topOffset, int bottomOffset, boolean needsAnimate) {}
+
+    @Override
+    public void onToggleOverlayVideoMode(boolean enabled) {}
+
+    @Override
+    public void onBottomControlsHeightChanged(int bottomControlsHeight) {
+        final View recentTabsRoot = mView.findViewById(R.id.recent_tabs_root);
+        ViewCompat.setPaddingRelative(recentTabsRoot, ViewCompat.getPaddingStart(recentTabsRoot),
+                mFullscreenManager.getTopControlsHeight(), ViewCompat.getPaddingEnd(recentTabsRoot),
+                bottomControlsHeight);
+    }
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ntp/RecentTabsRowAdapter.java b/chrome/android/java/src/org/chromium/chrome/browser/ntp/RecentTabsRowAdapter.java
index de9517c..e2c1a51 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/ntp/RecentTabsRowAdapter.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/ntp/RecentTabsRowAdapter.java
@@ -676,7 +676,7 @@
         mDefaultFaviconHelper = new DefaultFaviconHelper();
         mFaviconSize = resources.getDimensionPixelSize(R.dimen.default_favicon_size);
 
-        mIconGenerator = ViewUtils.createDefaultRoundedIconGenerator(true);
+        mIconGenerator = ViewUtils.createDefaultRoundedIconGenerator(activity.getResources(), true);
 
         RecordHistogram.recordEnumeratedHistogram("HistoryPage.OtherDevicesMenu",
                 OtherSessionsActions.MENU_INITIALIZED, OtherSessionsActions.NUM_ENTRIES);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ntp/cards/Footer.java b/chrome/android/java/src/org/chromium/chrome/browser/ntp/cards/Footer.java
index 57bd1be..daab198 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/ntp/cards/Footer.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/ntp/cards/Footer.java
@@ -47,8 +47,8 @@
             super(LayoutInflater.from(root.getContext())
                             .inflate(R.layout.new_tab_page_footer, root, false));
 
-            NoUnderlineClickableSpan link =
-                    new NoUnderlineClickableSpan((view) -> navigationDelegate.navigateToHelpPage());
+            NoUnderlineClickableSpan link = new NoUnderlineClickableSpan(
+                    root.getResources(), (view) -> navigationDelegate.navigateToHelpPage());
 
             TextView textView = (TextView) itemView.findViewById(R.id.text);
             textView.setText(SpanApplier.applySpans(
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/answer/AnswerText.java b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/answer/AnswerText.java
index 34831aa..4d0b334 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/answer/AnswerText.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/answer/AnswerText.java
@@ -93,6 +93,10 @@
      */
     @SuppressWarnings("deprecation") // Update usage of Html.fromHtml when API min is 24
     protected void appendAndStyleText(String text, MetricAffectingSpan[] styles) {
+        // Unescape HTML entities (e.g. "&quot;", "&gt;").
+        text = Html.fromHtml(text).toString();
+        text = processAnswerText(text);
+
         // Determine the maximum height of the TextAppearanceSpans that are applied for this field.
         for (MetricAffectingSpan style : styles) {
             if (!(style instanceof TextAppearanceSpan)) continue;
@@ -101,12 +105,9 @@
             if (mHeightSp < textHeightSp) mHeightSp = textHeightSp;
         }
 
-        // Unescape HTML entities (e.g. "&quot;", "&gt;").
-        text = Html.fromHtml(text).toString();
-
         // Append as HTML (answer responses contain simple markup).
         int start = mText.length();
-        mText.append(Html.fromHtml(text));
+        mText.append(text);
         int end = mText.length();
 
         for (MetricAffectingSpan style : styles) {
@@ -121,4 +122,14 @@
      * @return TextAppearanceSpan array specifying styles to be used to present text field.
      */
     protected abstract MetricAffectingSpan[] getAppearanceForText(@AnswerTextType int type);
+
+    /**
+     * Process (if desired) content of the answer text.
+     *
+     * @param text Source text.
+     * @return Either original or modified text.
+     */
+    protected String processAnswerText(String text) {
+        return text;
+    }
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/answer/AnswerTextNewLayout.java b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/answer/AnswerTextNewLayout.java
index 5693c6ef..7c1aee4 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/answer/AnswerTextNewLayout.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/answer/AnswerTextNewLayout.java
@@ -23,6 +23,7 @@
 class AnswerTextNewLayout extends AnswerText {
     private static final String TAG = "AnswerTextNewLayout";
     private final boolean mIsAnswer;
+    private final @AnswerType int mAnswerType;
 
     /**
      * Convert SuggestionAnswer to array of elements that directly translate to user-presented
@@ -44,12 +45,16 @@
             result[0] = new AnswerTextNewLayout(context, suggestion.getFillIntoEdit(), true);
             result[1] = new AnswerTextNewLayout(context, query, false);
         } else if (answer.getType() == AnswerType.DICTIONARY) {
-            result[0] = new AnswerTextNewLayout(context, answer.getFirstLine(), true);
-            result[1] = new AnswerTextNewLayout(context, answer.getSecondLine(), false);
+            result[0] =
+                    new AnswerTextNewLayout(context, answer.getType(), answer.getFirstLine(), true);
+            result[1] = new AnswerTextNewLayout(
+                    context, answer.getType(), answer.getSecondLine(), false);
             result[0].mMaxLines = 1;
         } else {
-            result[0] = new AnswerTextNewLayout(context, answer.getSecondLine(), true);
-            result[1] = new AnswerTextNewLayout(context, answer.getFirstLine(), false);
+            result[0] = new AnswerTextNewLayout(
+                    context, answer.getType(), answer.getSecondLine(), true);
+            result[1] = new AnswerTextNewLayout(
+                    context, answer.getType(), answer.getFirstLine(), false);
             result[1].mMaxLines = 1;
         }
 
@@ -60,12 +65,15 @@
      * Create new instance of AnswerTextNewLayout for answer suggestions.
      *
      * @param context Current context.
+     * @param type Answer type, eg. AnswerType.WEATHER.
      * @param line Suggestion line that will be converted to Answer Text.
      * @param isAnswerLine True, if this instance holds answer.
      */
-    AnswerTextNewLayout(Context context, SuggestionAnswer.ImageLine line, boolean isAnswerLine) {
+    AnswerTextNewLayout(Context context, @AnswerType int type, SuggestionAnswer.ImageLine line,
+            boolean isAnswerLine) {
         super(context);
         mIsAnswer = isAnswerLine;
+        mAnswerType = type;
         build(line);
     }
 
@@ -78,10 +86,32 @@
     AnswerTextNewLayout(Context context, String text, boolean isAnswerLine) {
         super(context);
         mIsAnswer = isAnswerLine;
+        mAnswerType = AnswerType.INVALID;
         appendAndStyleText(text, getAppearanceForText(AnswerTextType.SUGGESTION));
     }
 
     /**
+     * Process (if desired) content of the answer text.
+     *
+     * @param text Source text.
+     * @return Either original or modified text.
+     */
+    @Override
+    protected String processAnswerText(String text) {
+        if (mIsAnswer && mAnswerType == AnswerType.CURRENCY) {
+            // Modify the content of answer to present only the value after conversion, that is:
+            //    1,000 United State Dollar = 1,330.75 Canadian Dollar
+            // becomes
+            //    1,330.75 Canadian Dollar
+            int offset = text.indexOf(" = ");
+            if (offset > 0) {
+                text = text.substring(offset + 3);
+            }
+        }
+        return text;
+    }
+
+    /**
      * Return the TextAppearanceSpan array specifying text decorations for a given field type.
      *
      * @param type The answer type as specified at http://goto.google.com/ais_api.
@@ -99,9 +129,13 @@
      * @return array of TextAppearanceSpan objects defining style for the text.
      */
     private MetricAffectingSpan[] getAppearanceForAnswerText(@AnswerTextType int type) {
+        if (mAnswerType != AnswerType.DICTIONARY && mAnswerType != AnswerType.FINANCE) {
+            return new TextAppearanceSpan[] {
+                    new TextAppearanceSpan(mContext, R.style.TextAppearance_BlackTitle1)};
+        }
+
         @StyleRes
         int res = 0;
-
         switch (type) {
             case AnswerTextType.DESCRIPTION_NEGATIVE:
                 res = R.style.TextAppearance_OmniboxAnswerDescriptionNegativeSmall;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/page_info/PageInfoController.java b/chrome/android/java/src/org/chromium/chrome/browser/page_info/PageInfoController.java
index 0fd69a2..a88de0d 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/page_info/PageInfoController.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/page_info/PageInfoController.java
@@ -346,7 +346,7 @@
                             // The callback given to NoUnderlineClickableSpan is overridden in
                             // PageInfoView so use previewShowOriginalClickCallback (above) instead
                             // because the entire TextView will be clickable.
-                            new NoUnderlineClickableSpan((view) -> {})));
+                            new NoUnderlineClickableSpan(mContext.getResources(), (view) -> {})));
             viewParams.previewLoadOriginalMessage = loadOriginalSpan;
 
             viewParams.previewStaleTimestamp =
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/payments/ui/PaymentRequestUI.java b/chrome/android/java/src/org/chromium/chrome/browser/payments/ui/PaymentRequestUI.java
index 908c09c..b48db34 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/payments/ui/PaymentRequestUI.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/payments/ui/PaymentRequestUI.java
@@ -1023,8 +1023,8 @@
             message = mContext.getString(R.string.payments_card_and_address_settings_signed_out);
         }
 
-        NoUnderlineClickableSpan settingsSpan =
-                new NoUnderlineClickableSpan((widget) -> mClient.onCardAndAddressSettingsClicked());
+        NoUnderlineClickableSpan settingsSpan = new NoUnderlineClickableSpan(
+                mContext.getResources(), (widget) -> mClient.onCardAndAddressSettingsClicked());
         SpannableString spannableMessage = SpanApplier.applySpans(
                 message, new SpanInfo("BEGIN_LINK", "END_LINK", settingsSpan));
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/preferences/ChromePreferenceManager.java b/chrome/android/java/src/org/chromium/chrome/browser/preferences/ChromePreferenceManager.java
index 6fa5b572..ace4e63 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/preferences/ChromePreferenceManager.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/preferences/ChromePreferenceManager.java
@@ -186,6 +186,12 @@
     public static final String BOTTOM_TOOLBAR_ENABLED_KEY = "bottom_toolbar_enabled";
 
     /**
+     * Whether or not the adaptive toolbar is enabled.
+     * Default value is true.
+     */
+    public static final String ADAPTIVE_TOOLBAR_ENABLED_KEY = "adaptive_toolbar_enabled";
+
+    /**
      * Whether or not night mode is available.
      * Default value is false.
      */
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/preferences/ClearBrowsingDataCheckBoxPreference.java b/chrome/android/java/src/org/chromium/chrome/browser/preferences/ClearBrowsingDataCheckBoxPreference.java
index cd62745b..1d65db3 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/preferences/ClearBrowsingDataCheckBoxPreference.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/preferences/ClearBrowsingDataCheckBoxPreference.java
@@ -123,8 +123,8 @@
         }
         // Linkify <link></link> span.
         final SpannableString summaryWithLink = SpanApplier.applySpans(summaryString,
-                new SpanApplier.SpanInfo(
-                        "<link>", "</link>", new NoUnderlineClickableSpan((widget) -> {
+                new SpanApplier.SpanInfo("<link>", "</link>",
+                        new NoUnderlineClickableSpan(getContext().getResources(), (widget) -> {
                             if (mLinkClickDelegate != null) mLinkClickDelegate.run();
                         })));
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/preferences/ContextualSuggestionsPreference.java b/chrome/android/java/src/org/chromium/chrome/browser/preferences/ContextualSuggestionsPreference.java
index a7c5e78..ee5fa36 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/preferences/ContextualSuggestionsPreference.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/preferences/ContextualSuggestionsPreference.java
@@ -90,26 +90,28 @@
         if (!isUnifiedConsentEnabled || !isSignedIn
                 || (!ProfileSyncService.get().isUrlKeyedDataCollectionEnabled(false)
                            && !ProfileSyncService.get().isUrlKeyedDataCollectionEnabled(true))) {
-            final NoUnderlineClickableSpan span = new NoUnderlineClickableSpan((widget) -> {
-                if (isUnifiedConsentEnabled) {
-                    if (isSignedIn) {
-                        PreferencesLauncher.launchSettingsPage(context,
-                                SyncAndServicesPreferences.class,
-                                SyncAndServicesPreferences.createArguments(false));
-                    } else {
-                        startActivity(SigninActivity.createIntentForPromoChooseAccountFlow(
-                                context, SigninAccessPoint.SETTINGS, null));
-                    }
-                } else {
-                    if (isSignedIn) {
-                        PreferencesLauncher.launchSettingsPage(
-                                context, SyncCustomizationFragment.class);
-                    } else {
-                        startActivity(AccountSigninActivity.createIntentForDefaultSigninFlow(
-                                context, SigninAccessPoint.SETTINGS, false));
-                    }
-                }
-            });
+            final NoUnderlineClickableSpan span =
+                    new NoUnderlineClickableSpan(context.getResources(), (widget) -> {
+                        if (isUnifiedConsentEnabled) {
+                            if (isSignedIn) {
+                                PreferencesLauncher.launchSettingsPage(context,
+                                        SyncAndServicesPreferences.class,
+                                        SyncAndServicesPreferences.createArguments(false));
+                            } else {
+                                startActivity(SigninActivity.createIntentForPromoChooseAccountFlow(
+                                        context, SigninAccessPoint.SETTINGS, null));
+                            }
+                        } else {
+                            if (isSignedIn) {
+                                PreferencesLauncher.launchSettingsPage(
+                                        context, SyncCustomizationFragment.class);
+                            } else {
+                                startActivity(
+                                        AccountSigninActivity.createIntentForDefaultSigninFlow(
+                                                context, SigninAccessPoint.SETTINGS, false));
+                            }
+                        }
+                    });
             final SpannableString spannable = SpanApplier.applySpans(
                     getResources().getString(isUnifiedConsentEnabled
                                     ? R.string.contextual_suggestions_message_unified_consent
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/preferences/SearchEnginePreference.java b/chrome/android/java/src/org/chromium/chrome/browser/preferences/SearchEnginePreference.java
index 966f4713..1c9205f5 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/preferences/SearchEnginePreference.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/preferences/SearchEnginePreference.java
@@ -47,6 +47,7 @@
         mListView = (ListView) getView().findViewById(android.R.id.list);
         mListView.setAdapter(mSearchEngineAdapter);
         mListView.setDivider(null);
+        mListView.setItemsCanFocus(true);
     }
 
     @Override
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/preferences/privacy/ConfirmImportantSitesDialogFragment.java b/chrome/android/java/src/org/chromium/chrome/browser/preferences/privacy/ConfirmImportantSitesDialogFragment.java
index 1b145f3..43718caf 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/preferences/privacy/ConfirmImportantSitesDialogFragment.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/preferences/privacy/ConfirmImportantSitesDialogFragment.java
@@ -65,7 +65,7 @@
             mDomains = domains;
             mFaviconURLs = faviconURLs;
             mFaviconSize = resources.getDimensionPixelSize(R.dimen.default_favicon_size);
-            mIconGenerator = ViewUtils.createDefaultRoundedIconGenerator(false);
+            mIconGenerator = ViewUtils.createDefaultRoundedIconGenerator(getResources(), false);
         }
 
         @Override
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/preferences/privacy/OtherFormsOfHistoryDialogFragment.java b/chrome/android/java/src/org/chromium/chrome/browser/preferences/privacy/OtherFormsOfHistoryDialogFragment.java
index 2ea0c118..fec6b3a 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/preferences/privacy/OtherFormsOfHistoryDialogFragment.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/preferences/privacy/OtherFormsOfHistoryDialogFragment.java
@@ -55,8 +55,8 @@
         // Linkify the <link></link> span in the dialog text.
         TextView textView = (TextView) view.findViewById(R.id.text);
         final SpannableString textWithLink = SpanApplier.applySpans(textView.getText().toString(),
-                new SpanApplier.SpanInfo(
-                        "<link>", "</link>", new NoUnderlineClickableSpan((widget) -> {
+                new SpanApplier.SpanInfo("<link>", "</link>",
+                        new NoUnderlineClickableSpan(getResources(), (widget) -> {
                             new TabDelegate(false /* incognito */)
                                     .launchUrl(UrlConstants.MY_ACTIVITY_URL_IN_CBD_NOTICE,
                                             TabLaunchType.FROM_CHROME_UI);
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 5740a2ae1..a353be3 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
@@ -100,11 +100,12 @@
             preferenceScreen.addPreference(networkPredictionPref);
 
             Preference syncAndServicesLink = findPreference(PREF_SYNC_AND_SERVICES_LINK);
-            NoUnderlineClickableSpan linkSpan = new NoUnderlineClickableSpan(view -> {
-                PreferencesLauncher.launchSettingsPage(getActivity(),
-                        SyncAndServicesPreferences.class,
-                        SyncAndServicesPreferences.createArguments(false));
-            });
+            NoUnderlineClickableSpan linkSpan =
+                    new NoUnderlineClickableSpan(getResources(), view -> {
+                        PreferencesLauncher.launchSettingsPage(getActivity(),
+                                SyncAndServicesPreferences.class,
+                                SyncAndServicesPreferences.createArguments(false));
+                    });
             syncAndServicesLink.setSummary(
                     SpanApplier.applySpans(getString(R.string.privacy_sync_and_services_link),
                             new SpanApplier.SpanInfo("<link>", "</link>", linkSpan)));
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/signin/AccountSigninView.java b/chrome/android/java/src/org/chromium/chrome/browser/signin/AccountSigninView.java
index 530c9d4..98c1986 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/signin/AccountSigninView.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/signin/AccountSigninView.java
@@ -341,15 +341,17 @@
         mConsentTextTracker.setText(mMoreButton, R.string.more);
 
         // The clickable "Settings" link.
-        NoUnderlineClickableSpan settingsSpan = new NoUnderlineClickableSpan((widget) -> {
-            if (mSelectedAccountName == null) return;
-            mListener.onAccountSelected(mSelectedAccountName, mIsDefaultAccountSelected, true);
-            RecordUserAction.record("Signin_Signin_WithAdvancedSyncSettings");
+        NoUnderlineClickableSpan settingsSpan =
+                new NoUnderlineClickableSpan(getResources(), (widget) -> {
+                    if (mSelectedAccountName == null) return;
+                    mListener.onAccountSelected(
+                            mSelectedAccountName, mIsDefaultAccountSelected, true);
+                    RecordUserAction.record("Signin_Signin_WithAdvancedSyncSettings");
 
-            // Record the fact that the user consented to the consent text by clicking
-            // on |mSigninSettingsControl|.
-            recordConsent((TextView) widget, mSelectedAccountName);
-        });
+                    // Record the fact that the user consented to the consent text by clicking
+                    // on |mSigninSettingsControl|.
+                    recordConsent((TextView) widget, mSelectedAccountName);
+                });
         mConsentTextTracker.setText(mSigninSettingsControl,
                 getSettingsControlDescription(mChildAccountStatus), input -> {
                     return SpanApplier.applySpans(input.toString(),
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/signin/SigninFragmentBase.java b/chrome/android/java/src/org/chromium/chrome/browser/signin/SigninFragmentBase.java
index ab28e3c4..211e5b9 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/signin/SigninFragmentBase.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/signin/SigninFragmentBase.java
@@ -320,8 +320,9 @@
     }
 
     private void updateSigninDetailsDescription(boolean addSettingsLink) {
-        final @Nullable Object settingsLinkSpan =
-                addSettingsLink ? new NoUnderlineClickableSpan(this::onSettingsLinkClicked) : null;
+        final @Nullable Object settingsLinkSpan = addSettingsLink
+                ? new NoUnderlineClickableSpan(getResources(), this::onSettingsLinkClicked)
+                : null;
         final SpanApplier.SpanInfo spanInfo =
                 new SpanApplier.SpanInfo(SETTINGS_LINK_OPEN, SETTINGS_LINK_CLOSE, settingsLinkSpan);
         mConsentTextTracker.setText(mView.getDetailsDescriptionView(),
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/suggestions/SuggestionsDependencyFactory.java b/chrome/android/java/src/org/chromium/chrome/browser/suggestions/SuggestionsDependencyFactory.java
index 47748db6..1aec6f0 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/suggestions/SuggestionsDependencyFactory.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/suggestions/SuggestionsDependencyFactory.java
@@ -62,7 +62,8 @@
     }
 
     public ThumbnailProvider createThumbnailProvider(DiscardableReferencePool referencePool) {
-        return new ThumbnailProviderImpl(referencePool);
+        return new ThumbnailProviderImpl(
+                referencePool, ThumbnailProviderImpl.ClientType.NTP_SUGGESTIONS);
     }
 
     public FaviconHelper createFaviconHelper() {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/tab/SadTab.java b/chrome/android/java/src/org/chromium/chrome/browser/tab/SadTab.java
index f0b7cd0..6b7385d 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/tab/SadTab.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/tab/SadTab.java
@@ -218,10 +218,11 @@
      */
     private static CharSequence getHelpMessage(
             Context context, final Runnable suggestionAction, final boolean showSendFeedback) {
-        NoUnderlineClickableSpan linkSpan = new NoUnderlineClickableSpan((view) -> {
-            recordEvent(showSendFeedback, SadTabEvent.HELP_LINK_CLICKED);
-            suggestionAction.run();
-        });
+        NoUnderlineClickableSpan linkSpan =
+                new NoUnderlineClickableSpan(context.getResources(), (view) -> {
+                    recordEvent(showSendFeedback, SadTabEvent.HELP_LINK_CLICKED);
+                    suggestionAction.run();
+                });
 
         if (showSendFeedback) {
             SpannableString learnMoreLink =
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/tab/Tab.java b/chrome/android/java/src/org/chromium/chrome/browser/tab/Tab.java
index bf9477f..ee3de8d6 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/tab/Tab.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/tab/Tab.java
@@ -8,8 +8,7 @@
 import android.app.Activity;
 import android.content.Context;
 import android.content.Intent;
-import android.content.res.Resources;
-import android.graphics.Bitmap;
+import android.content.res.Configuration;
 import android.graphics.Color;
 import android.graphics.Rect;
 import android.net.Uri;
@@ -72,6 +71,8 @@
 import org.chromium.chrome.browser.tabmodel.TabReparentingParams;
 import org.chromium.chrome.browser.tabmodel.TabSelectionType;
 import org.chromium.chrome.browser.vr.VrModuleProvider;
+import org.chromium.components.content_capture.ContentCaptureFeatures;
+import org.chromium.components.content_capture.ContentCaptureReceiverManager;
 import org.chromium.components.dom_distiller.core.DomDistillerUrlUtils;
 import org.chromium.components.embedder_support.view.ContentView;
 import org.chromium.components.navigation_interception.InterceptNavigationDelegate;
@@ -182,17 +183,6 @@
     private boolean mIsClosing;
     private boolean mIsShowingErrorPage;
 
-    private Bitmap mFavicon;
-    private int mFaviconWidth;
-    private int mFaviconHeight;
-    private String mFaviconUrl;
-
-    /**
-     * The size in pixels at which favicons will be drawn. Ideally mFavicon will have this size to
-     * avoid scaling artifacts.
-     */
-    private int mIdealFaviconSize;
-
     /** Whether or not the TabState has changed. */
     private boolean mIsTabStateDirty = true;
 
@@ -303,11 +293,6 @@
 
     private FullscreenManager mFullscreenManager;
 
-    /**
-     * The publisher URL for pages hosted on a trusted CDN, or null otherwise.
-     */
-    private @Nullable String mTrustedCdnPublisherUrl;
-
     /** The current browser controls constraints. -1 if not set. */
     private @BrowserControlsState int mBrowserConstrolsConstraints = -1;
 
@@ -375,15 +360,24 @@
         mId = TabIdManager.getInstance().generateValidId(id);
         mParentId = parentId;
         mIncognito = incognito;
-        mThemedApplicationContext = new ContextThemeWrapper(
+
+        // Override the configuration for night mode to always stay in light mode until all UIs in
+        // Tab are inflated from activity context instead of application context. This is to avoid
+        // getting the wrong night mode state when application context inherits a system UI mode
+        // different from the UI mode we need.
+        // TODO(https://crbug.com/938641): Remove this once Tab UIs are all inflated from activity.
+        ContextThemeWrapper themeWrapper = new ContextThemeWrapper(
                 ContextUtils.getApplicationContext(), ChromeActivity.getThemeId());
+        Configuration config = new Configuration();
+        config.uiMode = Configuration.UI_MODE_NIGHT_NO
+                | (config.uiMode & ~Configuration.UI_MODE_NIGHT_MASK);
+        themeWrapper.applyOverrideConfiguration(config);
+        mThemedApplicationContext = themeWrapper;
+
         mWindowAndroid = window;
         mLaunchType = type;
         mLaunchTypeAtCreation = type;
 
-        Resources resources = mThemedApplicationContext.getResources();
-        mIdealFaviconSize = resources.getDimensionPixelSize(R.dimen.default_favicon_size);
-
         TabThemeColorHelper.createForTab(this);
 
         // Restore data from the TabState, if it existed.
@@ -1406,6 +1400,7 @@
             updateInteractableState();
             mWebContentsDelegate = mDelegateFactory.createWebContentsDelegate(this);
             TabWebContentsObserver.from(this);
+            TabFavicon.from(this);
 
             int parentId = getParentId();
             if (parentId != INVALID_TAB_ID) {
@@ -1430,6 +1425,7 @@
             InfoBarContainer.from(this);
 
             SwipeRefreshHandler.from(this);
+            TrustedCdn.from(this);
 
             notifyContentChanged();
 
@@ -1456,6 +1452,10 @@
 
             setInterceptNavigationDelegate(mDelegateFactory.createInterceptNavigationDelegate(
                     this));
+            if (ContentCaptureFeatures.isEnabled()) {
+                // The created object is held by native side.
+                ContentCaptureReceiverManager.create(getWebContents());
+            }
         } finally {
             TraceEvent.end("ChromeTab.initBrowserComponents");
         }
@@ -1645,22 +1645,6 @@
     }
 
     /**
-     * @return The bitmap of the favicon scaled to 16x16dp. null if no favicon
-     *         is specified or it requires the default favicon.
-     */
-    public Bitmap getFavicon() {
-        // If we have no content or a native page, return null.
-        if (isNativePage() || getWebContents() == null) return null;
-
-        // Use the cached favicon only if the page wasn't changed.
-        if (mFavicon != null && mFaviconUrl != null && mFaviconUrl.equals(getUrl())) {
-            return mFavicon;
-        }
-
-        return nativeGetFavicon(mNativeTabAndroid);
-    }
-
-    /**
      * Loads the tab if it's not loaded (e.g. because it was killed in background).
      * This will trigger a regular load for tabs with pending lazy first load (tabs opened in
      * background on low-memory devices).
@@ -1885,49 +1869,6 @@
         return mWebContentsDelegate;
     }
 
-    private boolean isIdealFaviconSize(int width, int height) {
-        return width == mIdealFaviconSize && height == mIdealFaviconSize;
-    }
-
-    /**
-     * @param width new favicon's width.
-     * @param height new favicon's height.
-     * @return true iff the new favicon should replace the current one.
-     */
-    private boolean isBetterFavicon(int width, int height) {
-        if (isIdealFaviconSize(width, height)) return true;
-
-        // Prefer square favicons over rectangular ones
-        if (mFaviconWidth != mFaviconHeight && width == height) return true;
-        if (mFaviconWidth == mFaviconHeight && width != height) return false;
-
-        // Do not update favicon if it's already at least as big as the ideal size in both dimens
-        if (mFaviconWidth >= mIdealFaviconSize && mFaviconHeight >= mIdealFaviconSize) return false;
-
-        // Update favicon if the new one is larger in one dimen, but not smaller in the other
-        return (width > mFaviconWidth && !(height < mFaviconHeight))
-                || (!(width < mFaviconWidth) && height > mFaviconHeight);
-    }
-
-    @CalledByNative
-    protected void onFaviconAvailable(Bitmap icon) {
-        if (icon == null) return;
-        String url = getUrl();
-        boolean pageUrlChanged = !url.equals(mFaviconUrl);
-        // This method will be called multiple times if the page has more than one favicon.
-        // We are trying to use the |mIdealFaviconSize|x|mIdealFaviconSize| DP icon here, or the
-        // first one larger than that received. Bitmap.createScaledBitmap will return the original
-        // bitmap if it is already |mIdealFaviconSize|x|mIdealFaviconSize| DP.
-        if (pageUrlChanged || isBetterFavicon(icon.getWidth(), icon.getHeight())) {
-            mFavicon = Bitmap.createScaledBitmap(icon, mIdealFaviconSize, mIdealFaviconSize, true);
-            mFaviconWidth = icon.getWidth();
-            mFaviconHeight = icon.getHeight();
-            mFaviconUrl = url;
-        }
-
-        for (TabObserver observer : mObservers) observer.onFaviconUpdated(this, icon);
-    }
-
     /**
      * Checks if this tab is currently presented in the context of custom tabs. Tabs can be moved
      * between different activities so the returned value might change over the lifetime of the tab.
@@ -2573,22 +2514,6 @@
         return nativeAreRendererInputEventsIgnored(mNativeTabAndroid);
     }
 
-    /**
-     * @return The publisher URL if the current page is hosted on a trusted CDN, or null otherwise.
-     */
-    public @Nullable String getTrustedCdnPublisherUrl() {
-        ChromeActivity activity = getActivity();
-        if (activity == null) return null;
-        if (!activity.canShowTrustedCdnPublisherUrl()) return null;
-        if (getSecurityLevel() == ConnectionSecurityLevel.DANGEROUS) return null;
-        return mTrustedCdnPublisherUrl;
-    }
-
-    @CalledByNative
-    private void setTrustedCdnPublisherUrl(@Nullable String url) {
-        mTrustedCdnPublisherUrl = url;
-    }
-
     private native void nativeInit();
     private native void nativeDestroy(long nativeTabAndroid);
     private native void nativeInitWebContents(long nativeTabAndroid, boolean incognito,
@@ -2607,7 +2532,6 @@
             long intentReceivedTimestamp);
     private native void nativeSetActiveNavigationEntryTitleForUrl(long nativeTabAndroid, String url,
             String title);
-    private native Bitmap nativeGetFavicon(long nativeTabAndroid);
     private native void nativeCreateHistoricalTab(long nativeTabAndroid);
     private native void nativeUpdateBrowserControlsState(
             long nativeTabAndroid, int constraints, int current, boolean animate);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/tab/TabFavicon.java b/chrome/android/java/src/org/chromium/chrome/browser/tab/TabFavicon.java
new file mode 100644
index 0000000..27e6851d
--- /dev/null
+++ b/chrome/android/java/src/org/chromium/chrome/browser/tab/TabFavicon.java
@@ -0,0 +1,144 @@
+// 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.tab;
+
+import android.content.res.Resources;
+import android.graphics.Bitmap;
+
+import org.chromium.base.ObserverList.RewindableIterator;
+import org.chromium.base.annotations.CalledByNative;
+import org.chromium.chrome.R;
+import org.chromium.content_public.browser.WebContents;
+
+/**
+ * Fetches a favicon for active WebContents in a Tab.
+ */
+public class TabFavicon extends TabWebContentsUserData {
+    private static final Class<TabFavicon> USER_DATA_KEY = TabFavicon.class;
+
+    private final Tab mTab;
+    private final long mNativeTabFavicon;
+
+    /**
+     * The size in pixels at which favicons will be drawn. Ideally mFavicon will have this size to
+     * avoid scaling artifacts.
+     */
+    private final int mIdealFaviconSize;
+
+    private Bitmap mFavicon;
+    private int mFaviconWidth;
+    private int mFaviconHeight;
+    private String mFaviconUrl;
+
+    static TabFavicon from(Tab tab) {
+        TabFavicon favicon = get(tab);
+        if (favicon == null) {
+            favicon = tab.getUserDataHost().setUserData(USER_DATA_KEY, new TabFavicon(tab));
+        }
+        return favicon;
+    }
+
+    private static TabFavicon get(Tab tab) {
+        return tab.getUserDataHost().getUserData(USER_DATA_KEY);
+    }
+
+    /**
+     * @param tab Tab containing the web contents's favicon.
+     * @return {@link Bitmap} of the favicon.
+     */
+    public static Bitmap getBitmap(Tab tab) {
+        TabFavicon tabFavicon = get(tab);
+        return tabFavicon != null ? tabFavicon.getFavicon() : null;
+    }
+
+    private TabFavicon(Tab tab) {
+        super(tab);
+        Resources resources = tab.getThemedApplicationContext().getResources();
+        mIdealFaviconSize = resources.getDimensionPixelSize(R.dimen.default_favicon_size);
+        mTab = tab;
+        mNativeTabFavicon = nativeInit();
+    }
+
+    @Override
+    public void initWebContents(WebContents webContents) {
+        nativeSetWebContents(mNativeTabFavicon, webContents);
+    }
+
+    @Override
+    public void cleanupWebContents(WebContents webContents) {
+        nativeResetWebContents(mNativeTabFavicon);
+    }
+
+    @Override
+    public void destroyInternal() {
+        nativeOnDestroyed(mNativeTabFavicon);
+    }
+
+    /**
+     * @return The bitmap of the favicon scaled to 16x16dp. null if no favicon
+     *         is specified or it requires the default favicon.
+     */
+    private Bitmap getFavicon() {
+        // If we have no content or a native page, return null.
+        if (mTab.isNativePage() || mTab.getWebContents() == null) return null;
+
+        // Use the cached favicon only if the page wasn't changed.
+        if (mFavicon != null && mFaviconUrl != null && mFaviconUrl.equals(mTab.getUrl())) {
+            return mFavicon;
+        }
+
+        return nativeGetFavicon(mNativeTabFavicon);
+    }
+
+    /**
+     * @param width new favicon's width.
+     * @param height new favicon's height.
+     * @return true iff the new favicon should replace the current one.
+     */
+    private boolean isBetterFavicon(int width, int height) {
+        if (isIdealFaviconSize(width, height)) return true;
+
+        // Prefer square favicons over rectangular ones
+        if (mFaviconWidth != mFaviconHeight && width == height) return true;
+        if (mFaviconWidth == mFaviconHeight && width != height) return false;
+
+        // Do not update favicon if it's already at least as big as the ideal size in both dimens
+        if (mFaviconWidth >= mIdealFaviconSize && mFaviconHeight >= mIdealFaviconSize) return false;
+
+        // Update favicon if the new one is larger in one dimen, but not smaller in the other
+        return (width > mFaviconWidth && !(height < mFaviconHeight))
+                || (!(width < mFaviconWidth) && height > mFaviconHeight);
+    }
+
+    private boolean isIdealFaviconSize(int width, int height) {
+        return width == mIdealFaviconSize && height == mIdealFaviconSize;
+    }
+
+    @CalledByNative
+    private void onFaviconAvailable(Bitmap icon) {
+        if (icon == null) return;
+        String url = mTab.getUrl();
+        boolean pageUrlChanged = !url.equals(mFaviconUrl);
+        // This method will be called multiple times if the page has more than one favicon.
+        // We are trying to use the |mIdealFaviconSize|x|mIdealFaviconSize| DP icon here, or the
+        // first one larger than that received. Bitmap.createScaledBitmap will return the original
+        // bitmap if it is already |mIdealFaviconSize|x|mIdealFaviconSize| DP.
+        if (pageUrlChanged || isBetterFavicon(icon.getWidth(), icon.getHeight())) {
+            mFavicon = Bitmap.createScaledBitmap(icon, mIdealFaviconSize, mIdealFaviconSize, true);
+            mFaviconWidth = icon.getWidth();
+            mFaviconHeight = icon.getHeight();
+            mFaviconUrl = url;
+        }
+
+        RewindableIterator<TabObserver> observers = mTab.getTabObservers();
+        while (observers.hasNext()) observers.next().onFaviconUpdated(mTab, icon);
+    }
+
+    private native long nativeInit();
+    private native void nativeOnDestroyed(long nativeTabFavicon);
+    private native void nativeSetWebContents(long nativeTabFavicon, WebContents webContents);
+    private native void nativeResetWebContents(long nativeTabFavicon);
+    private native Bitmap nativeGetFavicon(long nativeTabFavicon);
+}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/tab/TrustedCdn.java b/chrome/android/java/src/org/chromium/chrome/browser/tab/TrustedCdn.java
new file mode 100644
index 0000000..ce5bd2c
--- /dev/null
+++ b/chrome/android/java/src/org/chromium/chrome/browser/tab/TrustedCdn.java
@@ -0,0 +1,94 @@
+// 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.tab;
+
+import android.support.annotation.Nullable;
+
+import org.chromium.base.annotations.CalledByNative;
+import org.chromium.chrome.browser.ChromeActivity;
+import org.chromium.chrome.browser.ssl.SecurityStateModel;
+import org.chromium.components.security_state.ConnectionSecurityLevel;
+import org.chromium.content_public.browser.WebContents;
+
+/**
+ * Provides a trusted CDN publisher URL for the current web contents in a Tab.
+ */
+public class TrustedCdn extends TabWebContentsUserData {
+    private static final Class<TrustedCdn> USER_DATA_KEY = TrustedCdn.class;
+
+    private final Tab mTab;
+    private final long mNativeTrustedCdn;
+
+    /**
+     * The publisher URL for pages hosted on a trusted CDN, or null otherwise.
+     */
+    private String mPublisherUrl;
+
+    /**
+     *  @return The publisher URL if the current page is hosted on a trusted CDN, or null otherwise
+     */
+    @Nullable
+    public static String getPublisherUrl(Tab tab) {
+        TrustedCdn cdn = get(tab);
+        return cdn != null ? cdn.getPublisherUrl() : null;
+    }
+
+    static TrustedCdn from(Tab tab) {
+        TrustedCdn trustedCdn = get(tab);
+        if (trustedCdn == null) {
+            trustedCdn = tab.getUserDataHost().setUserData(USER_DATA_KEY, new TrustedCdn(tab));
+        }
+        return trustedCdn;
+    }
+
+    private static TrustedCdn get(Tab tab) {
+        return tab != null ? tab.getUserDataHost().getUserData(USER_DATA_KEY) : null;
+    }
+
+    private TrustedCdn(Tab tab) {
+        super(tab);
+        mTab = tab;
+        mNativeTrustedCdn = nativeInit();
+    }
+
+    @Override
+    public void initWebContents(WebContents webContents) {
+        nativeSetWebContents(mNativeTrustedCdn, webContents);
+    }
+
+    @Override
+    public void cleanupWebContents(WebContents webContents) {
+        nativeResetWebContents(mNativeTrustedCdn);
+        mPublisherUrl = null;
+    }
+
+    @Override
+    public void destroyInternal() {
+        nativeOnDestroyed(mNativeTrustedCdn);
+    }
+
+    @Nullable
+    private String getPublisherUrl() {
+        ChromeActivity activity = mTab.getActivity();
+        if (activity == null) return null;
+        if (!activity.canShowTrustedCdnPublisherUrl()) return null;
+        if (getSecurityLevel() == ConnectionSecurityLevel.DANGEROUS) return null;
+        return mPublisherUrl;
+    }
+
+    private int getSecurityLevel() {
+        return SecurityStateModel.getSecurityLevelForWebContents(mTab.getWebContents());
+    }
+
+    @CalledByNative
+    private void setPublisherUrl(@Nullable String url) {
+        mPublisherUrl = url;
+    }
+
+    private native long nativeInit();
+    private native void nativeOnDestroyed(long nativeTrustedCdn);
+    private native void nativeSetWebContents(long nativeTrustedCdn, WebContents webContents);
+    private native void nativeResetWebContents(long nativeTrustedCdn);
+}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/tasks/tab_list_ui/BottomTabGridCoordinator.java b/chrome/android/java/src/org/chromium/chrome/browser/tasks/tab_list_ui/BottomTabGridCoordinator.java
index 0a51d6b..256580b 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/tasks/tab_list_ui/BottomTabGridCoordinator.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/tasks/tab_list_ui/BottomTabGridCoordinator.java
@@ -4,13 +4,10 @@
 
 package org.chromium.chrome.browser.tasks.tab_list_ui;
 
-import static org.chromium.chrome.browser.dependency_injection.ChromeCommonQualifiers.ACTIVITY_CONTEXT;
-
 import android.content.Context;
 
 import org.chromium.chrome.browser.compositor.layouts.content.TabContentManager;
 import org.chromium.chrome.browser.dependency_injection.ActivityScope;
-import org.chromium.chrome.browser.init.ActivityLifecycleDispatcher;
 import org.chromium.chrome.browser.lifecycle.Destroyable;
 import org.chromium.chrome.browser.tabmodel.TabCreatorManager;
 import org.chromium.chrome.browser.tabmodel.TabModel;
@@ -18,9 +15,6 @@
 import org.chromium.chrome.browser.widget.bottomsheet.BottomSheetController;
 import org.chromium.ui.modelutil.PropertyModel;
 
-import javax.inject.Inject;
-import javax.inject.Named;
-
 /**
  * A coordinator for BottomTabGrid component. Manages the communication with
  * {@link TabListCoordinator} as well as the life-cycle of shared component
@@ -29,17 +23,14 @@
 @ActivityScope
 public class BottomTabGridCoordinator implements Destroyable {
     private final Context mContext;
-    private final ActivityLifecycleDispatcher mLifecycleDispatcher;
     private final TabListCoordinator mTabGridCoordinator;
     private final BottomTabGridMediator mMediator;
     private BottomTabGridSheetContent mBottomSheetContent;
     private BottomTabGridSheetToolbarCoordinator mToolbarCoordinator;
     private final PropertyModel mToolbarPropertyModel;
 
-    @Inject
-    BottomTabGridCoordinator(@Named(ACTIVITY_CONTEXT) Context context,
-            BottomSheetController bottomSheetController, TabModelSelector tabModelSelector,
-            TabContentManager tabContentManager, ActivityLifecycleDispatcher lifecycleDispatcher,
+    BottomTabGridCoordinator(Context context, BottomSheetController bottomSheetController,
+            TabModelSelector tabModelSelector, TabContentManager tabContentManager,
             TabCreatorManager tabCreatorManager) {
         mContext = context;
 
@@ -51,9 +42,6 @@
         mMediator =
                 new BottomTabGridMediator(mContext, bottomSheetController, this::resetWithTabModel,
                         mToolbarPropertyModel, tabModelSelector, tabCreatorManager);
-
-        mLifecycleDispatcher = lifecycleDispatcher;
-        mLifecycleDispatcher.register(this);
     }
 
     /**
@@ -71,8 +59,6 @@
         if (mToolbarCoordinator != null) {
             mToolbarCoordinator.destroy();
         }
-
-        mLifecycleDispatcher.unregister(this);
     }
 
     /**
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/tasks/tab_list_ui/BottomTabGridSheetToolbarCoordinator.java b/chrome/android/java/src/org/chromium/chrome/browser/tasks/tab_list_ui/BottomTabGridSheetToolbarCoordinator.java
index 27c17faa..6b1e5596 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/tasks/tab_list_ui/BottomTabGridSheetToolbarCoordinator.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/tasks/tab_list_ui/BottomTabGridSheetToolbarCoordinator.java
@@ -19,7 +19,7 @@
  * grid when presented inside the bottom sheet. {@link BottomTabGridCoordinator}
  */
 class BottomTabGridSheetToolbarCoordinator implements Destroyable {
-    private final BottomTabGridSheetToolbarView mToolbarView;
+    private final BottomTabListToolbarView mToolbarView;
     private final PropertyModelChangeProcessor mModelChangeProcessor;
 
     /**
@@ -33,7 +33,7 @@
      */
     BottomTabGridSheetToolbarCoordinator(
             Context context, ViewGroup parentView, PropertyModel toolbarPropertyModel) {
-        mToolbarView = (BottomTabGridSheetToolbarView) LayoutInflater.from(context).inflate(
+        mToolbarView = (BottomTabListToolbarView) LayoutInflater.from(context).inflate(
                 R.layout.bottom_tab_grid_toolbar, parentView, false);
         mModelChangeProcessor = PropertyModelChangeProcessor.create(
                 toolbarPropertyModel, mToolbarView, BottomTabGridSheetToolbarViewBinder::bind);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/tasks/tab_list_ui/BottomTabGridSheetToolbarView.java b/chrome/android/java/src/org/chromium/chrome/browser/tasks/tab_list_ui/BottomTabGridSheetToolbarView.java
deleted file mode 100644
index 6216783..0000000
--- a/chrome/android/java/src/org/chromium/chrome/browser/tasks/tab_list_ui/BottomTabGridSheetToolbarView.java
+++ /dev/null
@@ -1,48 +0,0 @@
-// Copyright 2018 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-package org.chromium.chrome.browser.tasks.tab_list_ui;
-
-import android.content.Context;
-import android.util.AttributeSet;
-import android.widget.FrameLayout;
-import android.widget.TextView;
-
-import org.chromium.chrome.R;
-import org.chromium.chrome.browser.widget.ListMenuButton;
-
-/**
- * Represents the toolbar in bottom tab grid sheet.
- * {@link BottomTabGridSheetToolbarCoordinator}
- */
-public class BottomTabGridSheetToolbarView extends FrameLayout {
-    private ListMenuButton mCollapseButton;
-    private ListMenuButton mAddButton;
-    private TextView mTitle;
-
-    public BottomTabGridSheetToolbarView(Context context, AttributeSet attrs) {
-        super(context, attrs);
-    }
-
-    @Override
-    protected void onFinishInflate() {
-        super.onFinishInflate();
-
-        mAddButton = findViewById(R.id.add);
-        mCollapseButton = findViewById(R.id.collapse);
-        mTitle = (TextView) findViewById(R.id.title);
-    }
-
-    void setCollapseButtonOnClickListener(OnClickListener listener) {
-        mCollapseButton.setOnClickListener(listener);
-    }
-
-    void setAddButtonOnClickListener(OnClickListener listener) {
-        mAddButton.setOnClickListener(listener);
-    }
-
-    void setTitle(String title) {
-        mTitle.setText(title);
-    }
-}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/tasks/tab_list_ui/BottomTabGridSheetToolbarViewBinder.java b/chrome/android/java/src/org/chromium/chrome/browser/tasks/tab_list_ui/BottomTabGridSheetToolbarViewBinder.java
index 6f08d4b..46c77e4 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/tasks/tab_list_ui/BottomTabGridSheetToolbarViewBinder.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/tasks/tab_list_ui/BottomTabGridSheetToolbarViewBinder.java
@@ -22,11 +22,11 @@
      * @param propertyKey The key for the property to update for.
      */
     public static void bind(
-            PropertyModel model, BottomTabGridSheetToolbarView view, PropertyKey propertyKey) {
+            PropertyModel model, BottomTabListToolbarView view, PropertyKey propertyKey) {
         if (COLLAPSE_CLICK_LISTENER == propertyKey) {
-            view.setCollapseButtonOnClickListener(model.get(COLLAPSE_CLICK_LISTENER));
+            view.setLeftButtonOnClickListener(model.get(COLLAPSE_CLICK_LISTENER));
         } else if (ADD_CLICK_LISTENER == propertyKey) {
-            view.setAddButtonOnClickListener(model.get(ADD_CLICK_LISTENER));
+            view.setRightButtonOnClickListener(model.get(ADD_CLICK_LISTENER));
         } else if (HEADER_TITLE == propertyKey) {
             view.setTitle(model.get(HEADER_TITLE));
         }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/tasks/tab_list_ui/BottomTabListToolbarView.java b/chrome/android/java/src/org/chromium/chrome/browser/tasks/tab_list_ui/BottomTabListToolbarView.java
new file mode 100644
index 0000000..11c6fc38
--- /dev/null
+++ b/chrome/android/java/src/org/chromium/chrome/browser/tasks/tab_list_ui/BottomTabListToolbarView.java
@@ -0,0 +1,69 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.chrome.browser.tasks.tab_list_ui;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.FrameLayout;
+import android.widget.TextView;
+
+import org.chromium.chrome.R;
+import org.chromium.chrome.browser.widget.ListMenuButton;
+
+/**
+ * Represents a generic toolbar used in the bottom strip/grid component.
+ * {@link BottomTabGridSheetToolbarCoordinator}
+ */
+public class BottomTabListToolbarView extends FrameLayout {
+    private ListMenuButton mRightButton;
+    private ListMenuButton mLeftButton;
+    private ViewGroup mContainerView;
+    private TextView mTitleTextView;
+
+    public BottomTabListToolbarView(Context context, AttributeSet attrs) {
+        super(context, attrs);
+    }
+
+    @Override
+    protected void onFinishInflate() {
+        super.onFinishInflate();
+
+        mLeftButton = findViewById(R.id.toolbar_left_button);
+        mRightButton = findViewById(R.id.toolbar_right_button);
+        mContainerView = (ViewGroup) findViewById(R.id.toolbar_container_view);
+        mTitleTextView = (TextView) findViewById(R.id.title);
+    }
+
+    void setLeftButtonOnClickListener(OnClickListener listener) {
+        mLeftButton.setOnClickListener(listener);
+    }
+
+    void setRightButtonOnClickListener(OnClickListener listener) {
+        mRightButton.setOnClickListener(listener);
+    }
+
+    ViewGroup getViewContainer() {
+        return mContainerView;
+    }
+
+    void setMainContentVisibility(boolean isVisible) {
+        if (mContainerView == null)
+            throw new IllegalStateException("Current Toolbar doesn't have a container view");
+
+        for (int i = 0; i < ((ViewGroup) mContainerView).getChildCount(); i++) {
+            View child = ((ViewGroup) mContainerView).getChildAt(i);
+            child.setVisibility(isVisible ? View.VISIBLE : View.INVISIBLE);
+        }
+    }
+
+    void setTitle(String title) {
+        if (mTitleTextView == null)
+            throw new IllegalStateException("Current Toolbar doesn't have a title text view");
+
+        mTitleTextView.setText(title);
+    }
+}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/tasks/tab_list_ui/BottomTabStripToolbarViewBinder.java b/chrome/android/java/src/org/chromium/chrome/browser/tasks/tab_list_ui/BottomTabStripToolbarViewBinder.java
new file mode 100644
index 0000000..4beec2a
--- /dev/null
+++ b/chrome/android/java/src/org/chromium/chrome/browser/tasks/tab_list_ui/BottomTabStripToolbarViewBinder.java
@@ -0,0 +1,35 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.chrome.browser.tasks.tab_list_ui;
+
+import static org.chromium.chrome.browser.tasks.tab_list_ui.TabStripToolbarViewProperties.ADD_CLICK_LISTENER;
+import static org.chromium.chrome.browser.tasks.tab_list_ui.TabStripToolbarViewProperties.EXPAND_CLICK_LISTENER;
+import static org.chromium.chrome.browser.tasks.tab_list_ui.TabStripToolbarViewProperties.IS_MAIN_CONTENT_VISIBLE;
+
+import org.chromium.ui.modelutil.PropertyKey;
+import org.chromium.ui.modelutil.PropertyModel;
+
+/**
+ * ViewBinder for BottomTabStripToolbar.
+ */
+class BottomTabStripToolbarViewBinder {
+    /**
+     * Binds the given model to the given view, updating the payload in propertyKey.
+     *
+     * @param model       The model to use.
+     * @param view        The view to use.
+     * @param propertyKey The key for the property to update for.
+     */
+    public static void bind(
+            PropertyModel model, BottomTabListToolbarView view, PropertyKey propertyKey) {
+        if (EXPAND_CLICK_LISTENER == propertyKey) {
+            view.setLeftButtonOnClickListener(model.get(EXPAND_CLICK_LISTENER));
+        } else if (ADD_CLICK_LISTENER == propertyKey) {
+            view.setRightButtonOnClickListener(model.get(ADD_CLICK_LISTENER));
+        } else if (IS_MAIN_CONTENT_VISIBLE == propertyKey) {
+            view.setMainContentVisibility(model.get(IS_MAIN_CONTENT_VISIBLE));
+        }
+    }
+}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/tasks/tab_list_ui/TabListMediator.java b/chrome/android/java/src/org/chromium/chrome/browser/tasks/tab_list_ui/TabListMediator.java
index 8158783..9531b34 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/tasks/tab_list_ui/TabListMediator.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/tasks/tab_list_ui/TabListMediator.java
@@ -16,6 +16,7 @@
 import org.chromium.chrome.browser.profiles.Profile;
 import org.chromium.chrome.browser.tab.EmptyTabObserver;
 import org.chromium.chrome.browser.tab.Tab;
+import org.chromium.chrome.browser.tab.TabFavicon;
 import org.chromium.chrome.browser.tab.TabObserver;
 import org.chromium.chrome.browser.tabmodel.TabModel;
 import org.chromium.chrome.browser.tabmodel.TabModelSelector;
@@ -202,7 +203,7 @@
                 new PropertyModel.Builder(TabProperties.ALL_KEYS_TAB_GRID)
                         .with(TabProperties.TAB_ID, tab.getId())
                         .with(TabProperties.TITLE, tab.getTitle())
-                        .with(TabProperties.FAVICON, tab.getFavicon())
+                        .with(TabProperties.FAVICON, TabFavicon.getBitmap(tab))
                         .with(TabProperties.IS_SELECTED, isSelected)
                         .with(TabProperties.TAB_SELECTED_LISTENER, mTabSelectedListener)
                         .with(TabProperties.TAB_CLOSED_LISTENER, mTabClosedListener)
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/tasks/tab_list_ui/TabStripBottomToolbarCoordinator.java b/chrome/android/java/src/org/chromium/chrome/browser/tasks/tab_list_ui/TabStripBottomToolbarCoordinator.java
new file mode 100644
index 0000000..41c1a60
--- /dev/null
+++ b/chrome/android/java/src/org/chromium/chrome/browser/tasks/tab_list_ui/TabStripBottomToolbarCoordinator.java
@@ -0,0 +1,120 @@
+// 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.tab_list_ui;
+
+import static org.chromium.chrome.browser.dependency_injection.ChromeCommonQualifiers.ACTIVITY_CONTEXT;
+
+import android.content.Context;
+import android.view.ViewGroup;
+import android.view.ViewStub;
+
+import org.chromium.chrome.R;
+import org.chromium.chrome.browser.ChromeActivity;
+import org.chromium.chrome.browser.compositor.layouts.content.TabContentManager;
+import org.chromium.chrome.browser.dependency_injection.ActivityScope;
+import org.chromium.chrome.browser.init.ActivityLifecycleDispatcher;
+import org.chromium.chrome.browser.lifecycle.Destroyable;
+import org.chromium.chrome.browser.tabmodel.TabCreatorManager;
+import org.chromium.chrome.browser.tabmodel.TabModel;
+import org.chromium.chrome.browser.tabmodel.TabModelSelector;
+import org.chromium.chrome.browser.widget.bottomsheet.BottomSheetController;
+import org.chromium.ui.modelutil.PropertyModel;
+
+import javax.inject.Inject;
+import javax.inject.Named;
+
+/**
+ * A coordinator for BottomTabStrip component. Manages the communication with
+ * {@link TabListCoordinator} & @{link BottomTabGridCoordinator} as well as the
+ * life-cycle of shared component objects.
+ */
+@ActivityScope
+public class TabStripBottomToolbarCoordinator
+        implements Destroyable, TabStripBottomToolbarMediator.ResetHandler {
+    private final ActivityLifecycleDispatcher mLifecycleDispatcher;
+    private final Context mContext;
+    private final PropertyModel mTabStripToolbarModel;
+    private BottomSheetController mBottomSheetController;
+    private BottomTabGridCoordinator mBottomTabGridCoordinator;
+    private TabContentManager mTabContentManager;
+    private TabCreatorManager mTabCreatorManager;
+    private TabListCoordinator mTabStripCoordinator;
+    private TabModelSelector mTabModelSelector;
+    private TabStripBottomToolbarMediator mMediator;
+    private TabStripToolbarCoordinator mTabStripToolbarCoordinator;
+
+    /**
+     * Creates a new {@link TabStripBottomToolbarCoordinator}
+     */
+    @Inject
+    public TabStripBottomToolbarCoordinator(@Named(ACTIVITY_CONTEXT) Context context,
+            ChromeActivity activity, ActivityLifecycleDispatcher lifecycleDispatcher) {
+        mContext = context;
+        mLifecycleDispatcher = lifecycleDispatcher;
+        mTabStripToolbarModel = new PropertyModel(TabStripToolbarViewProperties.ALL_KEYS);
+
+        ViewStub stub = activity.findViewById(R.id.bottom_toolbar_stub);
+        mTabStripToolbarCoordinator = new TabStripToolbarCoordinator(
+                mContext, (ViewGroup) stub.inflate(), mTabStripToolbarModel);
+
+        mLifecycleDispatcher.register(this);
+    }
+
+    /**
+     * Handle any initialization that occurs once native has been loaded.
+     */
+    public void initializeWithNative(TabModelSelector tabModelSelector,
+            TabContentManager tabContentManager, TabCreatorManager tabCreatorManager,
+            BottomSheetController bottomSheetController) {
+        mTabModelSelector = tabModelSelector;
+        mTabContentManager = tabContentManager;
+        mTabCreatorManager = tabCreatorManager;
+        mBottomSheetController = bottomSheetController;
+
+        mTabStripCoordinator = new TabListCoordinator(TabListCoordinator.TabListMode.STRIP,
+                mContext, mTabModelSelector, mTabContentManager,
+                mTabStripToolbarCoordinator.getTabListContainerView(), true);
+
+        mBottomTabGridCoordinator = new BottomTabGridCoordinator(mContext, mBottomSheetController,
+                mTabModelSelector, mTabContentManager, mTabCreatorManager);
+
+        mMediator = new TabStripBottomToolbarMediator(
+                this, mTabStripToolbarModel, mTabModelSelector, mTabCreatorManager);
+    }
+
+    /**
+     * Handles a reset event originated from {@link TabStripBottomToolbarMediator}
+     * when the bottom sheet is collaped.
+     *
+     * @param tabModel current {@link TabModel} instance.
+     */
+    @Override
+    public void resetStripWithTabModel(TabModel tabModel) {
+        mTabStripCoordinator.resetWithTabModel(tabModel);
+        mMediator.resetWithTabModel(tabModel);
+    }
+
+    /**
+     * Handles a reset event originated from {@link TabStripBottomToolbarMediator}
+     * when the bottom sheet is expanded and the component.
+     *
+     * @param tabModel current {@link TabModel} instance.
+     */
+    @Override
+    public void resetSheetWithTabModel(TabModel tabModel) {
+        mBottomTabGridCoordinator.resetWithTabModel(tabModel);
+    }
+
+    /**
+     * Destroy any members that needs clean up.
+     */
+    @Override
+    public void destroy() {
+        mTabStripCoordinator.destroy();
+        mBottomTabGridCoordinator.destroy();
+        mMediator.destroy();
+        mLifecycleDispatcher.unregister(this);
+    }
+}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/tasks/tab_list_ui/TabStripBottomToolbarMediator.java b/chrome/android/java/src/org/chromium/chrome/browser/tasks/tab_list_ui/TabStripBottomToolbarMediator.java
new file mode 100644
index 0000000..bece6415
--- /dev/null
+++ b/chrome/android/java/src/org/chromium/chrome/browser/tasks/tab_list_ui/TabStripBottomToolbarMediator.java
@@ -0,0 +1,112 @@
+// 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.tab_list_ui;
+
+import android.view.View.OnClickListener;
+
+import org.chromium.chrome.browser.UrlConstants;
+import org.chromium.chrome.browser.lifecycle.Destroyable;
+import org.chromium.chrome.browser.tab.Tab;
+import org.chromium.chrome.browser.tabmodel.TabCreatorManager;
+import org.chromium.chrome.browser.tabmodel.TabLaunchType;
+import org.chromium.chrome.browser.tabmodel.TabModel;
+import org.chromium.chrome.browser.tabmodel.TabModelSelector;
+import org.chromium.chrome.browser.tabmodel.TabModelSelectorTabModelObserver;
+import org.chromium.chrome.browser.tabmodel.TabModelUtils;
+import org.chromium.chrome.browser.tabmodel.TabSelectionType;
+import org.chromium.content_public.browser.LoadUrlParams;
+import org.chromium.ui.modelutil.PropertyModel;
+
+/**
+ * A mediator for the TabStripBottomToolbar. Responsible for managing the
+ * internal state of the component.
+ */
+public class TabStripBottomToolbarMediator implements Destroyable {
+    /**
+     * Defines an interface for a {@link TabStripBottomToolbarMediator} reset event
+     * handler.
+     */
+    interface ResetHandler {
+        /**
+         * Handles a reset event originated from {@link TabStripBottomToolbarMediator}
+         * when the bottom sheet is collaped.
+         *
+         * @param tabModel current {@link TabModel} instance.
+         */
+        void resetStripWithTabModel(TabModel tabModel);
+
+        /**
+         * Handles a reset event originated from {@link TabStripBottomToolbarMediator}
+         * when the bottom sheet is expanded and the component.
+         *
+         * @param tabModel current {@link TabModel} instance.
+         */
+        void resetSheetWithTabModel(TabModel tabModel);
+    }
+
+    private final PropertyModel mToolbarPropertyModel;
+    private final TabModelSelectorTabModelObserver mTabModelObserver;
+    private final ResetHandler mResetHandler;
+    private final TabModelSelector mTabModelSelector;
+    private final TabCreatorManager mTabCreatorManager;
+
+    TabStripBottomToolbarMediator(ResetHandler resetHandler, PropertyModel toolbarPropertyModel,
+            TabModelSelector tabModelSelector, TabCreatorManager tabCreatorManager) {
+        mResetHandler = resetHandler;
+        mToolbarPropertyModel = toolbarPropertyModel;
+        mTabModelSelector = tabModelSelector;
+        mTabCreatorManager = tabCreatorManager;
+
+        // register for tab model
+        mTabModelObserver = new TabModelSelectorTabModelObserver(tabModelSelector) {
+            @Override
+            public void didSelectTab(Tab tab, @TabSelectionType int type, int lastId) {
+                if (TabModelUtils.getTabById(tabModelSelector.getCurrentModel(), lastId) != null)
+                    return;
+
+                mResetHandler.resetStripWithTabModel(tabModelSelector.getCurrentModel());
+            }
+        };
+
+        setupToolbarClickHandlers();
+        mResetHandler.resetStripWithTabModel(tabModelSelector.getCurrentModel());
+    }
+
+    void resetWithTabModel(TabModel tabModel) {
+        mToolbarPropertyModel.set(TabStripToolbarViewProperties.IS_MAIN_CONTENT_VISIBLE, true);
+    }
+
+    private void setupToolbarClickHandlers() {
+        mToolbarPropertyModel.set(TabStripToolbarViewProperties.EXPAND_CLICK_LISTENER, view -> {
+            mResetHandler.resetSheetWithTabModel(mTabModelSelector.getCurrentModel());
+        });
+        mToolbarPropertyModel.set(TabStripToolbarViewProperties.ADD_CLICK_LISTENER, view -> {
+            Tab currentTab = mTabModelSelector.getCurrentTab();
+            mTabCreatorManager.getTabCreator(currentTab.isIncognito())
+                    .createNewTab(new LoadUrlParams(UrlConstants.NTP_URL), TabLaunchType.FROM_LINK,
+                            currentTab);
+        });
+    }
+
+    private OnClickListener getExpandButtonClickListener() {
+        return view -> {
+            mResetHandler.resetSheetWithTabModel(mTabModelSelector.getCurrentModel());
+        };
+    }
+
+    private OnClickListener getAddButtonClickListener() {
+        return view -> {
+            Tab currentTab = mTabModelSelector.getCurrentTab();
+            mTabCreatorManager.getTabCreator(currentTab.isIncognito())
+                    .createNewTab(new LoadUrlParams(UrlConstants.NTP_URL), TabLaunchType.FROM_LINK,
+                            currentTab);
+        };
+    }
+
+    @Override
+    public void destroy() {
+        mTabModelObserver.destroy();
+    }
+}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/tasks/tab_list_ui/TabStripToolbarCoordinator.java b/chrome/android/java/src/org/chromium/chrome/browser/tasks/tab_list_ui/TabStripToolbarCoordinator.java
new file mode 100644
index 0000000..ab4bdd5
--- /dev/null
+++ b/chrome/android/java/src/org/chromium/chrome/browser/tasks/tab_list_ui/TabStripToolbarCoordinator.java
@@ -0,0 +1,56 @@
+// 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.tab_list_ui;
+
+import static org.chromium.chrome.browser.dependency_injection.ChromeCommonQualifiers.ACTIVITY_CONTEXT;
+
+import android.content.Context;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+
+import org.chromium.chrome.R;
+import org.chromium.chrome.browser.lifecycle.Destroyable;
+import org.chromium.ui.modelutil.PropertyModel;
+import org.chromium.ui.modelutil.PropertyModelChangeProcessor;
+
+import javax.inject.Named;
+
+/**
+ * A coordinator for BottomTabStripToolbar component.
+ */
+public class TabStripToolbarCoordinator implements Destroyable {
+    private final BottomTabListToolbarView mToolbarView;
+    private final PropertyModel mModel;
+    private final PropertyModelChangeProcessor mModelChangeProcessor;
+
+    TabStripToolbarCoordinator(
+            @Named(ACTIVITY_CONTEXT) Context context, ViewGroup parentView, PropertyModel model) {
+        mModel = model;
+        mToolbarView = (BottomTabListToolbarView) LayoutInflater.from(context).inflate(
+                R.layout.bottom_tab_strip_toolbar, parentView, false);
+
+        parentView.addView(mToolbarView);
+
+        mModelChangeProcessor = PropertyModelChangeProcessor.create(
+                model, mToolbarView, BottomTabStripToolbarViewBinder::bind);
+    }
+
+    View getView() {
+        return mToolbarView;
+    }
+
+    ViewGroup getTabListContainerView() {
+        return mToolbarView.getViewContainer();
+    }
+
+    /**
+     * Destroy any members that needs clean up.
+     */
+    @Override
+    public void destroy() {
+        mModelChangeProcessor.destroy();
+    }
+}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/tasks/tab_list_ui/TabStripToolbarViewProperties.java b/chrome/android/java/src/org/chromium/chrome/browser/tasks/tab_list_ui/TabStripToolbarViewProperties.java
new file mode 100644
index 0000000..e6c9a7473
--- /dev/null
+++ b/chrome/android/java/src/org/chromium/chrome/browser/tasks/tab_list_ui/TabStripToolbarViewProperties.java
@@ -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.
+
+package org.chromium.chrome.browser.tasks.tab_list_ui;
+
+import android.view.View.OnClickListener;
+
+import org.chromium.ui.modelutil.PropertyKey;
+import org.chromium.ui.modelutil.PropertyModel;
+
+class TabStripToolbarViewProperties {
+    public static final PropertyModel
+            .WritableObjectPropertyKey<OnClickListener> EXPAND_CLICK_LISTENER =
+            new PropertyModel.WritableObjectPropertyKey<OnClickListener>();
+    public static final PropertyModel
+            .WritableObjectPropertyKey<OnClickListener> ADD_CLICK_LISTENER =
+            new PropertyModel.WritableObjectPropertyKey<OnClickListener>();
+    public static final PropertyModel.WritableBooleanPropertyKey IS_MAIN_CONTENT_VISIBLE =
+            new PropertyModel.WritableBooleanPropertyKey();
+
+    public static final PropertyKey[] ALL_KEYS =
+            new PropertyKey[] {EXPAND_CLICK_LISTENER, ADD_CLICK_LISTENER, IS_MAIN_CONTENT_VISIBLE};
+}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/LocationBarModel.java b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/LocationBarModel.java
index d597a1a..a8b7f5d 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/LocationBarModel.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/LocationBarModel.java
@@ -29,6 +29,7 @@
 import org.chromium.chrome.browser.previews.PreviewsAndroidBridge;
 import org.chromium.chrome.browser.profiles.Profile;
 import org.chromium.chrome.browser.tab.Tab;
+import org.chromium.chrome.browser.tab.TrustedCdn;
 import org.chromium.chrome.browser.util.ColorUtils;
 import org.chromium.chrome.browser.util.UrlUtilities;
 import org.chromium.components.dom_distiller.core.DomDistillerService;
@@ -238,7 +239,7 @@
         // If the toolbar shows the publisher URL, it applies its own formatting for emphasis.
         if (mTab == null) return true;
 
-        return !shouldDisplaySearchTerms() && mTab.getTrustedCdnPublisherUrl() == null;
+        return !shouldDisplaySearchTerms() && TrustedCdn.getPublisherUrl(mTab) == null;
     }
 
     /**
@@ -322,8 +323,7 @@
     @Override
     public int getSecurityLevel() {
         Tab tab = getTab();
-        return getSecurityLevel(
-                tab, isOfflinePage(), tab == null ? null : tab.getTrustedCdnPublisherUrl());
+        return getSecurityLevel(tab, isOfflinePage(), TrustedCdn.getPublisherUrl(tab));
     }
 
     @Override
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/MenuButton.java b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/MenuButton.java
index 470f60c..7046874 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/MenuButton.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/MenuButton.java
@@ -245,7 +245,8 @@
                         ViewCompat.getPaddingEnd(mMenuImageButton),
                         mMenuImageButton.getPaddingBottom());
             }
-            mHighlightDrawable.setUseLightPulseColor(mUseLightDrawables);
+            mHighlightDrawable.setUseLightPulseColor(
+                    getContext().getResources(), mUseLightDrawables);
             setBackground(mHighlightDrawable);
             mHighlightDrawable.start();
         } else {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/ToolbarButtonInProductHelpController.java b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/ToolbarButtonInProductHelpController.java
index 38ad662..e5c10e2 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/ToolbarButtonInProductHelpController.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/ToolbarButtonInProductHelpController.java
@@ -104,7 +104,7 @@
         if (anchorView == null) return;
 
         setupAndMaybeShowIPHForFeature(FeatureConstants.DATA_SAVER_DETAIL_FEATURE,
-                R.id.app_menu_footer, R.string.iph_data_saver_detail_text,
+                R.id.data_reduction_menu_item, false, R.string.iph_data_saver_detail_text,
                 R.string.iph_data_saver_detail_accessibility_text, anchorView,
                 activity.getAppMenuHandler(), Profile.getLastUsedProfile(), activity);
     }
@@ -116,7 +116,7 @@
         final View anchorView = activity.getToolbarManager().getSecurityIconView();
         if (anchorView == null) return;
 
-        setupAndMaybeShowIPHForFeature(FeatureConstants.PREVIEWS_OMNIBOX_UI_FEATURE, null,
+        setupAndMaybeShowIPHForFeature(FeatureConstants.PREVIEWS_OMNIBOX_UI_FEATURE, null, true,
                 R.string.iph_previews_omnibox_ui_text,
                 R.string.iph_previews_omnibox_ui_accessibility_text, anchorView, null,
                 Profile.getLastUsedProfile(), activity);
@@ -133,7 +133,7 @@
 
     private void maybeShowDownloadHomeIPH() {
         setupAndMaybeShowIPHForFeature(FeatureConstants.DOWNLOAD_HOME_FEATURE,
-                R.id.downloads_menu_id, R.string.iph_download_home_text,
+                R.id.downloads_menu_id, true, R.string.iph_download_home_text,
                 R.string.iph_download_home_accessibility_text,
                 mActivity.getToolbarManager().getMenuButton(), mActivity.getAppMenuHandler(),
                 Profile.getLastUsedProfile(), mActivity);
@@ -142,7 +142,7 @@
     private void maybeShowNTPButtonIPH() {
         if (!canShowNTPButtonIPH(mActivity)) return;
 
-        setupAndMaybeShowIPHForFeature(FeatureConstants.NTP_BUTTON_FEATURE, null,
+        setupAndMaybeShowIPHForFeature(FeatureConstants.NTP_BUTTON_FEATURE, null, true,
                 R.string.iph_ntp_button_text_home_text,
                 R.string.iph_ntp_button_text_home_accessibility_text,
                 mActivity.findViewById(R.id.home_button), null, Profile.getLastUsedProfile(),
@@ -158,20 +158,21 @@
             ChromeTabbedActivity activity, Profile profile) {
         setupAndMaybeShowIPHForFeature(
                 FeatureConstants.DOWNLOAD_INFOBAR_DOWNLOAD_CONTINUING_FEATURE,
-                R.id.downloads_menu_id, R.string.iph_download_infobar_download_continuing_text,
+                R.id.downloads_menu_id, true,
+                R.string.iph_download_infobar_download_continuing_text,
                 R.string.iph_download_infobar_download_continuing_text,
                 activity.getToolbarManager().getMenuButton(), activity.getAppMenuHandler(), profile,
                 activity);
     }
 
     private static void setupAndMaybeShowIPHForFeature(String featureName,
-            Integer highlightMenuItemId, @StringRes int stringId,
+            Integer highlightMenuItemId, boolean circleHighlight, @StringRes int stringId,
             @StringRes int accessibilityStringId, View anchorView,
             @Nullable AppMenuHandler appMenuHandler, Profile profile, ChromeActivity activity) {
         final Tracker tracker = TrackerFactory.getTrackerForProfile(profile);
         tracker.addOnInitializedCallback((Callback<Boolean>) success
-                -> maybeShowIPH(tracker, featureName, highlightMenuItemId, stringId,
-                        accessibilityStringId, anchorView, appMenuHandler, activity));
+                -> maybeShowIPH(tracker, featureName, highlightMenuItemId, circleHighlight,
+                        stringId, accessibilityStringId, anchorView, appMenuHandler, activity));
     }
 
     private static boolean shouldHighlightForIPH(String featureName) {
@@ -184,7 +185,7 @@
     }
 
     private static void maybeShowIPH(Tracker tracker, String featureName,
-            Integer highlightMenuItemId, @StringRes int stringId,
+            Integer highlightMenuItemId, boolean circleHighlight, @StringRes int stringId,
             @StringRes int accessibilityStringId, View anchorView, AppMenuHandler appMenuHandler,
             ChromeActivity activity) {
         // Activity was destroyed; don't show IPH.
@@ -217,7 +218,8 @@
             }, ViewHighlighter.IPH_MIN_DELAY_BETWEEN_TWO_HIGHLIGHTS));
 
             if (shouldHighlightForIPH(featureName)) {
-                turnOnHighlightForTextBubble(appMenuHandler, highlightMenuItemId, anchorView);
+                turnOnHighlightForTextBubble(
+                        appMenuHandler, highlightMenuItemId, circleHighlight, anchorView);
             }
 
             int yInsetPx = activity.getResources().getDimensionPixelOffset(
@@ -227,18 +229,18 @@
         });
     }
 
-    private static void turnOnHighlightForTextBubble(
-            AppMenuHandler handler, Integer highlightMenuItemId, View anchorView) {
+    private static void turnOnHighlightForTextBubble(AppMenuHandler handler,
+            Integer highlightMenuItemId, boolean circleHighlight, View anchorView) {
         if (handler != null) {
-            handler.setMenuHighlight(highlightMenuItemId);
+            handler.setMenuHighlight(highlightMenuItemId, circleHighlight);
         } else {
-            ViewHighlighter.turnOnHighlight(anchorView, true);
+            ViewHighlighter.turnOnHighlight(anchorView, circleHighlight);
         }
     }
 
     private static void turnOffHighlightForTextBubble(AppMenuHandler handler, View anchorView) {
         if (handler != null) {
-            handler.setMenuHighlight(null);
+            handler.clearMenuHighlight();
         } else {
             ViewHighlighter.turnOffHighlight(anchorView);
         }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/ToolbarManager.java b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/ToolbarManager.java
index 9dd1440..9590abb4 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/ToolbarManager.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/ToolbarManager.java
@@ -31,7 +31,6 @@
 import org.chromium.base.task.PostTask;
 import org.chromium.chrome.R;
 import org.chromium.chrome.browser.ChromeActivity;
-import org.chromium.chrome.browser.ChromeFeatureList;
 import org.chromium.chrome.browser.ChromeTabbedActivity;
 import org.chromium.chrome.browser.TabLoadStatus;
 import org.chromium.chrome.browser.ThemeColorProvider;
@@ -232,6 +231,8 @@
 
     private OmniboxStartupMetrics mOmniboxStartupMetrics;
 
+    private boolean mIsBottomToolbarVisible;
+
     /**
      * Creates a ToolbarManager object.
      *
@@ -302,6 +303,11 @@
             }
         };
 
+        mIsBottomToolbarVisible = FeatureUtilities.isBottomToolbarEnabled()
+                && (!FeatureUtilities.isAdaptiveToolbarEnabled()
+                        || mActivity.getResources().getConfiguration().orientation
+                                != Configuration.ORIENTATION_LANDSCAPE);
+
         mIncognitoStateProvider = new IncognitoStateProvider(mActivity);
         mTabCountProvider = new TabCountProvider();
         mThemeColorProvider = themeColorProvider;
@@ -309,6 +315,7 @@
 
         mToolbarProvider = AsyncViewProvider.of(controlContainer, R.id.toolbar_stub, R.id.toolbar);
         mToolbar = new TopToolbarCoordinator(controlContainer, mToolbarProvider);
+        mToolbar.onBottomToolbarVisibilityChanged(mIsBottomToolbarVisible);
         mToolbarProvider.whenLoaded((toolbar)
                                             -> onToolbarInflationComplete(menuHandler,
                                                     appMenuPropertiesDelegate, invalidator));
@@ -779,7 +786,7 @@
                 mActivity.findViewById(R.id.bottom_toolbar_stub),
                 mActivity.getActivityTabProvider(), homeButtonListener, searchAcceleratorListener,
                 shareButtonListener);
-        if (mAppMenuButtonHelper != null) mAppMenuButtonHelper.setMenuShowsFromBottom(true);
+        mBottomToolbarCoordinator.setBottomToolbarVisible(mIsBottomToolbarVisible);
         Toast.setGlobalExtraYOffset(
                 mActivity.getResources().getDimensionPixelSize(R.dimen.bottom_toolbar_height));
     }
@@ -810,11 +817,10 @@
     }
 
     /**
-     * @return Whether the bottom toolbar is currently enabled (an activity may or may not enable
-     *         this feature).
+     * @return Whether the menu button is in the bottom toolbar.
      */
-    public boolean isBottomToolbarEnabled() {
-        return mBottomToolbarCoordinator != null;
+    public boolean isMenuButtonInBottomToolbar() {
+        return mIsBottomToolbarVisible;
     }
 
     /**
@@ -825,8 +831,8 @@
     }
 
     private void showMenuIPHTextBubble(ChromeActivity activity, Tracker tracker, String featureName,
-            @StringRes int stringId, @StringRes int accessibilityStringId,
-            Integer highlightItemId) {
+            @StringRes int stringId, @StringRes int accessibilityStringId, Integer highlightItemId,
+            boolean circleHighlight) {
         ViewRectProvider rectProvider = new ViewRectProvider(getMenuButton());
         int yInsetPx = mActivity.getResources().getDimensionPixelOffset(
                 R.dimen.text_bubble_menu_anchor_y_inset);
@@ -837,10 +843,10 @@
         mTextBubble.addOnDismissListener(() -> {
             mHandler.postDelayed(() -> {
                 tracker.dismissed(featureName);
-                activity.getAppMenuHandler().setMenuHighlight(null);
+                activity.getAppMenuHandler().clearMenuHighlight();
             }, ViewHighlighter.IPH_MIN_DELAY_BETWEEN_TWO_HIGHLIGHTS);
         });
-        activity.getAppMenuHandler().setMenuHighlight(highlightItemId);
+        activity.getAppMenuHandler().setMenuHighlight(highlightItemId, circleHighlight);
         mTextBubble.show();
     }
 
@@ -865,7 +871,7 @@
         showMenuIPHTextBubble(activity, tracker, featureName,
                 R.string.iph_download_page_for_offline_usage_text,
                 R.string.iph_download_page_for_offline_usage_accessibility_text,
-                R.id.offline_page_id);
+                R.id.offline_page_id, true);
 
         // Record metrics if we show Download IPH after a screenshot of the page.
         ChromeTabbedActivity chromeActivity = ((ChromeTabbedActivity) activity);
@@ -895,7 +901,7 @@
 
         showMenuIPHTextBubble(activity, tracker, featureName,
                 R.string.iph_translate_menu_button_text,
-                R.string.iph_translate_menu_button_accessibility_text, R.id.translate_id);
+                R.string.iph_translate_menu_button_accessibility_text, R.id.translate_id, false);
     }
 
     /**
@@ -1087,7 +1093,7 @@
      * @return The view containing the pop up menu button.
      */
     public @Nullable View getMenuButton() {
-        if (mBottomToolbarCoordinator != null) {
+        if (mBottomToolbarCoordinator != null && isMenuButtonInBottomToolbar()) {
             return mBottomToolbarCoordinator.getMenuButtonWrapper().getImageButton();
         }
         return mToolbar.getMenuButton();
@@ -1198,15 +1204,15 @@
      * Called when the orientation of the activity has changed.
      */
     public void onOrientationChange() {
-        if (mActionModeController == null) return;
-        mActionModeController.showControlsOnOrientationChange();
-        if (mBottomToolbarCoordinator != null
-                && ChromeFeatureList.isEnabled(ChromeFeatureList.CHROME_DUET_ADAPTIVE)) {
-            final boolean isLandscape = mActivity.getResources().getConfiguration().orientation
-                    == Configuration.ORIENTATION_LANDSCAPE;
-            mBottomToolbarCoordinator.setBottomToolbarHidden(isLandscape);
+        if (mActionModeController != null) mActionModeController.showControlsOnOrientationChange();
+
+        if (mBottomToolbarCoordinator != null && FeatureUtilities.isAdaptiveToolbarEnabled()) {
+            mIsBottomToolbarVisible = mActivity.getResources().getConfiguration().orientation
+                    != Configuration.ORIENTATION_LANDSCAPE;
+            mToolbar.onBottomToolbarVisibilityChanged(mIsBottomToolbarVisible);
+            mBottomToolbarCoordinator.setBottomToolbarVisible(mIsBottomToolbarVisible);
             if (mAppMenuButtonHelper != null) {
-                mAppMenuButtonHelper.setMenuShowsFromBottom(!isLandscape);
+                mAppMenuButtonHelper.setMenuShowsFromBottom(mIsBottomToolbarVisible);
             }
         }
     }
@@ -1323,7 +1329,7 @@
             }
         });
         mAppMenuButtonHelper = new AppMenuButtonHelper(menuHandler);
-        mAppMenuButtonHelper.setMenuShowsFromBottom(mBottomToolbarCoordinator != null);
+        mAppMenuButtonHelper.setMenuShowsFromBottom(isMenuButtonInBottomToolbar());
         mAppMenuButtonHelper.setOnAppMenuShownListener(() -> {
             RecordUserAction.record("MobileToolbarShowMenu");
             mToolbar.onMenuShown();
@@ -1645,7 +1651,7 @@
         mToolbar.updateForwardButtonVisibility(currentTab != null && currentTab.canGoForward());
         updateReloadState(tabCrashed);
         updateBookmarkButtonStatus();
-        if (mToolbar.getMenuButtonWrapper() != null) {
+        if (mToolbar.getMenuButtonWrapper() != null && !isMenuButtonInBottomToolbar()) {
             mToolbar.getMenuButtonWrapper().setVisibility(View.VISIBLE);
         }
     }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/bottom/BottomToolbarCoordinator.java b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/bottom/BottomToolbarCoordinator.java
index fa449549..8fc1aa0 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/bottom/BottomToolbarCoordinator.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/bottom/BottomToolbarCoordinator.java
@@ -40,6 +40,9 @@
     /** A provider that notifies components when the theme color changes.*/
     private final BottomToolbarThemeColorProvider mBottomToolbarThemeColorProvider;
 
+    /** The root view of the bottom toolbar. */
+    private final View mRoot;
+
     /**
      * Build the coordinator that manages the bottom toolbar.
      * @param fullscreenManager A {@link ChromeFullscreenManager} to update the bottom controls
@@ -53,14 +56,15 @@
     public BottomToolbarCoordinator(ChromeFullscreenManager fullscreenManager, ViewStub stub,
             ActivityTabProvider tabProvider, OnClickListener homeButtonListener,
             OnClickListener searchAcceleratorListener, OnClickListener shareButtonListener) {
-        final View root = stub.inflate();
+        mRoot = stub.inflate();
 
-        mBrowsingModeCoordinator = new BrowsingModeBottomToolbarCoordinator(root, fullscreenManager,
-                tabProvider, homeButtonListener, searchAcceleratorListener, shareButtonListener);
+        mBrowsingModeCoordinator =
+                new BrowsingModeBottomToolbarCoordinator(mRoot, fullscreenManager, tabProvider,
+                        homeButtonListener, searchAcceleratorListener, shareButtonListener);
 
-        mTabSwitcherModeStub = root.findViewById(R.id.bottom_toolbar_tab_switcher_mode_stub);
+        mTabSwitcherModeStub = mRoot.findViewById(R.id.bottom_toolbar_tab_switcher_mode_stub);
 
-        mBottomToolbarThemeColorProvider = new BottomToolbarThemeColorProvider(root.getContext());
+        mBottomToolbarThemeColorProvider = new BottomToolbarThemeColorProvider(mRoot.getContext());
     }
 
     /**
@@ -104,11 +108,16 @@
     }
 
     /**
-     * @param shouldHide Whether the bottom toolbar should be hidden.
+     * @param isVisible Whether the bottom toolbar is visible.
      */
-    public void setBottomToolbarHidden(boolean shouldHide) {
+    public void setBottomToolbarVisible(boolean isVisible) {
+        // TODO (amaralp): The coordinator should not have direct access to the view.
+        mRoot.setVisibility(isVisible ? View.VISIBLE : View.GONE);
+
+        mBrowsingModeCoordinator.setVisible(isVisible);
+
         if (mTabSwitcherModeCoordinator != null) {
-            mTabSwitcherModeCoordinator.showToolbarOnTop(shouldHide);
+            mTabSwitcherModeCoordinator.showToolbarOnTop(!isVisible);
         }
     }
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/bottom/BrowsingModeBottomToolbarCoordinator.java b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/bottom/BrowsingModeBottomToolbarCoordinator.java
index d1a54e6..0c1ca9f 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/bottom/BrowsingModeBottomToolbarCoordinator.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/bottom/BrowsingModeBottomToolbarCoordinator.java
@@ -154,6 +154,13 @@
     }
 
     /**
+     * @param isVisible Whether the browsing mode bottom toolbar is visible.
+     */
+    public void setVisible(boolean isVisible) {
+        mMediator.setVisible(isVisible);
+    }
+
+    /**
      * Show the update badge over the bottom toolbar's app menu.
      */
     public void showAppMenuUpdateBadge() {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/bottom/BrowsingModeBottomToolbarMediator.java b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/bottom/BrowsingModeBottomToolbarMediator.java
index afa8098..398c6e1 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/bottom/BrowsingModeBottomToolbarMediator.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/bottom/BrowsingModeBottomToolbarMediator.java
@@ -47,9 +47,6 @@
     /** The model for the browsing mode bottom toolbar that holds all of its state. */
     private BrowsingModeBottomToolbarModel mModel;
 
-    /** The previous height of the bottom toolbar. */
-    private int mBrowsingModeBottomToolbarHeightBeforeHide;
-
     /** Whether the swipe layout is currently active. */
     private boolean mIsInSwipeLayout;
 
@@ -68,6 +65,12 @@
     /** A state set to {@code true} while any overlay panel is showing. */
     private boolean mIsOverlayPanelShowing;
 
+    /** Whether the bottom toolbar is hidden because we are in adaptive toolbar mode. */
+    private boolean mIsHiddenForAdaptiveToolbar;
+
+    /** The height of the bottom bar in pixels */
+    private final int mBottomToolbarHeight;
+
     /**
      * Build a new mediator that handles events from outside the bottom toolbar.
      * @param model The {@link BrowsingModeBottomToolbarModel} that holds all the state for the
@@ -81,11 +84,7 @@
         mModel = model;
         mFullscreenManager = fullscreenManager;
         mFullscreenManager.addListener(this);
-
-        // Notify the fullscreen manager that the bottom controls now have a height.
-        fullscreenManager.setBottomControlsHeight(
-                resources.getDimensionPixelOffset(R.dimen.bottom_toolbar_height));
-        fullscreenManager.updateViewportSize();
+        mBottomToolbarHeight = resources.getDimensionPixelOffset(R.dimen.bottom_toolbar_height);
     }
 
     /**
@@ -199,7 +198,10 @@
     public void onToggleOverlayVideoMode(boolean enabled) {}
 
     @Override
-    public void onBottomControlsHeightChanged(int bottomControlsHeight) {}
+    public void onBottomControlsHeightChanged(int bottomControlsHeight) {
+        mModel.set(
+                BrowsingModeBottomToolbarModel.COMPOSITED_VIEW_VISIBLE, bottomControlsHeight != 0);
+    }
 
     @Override
     public void onOverviewModeStartedShowing(boolean showToolbar) {
@@ -236,17 +238,14 @@
         // The toolbars are force shown when the keyboard is visible, so we can blindly set
         // the bottom toolbar view to visible or invisible regardless of the previous state.
         if (isShowing) {
-            mBrowsingModeBottomToolbarHeightBeforeHide =
-                    mFullscreenManager.getBottomControlsHeight();
             mModel.set(BrowsingModeBottomToolbarModel.ANDROID_VIEW_VISIBLE, false);
-            mModel.set(BrowsingModeBottomToolbarModel.COMPOSITED_VIEW_VISIBLE, false);
             mFullscreenManager.setBottomControlsHeight(0);
         } else {
-            mFullscreenManager.setBottomControlsHeight(mBrowsingModeBottomToolbarHeightBeforeHide);
+            mFullscreenManager.setBottomControlsHeight(
+                    mIsHiddenForAdaptiveToolbar ? 0 : mBottomToolbarHeight);
             tryShowingAndroidView();
             mModel.set(BrowsingModeBottomToolbarModel.Y_OFFSET,
                     (int) mFullscreenManager.getBottomControlOffset());
-            mModel.set(BrowsingModeBottomToolbarModel.COMPOSITED_VIEW_VISIBLE, true);
         }
     }
 
@@ -276,9 +275,17 @@
      * hidden.
      */
     private void tryShowingAndroidView() {
+        if (mIsHiddenForAdaptiveToolbar) return;
         if (mFullscreenManager.getBottomControlOffset() > 0) return;
         if (mIsOverlayPanelShowing) return;
         if (mModel.get(BrowsingModeBottomToolbarModel.Y_OFFSET) != 0) return;
         mModel.set(BrowsingModeBottomToolbarModel.ANDROID_VIEW_VISIBLE, true);
     }
+
+    public void setVisible(boolean isVisible) {
+        mIsHiddenForAdaptiveToolbar = !isVisible;
+        mFullscreenManager.setBottomControlsHeight(
+                mIsHiddenForAdaptiveToolbar ? 0 : mBottomToolbarHeight);
+        mFullscreenManager.updateViewportSize();
+    }
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/bottom/BrowsingModeBottomToolbarViewBinder.java b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/bottom/BrowsingModeBottomToolbarViewBinder.java
index abccabc..40eb7dd 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/bottom/BrowsingModeBottomToolbarViewBinder.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/bottom/BrowsingModeBottomToolbarViewBinder.java
@@ -58,20 +58,27 @@
                             ? View.VISIBLE
                             : View.INVISIBLE);
         } else if (BrowsingModeBottomToolbarModel.COMPOSITED_VIEW_VISIBLE == propertyKey) {
-            view.sceneLayer.setIsVisible(
-                    model.get(BrowsingModeBottomToolbarModel.COMPOSITED_VIEW_VISIBLE));
+            if (view.sceneLayer == null) return;
+            final boolean showCompositedView =
+                    model.get(BrowsingModeBottomToolbarModel.COMPOSITED_VIEW_VISIBLE);
+            view.sceneLayer.setIsVisible(showCompositedView);
+            model.get(BrowsingModeBottomToolbarModel.TOOLBAR_SWIPE_LAYOUT)
+                    .setBottomToolbarSceneLayersVisibility(showCompositedView);
             model.get(BrowsingModeBottomToolbarModel.LAYOUT_MANAGER).requestUpdate();
         } else if (BrowsingModeBottomToolbarModel.LAYOUT_MANAGER == propertyKey) {
             assert view.sceneLayer == null;
             view.sceneLayer = new ScrollingBottomViewSceneLayer(
                     view.toolbarRoot, view.toolbarRoot.getTopShadowHeight());
+            view.sceneLayer.setIsVisible(
+                    model.get(BrowsingModeBottomToolbarModel.COMPOSITED_VIEW_VISIBLE));
             model.get(BrowsingModeBottomToolbarModel.LAYOUT_MANAGER)
                     .addSceneOverlayToBack(view.sceneLayer);
         } else if (BrowsingModeBottomToolbarModel.TOOLBAR_SWIPE_LAYOUT == propertyKey) {
             assert view.sceneLayer != null;
             model.get(BrowsingModeBottomToolbarModel.TOOLBAR_SWIPE_LAYOUT)
                     .setBottomToolbarSceneLayers(new ScrollingBottomViewSceneLayer(view.sceneLayer),
-                            new ScrollingBottomViewSceneLayer(view.sceneLayer));
+                            new ScrollingBottomViewSceneLayer(view.sceneLayer),
+                            model.get(BrowsingModeBottomToolbarModel.COMPOSITED_VIEW_VISIBLE));
         } else if (BrowsingModeBottomToolbarModel.RESOURCE_MANAGER == propertyKey) {
             model.get(BrowsingModeBottomToolbarModel.RESOURCE_MANAGER)
                     .getDynamicResourceLoader()
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/top/CustomTabToolbar.java b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/top/CustomTabToolbar.java
index 446391d..5a53ec3 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/top/CustomTabToolbar.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/top/CustomTabToolbar.java
@@ -57,6 +57,7 @@
 import org.chromium.chrome.browser.page_info.PageInfoController;
 import org.chromium.chrome.browser.profiles.Profile;
 import org.chromium.chrome.browser.tab.Tab;
+import org.chromium.chrome.browser.tab.TrustedCdn;
 import org.chromium.chrome.browser.toolbar.ToolbarDataProvider;
 import org.chromium.chrome.browser.toolbar.ToolbarTabController;
 import org.chromium.chrome.browser.util.AccessibilityUtil;
@@ -350,7 +351,7 @@
         Tab tab = getToolbarDataProvider().getTab();
         if (tab == null) return null;
 
-        String publisherUrl = tab.getTrustedCdnPublisherUrl();
+        String publisherUrl = TrustedCdn.getPublisherUrl(tab);
         if (publisherUrl != null) return extractPublisherFromPublisherUrl(publisherUrl);
 
         // TODO(bauerb): Remove this once trusted CDN publisher URLs have rolled out completely.
@@ -407,14 +408,15 @@
 
     @Override
     public void setUrlToPageUrl() {
-        if (getCurrentTab() == null) {
+        Tab tab = getCurrentTab();
+        if (tab == null) {
             mUrlCoordinator.setUrlBarData(
                     UrlBarData.EMPTY, UrlBar.ScrollType.NO_SCROLL, SelectionState.SELECT_ALL);
             return;
         }
 
-        String publisherUrl = getCurrentTab().getTrustedCdnPublisherUrl();
-        String url = publisherUrl != null ? publisherUrl : getCurrentTab().getUrl().trim();
+        String publisherUrl = TrustedCdn.getPublisherUrl(tab);
+        String url = publisherUrl != null ? publisherUrl : tab.getUrl().trim();
         if (mState == STATE_TITLE_ONLY) {
             if (!TextUtils.isEmpty(getToolbarDataProvider().getTitle())) setTitleToPageTitle();
         }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/top/ToolbarLayout.java b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/top/ToolbarLayout.java
index 52f6e916..d7865c8 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/top/ToolbarLayout.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/top/ToolbarLayout.java
@@ -140,6 +140,11 @@
     }
 
     /**
+     * @param isVisible Whether the bottom toolbar is visible.
+     */
+    void onBottomToolbarVisibilityChanged(boolean isVisible) {}
+
+    /**
      * Disable the menu button. This removes the view from the hierarchy and nulls the related
      * instance vars.
      */
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/top/ToolbarPhone.java b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/top/ToolbarPhone.java
index c6bd7932..841e6b0 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/top/ToolbarPhone.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/top/ToolbarPhone.java
@@ -75,7 +75,6 @@
 import org.chromium.chrome.browser.util.ViewUtils;
 import org.chromium.chrome.browser.widget.animation.CancelAwareAnimatorListener;
 import org.chromium.components.feature_engagement.EventConstants;
-import org.chromium.ui.UiUtils;
 import org.chromium.ui.base.LocalizationUtils;
 import org.chromium.ui.interpolators.BakedBezierInterpolator;
 
@@ -132,7 +131,7 @@
 
     protected LocationBarPhone mLocationBar;
 
-    protected ViewGroup mToolbarButtonsContainer;
+    private ViewGroup mToolbarButtonsContainer;
     protected @Nullable ToggleTabStackButton mToggleTabStackButton;
     protected @Nullable ImageButton mHomeButton;
     private TextView mUrlBar;
@@ -254,6 +253,15 @@
     private int mCurrentLocationBarColor;
 
     /**
+     * Whether the bottom toolbar is visible. If it is visible then the top toolbar's home,
+     *  tab switcher, and menu buttons should be hidden.
+     */
+    private boolean mIsBottomToolbarVisible;
+
+    /** Whether the bottom toolbar was visible for the last texture capture. */
+    private boolean mWasBottomToolbarVisibleForLastTextureCapture;
+
+    /**
      * Used to specify the visual state of the toolbar.
      */
     @IntDef({VisualState.NORMAL, VisualState.INCOGNITO, VisualState.BRAND_COLOR,
@@ -358,13 +366,6 @@
             mToolbarButtonsContainer = (ViewGroup) findViewById(R.id.toolbar_buttons);
 
             mHomeButton = findViewById(R.id.home_button);
-            if (FeatureUtilities.isBottomToolbarEnabled()) {
-                disableMenuButton();
-                if (mHomeButton != null) {
-                    UiUtils.removeViewFromParent(mHomeButton);
-                    mHomeButton = null;
-                }
-            }
 
             mUrlBar = (TextView) findViewById(R.id.url_bar);
 
@@ -377,7 +378,10 @@
 
             setLayoutTransition(null);
 
-            if (getMenuButtonWrapper() != null) getMenuButtonWrapper().setVisibility(View.VISIBLE);
+            if (getMenuButtonWrapper() != null && !mIsBottomToolbarVisible) {
+                getMenuButtonWrapper().setVisibility(View.VISIBLE);
+            }
+
             inflateTabSwitchingResources();
 
             setWillNotDraw(false);
@@ -432,12 +436,7 @@
 
     private void inflateTabSwitchingResources() {
         mToggleTabStackButton = findViewById(R.id.tab_switcher_button);
-        if (FeatureUtilities.isBottomToolbarEnabled()) {
-            UiUtils.removeViewFromParent(mToggleTabStackButton);
-            mToggleTabStackButton = null;
-        } else {
-            mToggleTabStackButton.setClickable(false);
-        }
+        mToggleTabStackButton.setClickable(false);
     }
 
     private void enableTabSwitchingResources() {
@@ -468,7 +467,7 @@
 
         getLocationBar().onNativeLibraryReady();
 
-        if (!FeatureUtilities.isBottomToolbarEnabled()) enableTabSwitchingResources();
+        enableTabSwitchingResources();
 
         if (mHomeButton != null) {
             mHomeButton.setOnClickListener(this);
@@ -492,7 +491,8 @@
                 }
             });
         onHomeButtonUpdate(HomepageManager.isHomepageEnabled()
-                || FeatureUtilities.isNewTabPageButtonEnabled());
+                || FeatureUtilities.isNewTabPageButtonEnabled()
+                || FeatureUtilities.isBottomToolbarEnabled());
 
         setTabSwitcherAnimationMenuDrawable();
         updateVisualsForLocationBarState();
@@ -926,10 +926,12 @@
         // toolbar is not visible (e.g. in tab switcher mode).
         if (isInTabSwitcherMode()) return;
 
-        int toolbarButtonVisibility = getToolbarButtonVisibility();
-        mToolbarButtonsContainer.setVisibility(toolbarButtonVisibility);
-        if (mHomeButton != null && mHomeButton.getVisibility() != GONE) {
-            mHomeButton.setVisibility(toolbarButtonVisibility);
+        if (!mIsBottomToolbarVisible) {
+            int toolbarButtonVisibility = getToolbarButtonVisibility();
+            mToolbarButtonsContainer.setVisibility(toolbarButtonVisibility);
+            if (mHomeButton != null && mHomeButton.getVisibility() != GONE) {
+                mHomeButton.setVisibility(toolbarButtonVisibility);
+            }
         }
 
         updateLocationBarLayoutForExpansionAnimation();
@@ -1246,7 +1248,7 @@
 
         // Draw the tab stack button and associated text.
         if (mTabSwitcherAnimationTabStackDrawable != null && mToggleTabStackButton != null
-                && mUrlExpansionPercent != 1f) {
+                && !mIsBottomToolbarVisible && mUrlExpansionPercent != 1f) {
             // Draw the tab stack button image.
             canvas.save();
             translateCanvasToView(mToolbarButtonsContainer, mToggleTabStackButton, canvas);
@@ -1265,6 +1267,8 @@
             backgroundTop += mToggleTabStackButton.getPaddingTop();
             canvas.translate(backgroundLeft, backgroundTop);
 
+            mTabSwitcherAnimationTabStackDrawable.setBounds(
+                    mToggleTabStackButton.getDrawable().getBounds());
             mTabSwitcherAnimationTabStackDrawable.setAlpha(rgbAlpha);
             mTabSwitcherAnimationTabStackDrawable.draw(canvas);
             canvas.restore();
@@ -1272,7 +1276,7 @@
 
         // Draw the menu button if necessary.
         final ImageButton menuButton = getMenuButton();
-        if (menuButton != null && !isShowingAppMenuUpdateBadge()
+        if (menuButton != null && !mIsBottomToolbarVisible && !isShowingAppMenuUpdateBadge()
                 && mTabSwitcherAnimationMenuDrawable != null && mUrlExpansionPercent != 1f) {
             mTabSwitcherAnimationMenuDrawable.setBounds(menuButton.getPaddingLeft(),
                     menuButton.getPaddingTop(),
@@ -1292,8 +1296,8 @@
                 : mTabSwitcherAnimationMenuBadgeDarkDrawable;
 
         final View menuBadge = getMenuBadge();
-        if (menuBadge != null && isShowingAppMenuUpdateBadge() && badgeDrawable != null
-                && mUrlExpansionPercent != 1f) {
+        if (menuBadge != null && !mIsBottomToolbarVisible && isShowingAppMenuUpdateBadge()
+                && badgeDrawable != null && mUrlExpansionPercent != 1f) {
             badgeDrawable.setBounds(menuBadge.getPaddingLeft(), menuBadge.getPaddingTop(),
                     menuBadge.getWidth() - menuBadge.getPaddingRight(),
                     menuBadge.getHeight() - menuBadge.getPaddingBottom());
@@ -1303,8 +1307,10 @@
         }
 
         mLightDrawablesUsedForLastTextureCapture = mUseLightDrawablesForTextureCapture;
+        mWasBottomToolbarVisibleForLastTextureCapture = mIsBottomToolbarVisible;
 
-        if (mTabSwitcherAnimationTabStackDrawable != null && mToggleTabStackButton != null) {
+        if (mTabSwitcherAnimationTabStackDrawable != null && mToggleTabStackButton != null
+                && !mIsBottomToolbarVisible) {
             mTabCountForLastTextureCapture = mTabSwitcherAnimationTabStackDrawable.getTabCount();
         }
 
@@ -1538,7 +1544,8 @@
             // Only force a texture capture if the tint for the toolbar drawables is changing or
             // if the tab count has changed since the last texture capture.
             mForceTextureCapture =
-                    mLightDrawablesUsedForLastTextureCapture != mUseLightDrawablesForTextureCapture;
+                    mLightDrawablesUsedForLastTextureCapture != mUseLightDrawablesForTextureCapture
+                    || mWasBottomToolbarVisibleForLastTextureCapture != mIsBottomToolbarVisible;
 
             if (mTabSwitcherAnimationTabStackDrawable != null && mToggleTabStackButton != null) {
                 mForceTextureCapture = mForceTextureCapture
@@ -1601,6 +1608,7 @@
         boolean hideHomeButton = FeatureUtilities.isNewTabPageButtonEnabled()
                 ? isNTP || isIncognito()
                 : !mIsHomeButtonEnabled;
+        if (mIsBottomToolbarVisible) hideHomeButton = true;
         if (hideHomeButton) {
             removeHomeButton();
         } else {
@@ -1903,7 +1911,7 @@
                 MathUtils.flipSignIf(URL_FOCUS_TOOLBAR_BUTTONS_TRANSLATION_X_DP, isRtl) * density;
 
         final View menuButtonWrapper = getMenuButtonWrapper();
-        if (menuButtonWrapper != null) {
+        if (menuButtonWrapper != null && !mIsBottomToolbarVisible) {
             animator = ObjectAnimator.ofFloat(
                     menuButtonWrapper, TRANSLATION_X, toolbarButtonTranslationX);
             animator.setDuration(URL_FOCUS_TOOLBAR_BUTTONS_DURATION_MS);
@@ -1916,7 +1924,7 @@
             animators.add(animator);
         }
 
-        if (mToggleTabStackButton != null) {
+        if (mToggleTabStackButton != null && !mIsBottomToolbarVisible) {
             animator = ObjectAnimator.ofFloat(
                     mToggleTabStackButton, TRANSLATION_X, toolbarButtonTranslationX);
             animator.setDuration(URL_FOCUS_TOOLBAR_BUTTONS_DURATION_MS);
@@ -1955,7 +1963,7 @@
         animators.add(animator);
 
         final View menuButtonWrapper = getMenuButtonWrapper();
-        if (menuButtonWrapper != null) {
+        if (menuButtonWrapper != null && !mIsBottomToolbarVisible) {
             animator = ObjectAnimator.ofFloat(menuButtonWrapper, TRANSLATION_X, 0);
             animator.setDuration(URL_FOCUS_TOOLBAR_BUTTONS_DURATION_MS);
             animator.setStartDelay(URL_CLEAR_FOCUS_MENU_DELAY_MS);
@@ -1969,7 +1977,7 @@
             animators.add(animator);
         }
 
-        if (mToggleTabStackButton != null) {
+        if (mToggleTabStackButton != null && !mIsBottomToolbarVisible) {
             animator = ObjectAnimator.ofFloat(mToggleTabStackButton, TRANSLATION_X, 0);
             animator.setDuration(URL_FOCUS_TOOLBAR_BUTTONS_DURATION_MS);
             animator.setStartDelay(URL_CLEAR_FOCUS_TABSTACK_DELAY_MS);
@@ -2098,8 +2106,6 @@
                     getContext(), useTabStackDrawableLight);
             int[] stateSet = {android.R.attr.state_enabled};
             mTabSwitcherAnimationTabStackDrawable.setState(stateSet);
-            mTabSwitcherAnimationTabStackDrawable.setBounds(
-                    mToggleTabStackButton.getDrawable().getBounds());
             mIsOverlayTabStackDrawableLight = useTabStackDrawableLight;
         }
 
@@ -2418,7 +2424,7 @@
 
         if (getMenuButtonWrapper() != null) {
             setMenuButtonHighlightDrawable();
-            getMenuButtonWrapper().setVisibility(View.VISIBLE);
+            if (!mIsBottomToolbarVisible) getMenuButtonWrapper().setVisibility(View.VISIBLE);
         }
 
         DrawableCompat.setTint(mLocationBarBackground,
@@ -2504,7 +2510,9 @@
      * buttons besides the experimental button.
      */
     private boolean isMenuButtonPresent() {
-        return getMenuButton() != null;
+        final ImageButton menuButton = getMenuButton();
+        if (menuButton != null) return false;
+        return menuButton.isShown();
     }
 
     private void requestLayoutHostUpdateForExperimentalButton() {
@@ -2739,4 +2747,15 @@
             return mDrawnByNtp ? super.getCallback() : mCallback;
         }
     }
+
+    @Override
+    public void onBottomToolbarVisibilityChanged(boolean isVisible) {
+        mIsBottomToolbarVisible = isVisible;
+
+        final int visibility = isVisible ? GONE : VISIBLE;
+        mToggleTabStackButton.setVisibility(visibility);
+        getMenuButtonWrapper().setVisibility(visibility);
+        updateButtonVisibility();
+        mToolbarButtonsContainer.requestLayout();
+    }
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/top/TopToolbarCoordinator.java b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/top/TopToolbarCoordinator.java
index 4fd4c4a..1cb644f2 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/top/TopToolbarCoordinator.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/top/TopToolbarCoordinator.java
@@ -579,6 +579,14 @@
     }
 
     /**
+     * @param isVisible Whether the bottom toolbar is visible.
+     */
+    public void onBottomToolbarVisibilityChanged(boolean isVisible) {
+        mToolbarProvider.whenLoaded(
+                (toolbar) -> toolbar.onBottomToolbarVisibilityChanged(isVisible));
+    }
+
+    /**
      * @return The experimental toolbar button if it exists.
      */
     public @Nullable View getExperimentalButtonView() {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/usage_stats/PageViewObserver.java b/chrome/android/java/src/org/chromium/chrome/browser/usage_stats/PageViewObserver.java
index cb798a4..11c0f410 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/usage_stats/PageViewObserver.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/usage_stats/PageViewObserver.java
@@ -37,20 +37,25 @@
     private final TabObserver mTabObserver;
     private final EventTracker mEventTracker;
     private final TokenTracker mTokenTracker;
+    private final SuspensionTracker mSuspensionTracker;
 
     private Tab mCurrentTab;
     private String mLastFqdn;
 
     public PageViewObserver(Activity activity, TabModelSelector tabModelSelector,
-            EventTracker eventTracker, TokenTracker tokenTracker) {
+            EventTracker eventTracker, TokenTracker tokenTracker,
+            SuspensionTracker suspensionTracker) {
         mActivity = activity;
         mTabModelSelector = tabModelSelector;
         mEventTracker = eventTracker;
         mTokenTracker = tokenTracker;
+        mSuspensionTracker = suspensionTracker;
         mTabObserver = new EmptyTabObserver() {
             @Override
             public void onShown(Tab tab, @TabSelectionType int type) {
-                updateUrl(tab.getUrl());
+                if (!tab.isLoading() && !tab.isBeingRestored()) {
+                    updateUrl(tab.getUrl());
+                }
             }
 
             @Override
@@ -102,6 +107,13 @@
 
     private void updateUrl(String newUrl) {
         String newFqdn = newUrl == null ? "" : Uri.parse(newUrl).getHost();
+
+        boolean didSuspend = false;
+        if (newFqdn != null && mSuspensionTracker.isWebsiteSuspended(newFqdn)) {
+            SuspendedTab.create(mCurrentTab).show();
+            didSuspend = true;
+        }
+
         if (mLastFqdn != null && mLastFqdn.equals(newFqdn)) return;
 
         if (mLastFqdn != null) {
@@ -111,7 +123,7 @@
             mLastFqdn = null;
         }
 
-        if (!URLUtil.isHttpUrl(newUrl) && !URLUtil.isHttpsUrl(newUrl)) return;
+        if (!URLUtil.isHttpUrl(newUrl) && !URLUtil.isHttpsUrl(newUrl) || didSuspend) return;
 
         mLastFqdn = newFqdn;
         mEventTracker.addWebsiteEvent(new WebsiteEvent(
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/usage_stats/SuspendedTab.java b/chrome/android/java/src/org/chromium/chrome/browser/usage_stats/SuspendedTab.java
new file mode 100644
index 0000000..dab2b05
--- /dev/null
+++ b/chrome/android/java/src/org/chromium/chrome/browser/usage_stats/SuspendedTab.java
@@ -0,0 +1,130 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.chrome.browser.usage_stats;
+
+import android.content.Context;
+import android.content.Intent;
+import android.net.Uri;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.view.ViewGroup;
+import android.widget.LinearLayout;
+import android.widget.LinearLayout.LayoutParams;
+import android.widget.TextView;
+
+import org.chromium.chrome.R;
+import org.chromium.chrome.browser.tab.EmptyTabObserver;
+import org.chromium.chrome.browser.tab.Tab;
+import org.chromium.content_public.browser.LoadUrlParams;
+
+/**
+ * Represents the suspension page presented when a user tries to visit a site whose fully-qualified
+ * domain name (FQDN) has been suspended via Digital Wellbeing.
+ */
+public class SuspendedTab extends EmptyTabObserver {
+    private static final String DIGITAL_WELLBEING_DASHBOARD_ACTION =
+            "com.google.android.apps.wellbeing.action.APP_USAGE_DASHBOARD";
+
+    private final Tab mTab;
+    private View mView;
+
+    public static SuspendedTab create(Tab tab) {
+        return new SuspendedTab(tab);
+    }
+
+    private SuspendedTab(Tab tab) {
+        mTab = tab;
+        mTab.addObserver(this);
+    }
+
+    /**
+     * Show the suspended tab UI within the root view of the associated tab. This will stop loading
+     * of mTab so that the page is not also rendered.
+     */
+    public void show() {
+        if (mTab.getWebContents() == null) return;
+
+        mTab.stopLoading();
+        attachView();
+    }
+
+    private View createView() {
+        Context context = mTab.getContext();
+        LayoutInflater inflater = LayoutInflater.from(context);
+
+        String fqdn = Uri.parse(mTab.getUrl()).getHost();
+        View suspendedTabView = inflater.inflate(R.layout.suspended_tab, null);
+        TextView explanationText =
+                (TextView) suspendedTabView.findViewById(R.id.suspended_tab_explanation);
+        explanationText.setText(
+                context.getString(R.string.usage_stats_site_paused_explanation, fqdn));
+
+        View settingsLink = suspendedTabView.findViewById(R.id.suspended_tab_settings_button);
+        settingsLink.setOnClickListener(new OnClickListener() {
+            @Override
+            public void onClick(View v) {
+                Intent intent = new Intent(DIGITAL_WELLBEING_DASHBOARD_ACTION);
+                intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+                context.startActivity(intent);
+            }
+        });
+
+        return suspendedTabView;
+    }
+
+    private void attachView() {
+        assert mView == null;
+
+        ViewGroup parent = mTab.getContentView();
+        mView = createView();
+        parent.addView(mView,
+                new LinearLayout.LayoutParams(
+                        LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT));
+    }
+
+    private void removeIfPresent() {
+        removeViewIfPresent();
+
+        mTab.removeObserver(this);
+        mView = null;
+    }
+
+    private void removeViewIfPresent() {
+        if (isShowing()) {
+            mTab.getContentView().removeView(mView);
+        }
+    }
+
+    private boolean isShowing() {
+        return mView != null && mView.getParent() == mTab.getContentView();
+    }
+
+    // TabObserver implementation.
+    @Override
+    public void onLoadUrl(Tab tab, LoadUrlParams params, int loadType) {
+        removeIfPresent();
+    }
+
+    @Override
+    public void onPageLoadStarted(Tab tab, String url) {
+        removeIfPresent();
+    }
+
+    @Override
+    public void onDestroyed(Tab tab) {
+        removeIfPresent();
+    }
+
+    // TODO(pnoland): Add integration tests for SuspendedTab that exercise this multi-window logic.
+    @Override
+    public void onActivityAttachmentChanged(Tab tab, boolean isAttached) {
+        if (!isAttached) {
+            removeViewIfPresent();
+        } else {
+            attachView();
+        }
+    }
+}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/usage_stats/UsageStatsService.java b/chrome/android/java/src/org/chromium/chrome/browser/usage_stats/UsageStatsService.java
index aafc000f..386a04a 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/usage_stats/UsageStatsService.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/usage_stats/UsageStatsService.java
@@ -56,7 +56,8 @@
     public PageViewObserver createPageViewObserver(
             TabModelSelector tabModelSelector, Activity activity) {
         ThreadUtils.assertOnUiThread();
-        return new PageViewObserver(activity, tabModelSelector, mEventTracker, mTokenTracker);
+        return new PageViewObserver(
+                activity, tabModelSelector, mEventTracker, mTokenTracker, mSuspensionTracker);
     }
 
     /** @return Whether the user has authorized DW to access usage stats data. */
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/util/ColorUtils.java b/chrome/android/java/src/org/chromium/chrome/browser/util/ColorUtils.java
index 812c9d9..244930a 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/util/ColorUtils.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/util/ColorUtils.java
@@ -85,18 +85,18 @@
     /**
      * Returns the icon tint resource to use based on the current parameters and whether the app is
      * in night mode.
-     * @param useLight Whether or not the icon tint should be light.
+     * @param useLight Whether or not the icon tint should be light when not in night mode.
      * @return The {@link ColorRes} for the icon tint.
      */
     public static @ColorRes int getIconTintRes(boolean useLight) {
-        return useLight ? R.color.standard_mode_tint : R.color.tint_on_dark_bg;
+        return useLight ? R.color.tint_on_dark_bg : R.color.standard_mode_tint;
     }
 
     /**
      * Returns the icon tint to use based on the current parameters and whether the app is in night
      * mode.
      * @param context The {@link Context} used to retrieve colors.
-     * @param useLight Whether or not the icon tint should be light.
+     * @param useLight Whether or not the icon tint should be light when not in night mode.
      * @return The {@link ColorStateList} for the icon tint.
      */
     public static ColorStateList getIconTint(Context context, boolean useLight) {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/util/FeatureUtilities.java b/chrome/android/java/src/org/chromium/chrome/browser/util/FeatureUtilities.java
index a1485fe9..a7cdcca8 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/util/FeatureUtilities.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/util/FeatureUtilities.java
@@ -75,6 +75,7 @@
     private static Boolean sIsHomepageTileEnabled;
     private static Boolean sIsNewTabPageButtonEnabled;
     private static Boolean sIsBottomToolbarEnabled;
+    private static Boolean sIsAdaptiveToolbarEnabled;
     private static Boolean sShouldInflateToolbarOnBackgroundThread;
     private static Boolean sIsNightModeAvailable;
 
@@ -190,6 +191,7 @@
         cacheHomepageTileEnabled();
         cacheNewTabPageButtonEnabled();
         cacheBottomToolbarEnabled();
+        cacheAdaptiveToolbarEnabled();
         cacheInflateToolbarOnBackgroundThread();
         cacheNightModeAvailable();
         cacheDownloadAutoResumptionEnabledInNative();
@@ -349,6 +351,16 @@
     }
 
     /**
+     * Cache whether or not the adaptive toolbar is enabled so on next startup, the value can
+     * be made available immediately.
+     */
+    public static void cacheAdaptiveToolbarEnabled() {
+        ChromePreferenceManager.getInstance().writeBoolean(
+                ChromePreferenceManager.ADAPTIVE_TOOLBAR_ENABLED_KEY,
+                ChromeFeatureList.isEnabled(ChromeFeatureList.CHROME_DUET_ADAPTIVE));
+    }
+
+    /**
      * Cache whether or not download auto-resumptions are enabled in native so on next startup, the
      * value can be made available immediately.
      */
@@ -374,6 +386,19 @@
     }
 
     /**
+     * @return Whether or not the adaptive toolbar is enabled.
+     */
+    public static boolean isAdaptiveToolbarEnabled() {
+        if (sIsAdaptiveToolbarEnabled == null) {
+            ChromePreferenceManager prefManager = ChromePreferenceManager.getInstance();
+
+            sIsAdaptiveToolbarEnabled = prefManager.readBoolean(
+                    ChromePreferenceManager.ADAPTIVE_TOOLBAR_ENABLED_KEY, true);
+        }
+        return sIsAdaptiveToolbarEnabled;
+    }
+
+    /**
      * Cache whether or not night mode is available (i.e. night mode experiment is enabled) so on
      * next startup, the value can be made available immediately.
      */
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/util/ViewUtils.java b/chrome/android/java/src/org/chromium/chrome/browser/util/ViewUtils.java
index c5d75ab..917fc4a 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/util/ViewUtils.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/util/ViewUtils.java
@@ -177,12 +177,13 @@
 
     /**
      * Creates a {@link RoundedIconGenerator} that uses default styles.
+     * @param resources The {@link Resources} for accessing color and dimen resources.
      * @param circularIcon Whether the generated icons should be circles.
      * @return A {@link RoundedIconGenerator} that uses the default rounded icon style. Intended for
      *         monograms, e.g. a rounded rectangle or a circle with character(s) in the center.
      */
-    public static RoundedIconGenerator createDefaultRoundedIconGenerator(boolean circularIcon) {
-        Resources resources = ContextUtils.getApplicationContext().getResources();
+    public static RoundedIconGenerator createDefaultRoundedIconGenerator(
+            Resources resources, boolean circularIcon) {
         int iconColor =
                 ApiCompatibilityUtils.getColor(resources, R.color.default_favicon_background_color);
         int displayedIconSize;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/widget/PulseDrawable.java b/chrome/android/java/src/org/chromium/chrome/browser/widget/PulseDrawable.java
index 769af18..6ae271c 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/widget/PulseDrawable.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/widget/PulseDrawable.java
@@ -21,7 +21,6 @@
 import android.view.animation.Interpolator;
 
 import org.chromium.base.ApiCompatibilityUtils;
-import org.chromium.base.ContextUtils;
 import org.chromium.chrome.R;
 import org.chromium.chrome.browser.util.MathUtils;
 
@@ -64,9 +63,10 @@
 
     /**
      * Creates a {@link PulseDrawable} that will fill the bounds with a pulsing color.
+     * @param context The {@link Context} under which the drawable is created.
      * @return A new {@link PulseDrawable} instance.
      */
-    public static PulseDrawable createHighlight() {
+    public static PulseDrawable createHighlight(Context context) {
         PulseDrawable.Painter painter = new PulseDrawable.Painter() {
             @Override
             public void modifyDrawable(PulseDrawable drawable, float interpolation) {
@@ -80,11 +80,12 @@
             }
         };
 
-        return new PulseDrawable(new FastOutSlowInInterpolator(), painter);
+        return new PulseDrawable(context, new FastOutSlowInInterpolator(), painter);
     }
 
     /**
      * Creates a {@link PulseDrawable} that will draw a pulsing circle inside the bounds.
+     * @param context The {@link Context} under which the drawable is created.
      * @return A new {@link PulseDrawable} instance.
      */
     public static PulseDrawable createCircle(Context context) {
@@ -111,8 +112,8 @@
             }
         };
 
-        PulseDrawable drawable =
-                new PulseDrawable(PathInterpolatorCompat.create(.8f, 0.f, .6f, 1.f), painter);
+        PulseDrawable drawable = new PulseDrawable(
+                context, PathInterpolatorCompat.create(.8f, 0.f, .6f, 1.f), painter);
         drawable.setAlpha(76);
         return drawable;
     }
@@ -136,22 +137,24 @@
 
     /**
      * Creates a new {@link PulseDrawable} instance.
+     * @param context The {@link Context} under which the drawable is created.
      * @param interpolator An {@link Interpolator} that defines how the pulse will fade in and out.
      * @param painter      The {@link Painter} that will be responsible for drawing the pulse.
      */
-    private PulseDrawable(Interpolator interpolator, Painter painter) {
+    private PulseDrawable(Context context, Interpolator interpolator, Painter painter) {
         this(new PulseState(interpolator, painter));
-        setUseLightPulseColor(false);
+        setUseLightPulseColor(context.getResources(), false);
     }
 
     private PulseDrawable(PulseState state) {
         mState = state;
     }
 
-    /** Whether or not to use a light or dark color for the pulse. */
-    public void setUseLightPulseColor(boolean useLightPulseColor) {
-        Resources resources = ContextUtils.getApplicationContext().getResources();
-
+    /**
+     * @param resources The {@link Resources} for accessing colors.
+     * @param useLightPulseColor Whether or not to use a light or dark color for the pulse.
+     * */
+    public void setUseLightPulseColor(Resources resources, boolean useLightPulseColor) {
         @ColorInt
         int color = ApiCompatibilityUtils.getColor(resources,
                 useLightPulseColor ? R.color.modern_secondary_color
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/widget/ThumbnailDiskStorage.java b/chrome/android/java/src/org/chromium/chrome/browser/widget/ThumbnailDiskStorage.java
index dc4c1c6..10a401c 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/widget/ThumbnailDiskStorage.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/widget/ThumbnailDiskStorage.java
@@ -20,6 +20,7 @@
 import org.chromium.base.StreamUtil;
 import org.chromium.base.ThreadUtils;
 import org.chromium.base.VisibleForTesting;
+import org.chromium.base.metrics.RecordHistogram;
 import org.chromium.base.task.AsyncTask;
 import org.chromium.base.task.BackgroundOnlyAsyncTask;
 import org.chromium.chrome.browser.util.ConversionUtils;
@@ -146,6 +147,9 @@
 
         @Override
         protected void onPostExecute(Bitmap bitmap) {
+            RecordHistogram.recordBooleanHistogram(
+                    "Android.ThumbnailDiskStorage.CachedBitmap.Found", bitmap != null);
+
             if (bitmap != null) {
                 onThumbnailRetrieved(mRequest.getContentId(), bitmap, mRequest.getIconSize());
                 return;
@@ -296,6 +300,9 @@
                 Log.e(TAG, "Error while reading from disk.", e);
             }
         }
+
+        RecordHistogram.recordMemoryKBHistogram("Android.ThumbnailDiskStorage.Size",
+                (int) (mSizeBytes / ConversionUtils.BYTES_PER_KILOBYTE));
     }
 
     /**
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/widget/ThumbnailProviderImpl.java b/chrome/android/java/src/org/chromium/chrome/browser/widget/ThumbnailProviderImpl.java
index c2c7de73..92f43123 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/widget/ThumbnailProviderImpl.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/widget/ThumbnailProviderImpl.java
@@ -12,6 +12,7 @@
 
 import org.chromium.base.DiscardableReferencePool;
 import org.chromium.base.ThreadUtils;
+import org.chromium.base.metrics.RecordHistogram;
 import org.chromium.base.task.PostTask;
 import org.chromium.chrome.browser.BitmapCache;
 import org.chromium.chrome.browser.util.ConversionUtils;
@@ -33,6 +34,8 @@
  *                    duplicating work to decode the same image for two different requests.
  */
 public class ThumbnailProviderImpl implements ThumbnailProvider, ThumbnailStorageDelegate {
+    public enum ClientType { DOWNLOAD_HOME, NTP_SUGGESTIONS }
+
     /** Default in-memory thumbnail cache size. */
     private static final int DEFAULT_MAX_CACHE_BYTES = 5 * ConversionUtils.BYTES_PER_MEGABYTE;
 
@@ -47,6 +50,9 @@
      */
     private BitmapCache mBitmapCache;
 
+    /** The client type of the client using this provider. */
+    private final ClientType mClient;
+
     /**
      * Tracks a set of Content Ids where thumbnail generation or retrieval failed.  This should
      * prevent making subsequent (potentially expensive) thumbnail generation requests when there
@@ -63,23 +69,29 @@
 
     private ThumbnailDiskStorage mStorage;
 
+    private int mCacheSizeMaxBytesUma;
+
     /**
      * Constructor to build the thumbnail provider with default thumbnail cache size.
      * @param referencePool The application's reference pool.
+     * @param client The associated client type.
      */
-    public ThumbnailProviderImpl(DiscardableReferencePool referencePool) {
-        this(referencePool, DEFAULT_MAX_CACHE_BYTES);
+    public ThumbnailProviderImpl(DiscardableReferencePool referencePool, ClientType client) {
+        this(referencePool, DEFAULT_MAX_CACHE_BYTES, client);
     }
 
     /**
      * Constructor to build the thumbnail provider.
      * @param referencePool The application's reference pool.
      * @param bitmapCacheSizeByte The size in bytes of the in-memory LRU bitmap cache.
+     * @param client The associated client type.
      */
-    public ThumbnailProviderImpl(DiscardableReferencePool referencePool, int bitmapCacheSizeByte) {
+    public ThumbnailProviderImpl(
+            DiscardableReferencePool referencePool, int bitmapCacheSizeByte, ClientType client) {
         ThreadUtils.assertOnUiThread();
         mBitmapCache = new BitmapCache(referencePool, bitmapCacheSizeByte);
         mStorage = ThumbnailDiskStorage.create(this);
+        mClient = client;
     }
 
     @Override
@@ -89,6 +101,7 @@
         mRequestQueue.clear();
 
         ThreadUtils.assertOnUiThread();
+        recordBitmapCacheSize();
         mStorage.destroy();
         mBitmapCache.destroy();
     }
@@ -151,6 +164,10 @@
         String key = getKey(contentId, bitmapSizePx);
         Bitmap cachedBitmap = mBitmapCache.getBitmap(key);
         assert cachedBitmap == null || !cachedBitmap.isRecycled();
+
+        RecordHistogram.recordBooleanHistogram(
+                "Android.ThumbnailProvider.CachedBitmap.Found." + getClientTypeUmaSuffix(mClient),
+                cachedBitmap != null);
         return cachedBitmap;
     }
 
@@ -217,6 +234,8 @@
             mBitmapCache.putBitmap(key, bitmap);
             mNoBitmapCache.remove(contentId);
             mCurrentRequest.onThumbnailRetrieved(contentId, bitmap);
+
+            mCacheSizeMaxBytesUma = Math.max(mCacheSizeMaxBytesUma, mBitmapCache.size());
         } else {
             mNoBitmapCache.put(contentId, NO_BITMAP_PLACEHOLDER);
             mCurrentRequest.onThumbnailRetrieved(contentId, null);
@@ -225,4 +244,22 @@
         mCurrentRequest = null;
         processQueue();
     }
+
+    private void recordBitmapCacheSize() {
+        RecordHistogram.recordMemoryKBHistogram(
+                "Android.ThumbnailProvider.BitmapCache.Size." + getClientTypeUmaSuffix(mClient),
+                mCacheSizeMaxBytesUma / ConversionUtils.BYTES_PER_KILOBYTE);
+    }
+
+    private static String getClientTypeUmaSuffix(ClientType clientType) {
+        switch (clientType) {
+            case DOWNLOAD_HOME:
+                return "DownloadHome";
+            case NTP_SUGGESTIONS:
+                return "NTPSnippets";
+            default:
+                assert false;
+                return "Other";
+        }
+    }
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/widget/ViewHighlighter.java b/chrome/android/java/src/org/chromium/chrome/browser/widget/ViewHighlighter.java
index eb2ded88..81ca0f440 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/widget/ViewHighlighter.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/widget/ViewHighlighter.java
@@ -40,11 +40,10 @@
                 : false;
         if (highlighted) return;
 
-        PulseDrawable pulseDrawable = circular
-                ? PulseDrawable.createCircle(ContextUtils.getApplicationContext())
-                : PulseDrawable.createHighlight();
+        PulseDrawable pulseDrawable = circular ? PulseDrawable.createCircle(view.getContext())
+                                               : PulseDrawable.createHighlight(view.getContext());
 
-        Resources resources = ContextUtils.getApplicationContext().getResources();
+        Resources resources = view.getContext().getResources();
         Drawable background = (Drawable) view.getBackground();
         if (background != null) {
             background = background.getConstantState().newDrawable(resources);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/widget/accessibility/AccessibilityTabModelListItem.java b/chrome/android/java/src/org/chromium/chrome/browser/widget/accessibility/AccessibilityTabModelListItem.java
index 3978958..785ed63 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/widget/accessibility/AccessibilityTabModelListItem.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/widget/accessibility/AccessibilityTabModelListItem.java
@@ -35,6 +35,7 @@
 import org.chromium.chrome.R;
 import org.chromium.chrome.browser.tab.EmptyTabObserver;
 import org.chromium.chrome.browser.tab.Tab;
+import org.chromium.chrome.browser.tab.TabFavicon;
 import org.chromium.chrome.browser.tab.TabObserver;
 import org.chromium.chrome.browser.util.ColorUtils;
 
@@ -333,7 +334,7 @@
 
     private void updateFavicon() {
         if (mTab != null) {
-            Bitmap bitmap = mTab.getFavicon();
+            Bitmap bitmap = TabFavicon.getBitmap(mTab);
             if (bitmap != null) {
                 // Don't tint favicon bitmaps.
                 ApiCompatibilityUtils.setImageTintList(mFaviconView, null);
diff --git a/chrome/android/java/strings/android_chrome_strings.grd b/chrome/android/java/strings/android_chrome_strings.grd
index 4db64bb..5b1d55c 100644
--- a/chrome/android/java/strings/android_chrome_strings.grd
+++ b/chrome/android/java/strings/android_chrome_strings.grd
@@ -4029,7 +4029,7 @@
         Assistant Triggered Checkout
       </message>
       <message name="IDS_AUTOFILL_ASSISTANT_GOOGLE_TERMS_DESCRIPTION" desc="Message linking to the Google terms and conditions for Google Assistant in Chrome.">
-        Chrome will send the site’s URL and content to Google. Your email and credit card type from Chrome Autofill will also be sent to Google to provide this service. Visit Chrome settings to turn off Google Assistant in Chrome and Chrome Autofill. <ph name="BEGIN_LINK">&lt;link&gt;</ph>Learn&#xA0;more<ph name="END_LINK">&lt;/link&gt;</ph>
+        Chrome will send the site’s URL and content as well as your email and credit card type saved in Chrome to Google. You can turn this off in Chrome settings. <ph name="BEGIN_LINK">&lt;link&gt;</ph>Learn&#xA0;more<ph name="END_LINK">&lt;/link&gt;</ph>
       </message>
       <message name="IDS_AUTOFILL_ASSISTANT_GOOGLE_TERMS_URL" desc="URL for Google Autofill Assistant Terms of Service" translateable="false">
         http://support.google.com/assistant?p=fast_checkout
@@ -4040,6 +4040,9 @@
       <message name="IDS_AUTOFILL_ASSISTANT_3RD_PARTY_TERMS_REVIEW" desc="Message that indicates that the user wants to review the terms and conditions of a 3rd party's domain, e.g., 'odeon.co.uk'.">
         Read and agree to the terms &amp; conditions on <ph name="BEGIN_BOLD">&lt;b&gt;</ph><ph name="DOMAIN">%1$s<ex>google.com</ex></ph><ph name="END_BOLD">&lt;/b&gt;</ph> later
       </message>
+      <message name="IDS_AUTOFILL_ASSISTANT_3RD_PARTY_PRIVACY_NOTICE" desc="Privacy notice telling users that autofill assistant will send personal data to a third party’s website.">
+        Chrome will send personal data you selected to <ph name="BEGIN_BOLD">&lt;b&gt;</ph><ph name="DOMAIN">%1$s<ex>google.com</ex></ph><ph name="END_BOLD">&lt;/b&gt;</ph>
+      </message>
       <!-- Usage Stats strings -->
       <message name="IDS_USAGE_STATS_CONSENT_TITLE" desc="Title for activity authorizing Digital Wellbeing to access Chrome usage data">
         Connect Chrome to Digital Wellbeing?
@@ -4050,8 +4053,11 @@
       <message name="IDS_USAGE_STATS_SETTING_TITLE" desc="Title for setting toggling Digital Wellbeing to access Chrome usage data">
         Connect to Digital Wellbeing
       </message>
-      <message name="IDS_AUTOFILL_ASSISTANT_3RD_PARTY_PRIVACY_NOTICE" desc="Privacy notice telling users that autofill assistant will send personal data to a third party’s website.">
-        Chrome will send personal data you selected to <ph name="BEGIN_BOLD">&lt;b&gt;</ph><ph name="DOMAIN">%1$s<ex>google.com</ex></ph><ph name="END_BOLD">&lt;/b&gt;</ph>
+      <message name="IDS_USAGE_STATS_SITE_PAUSED" desc="Message when a website is suspended due to exceeding a user-defined limit">
+        Site paused
+      </message>
+      <message name="IDS_USAGE_STATS_SITE_PAUSED_EXPLANATION" desc="Message when a website is suspended due to exceeding a user-defined limit">
+        Your <ph name="FQDN">%1$s</ph> timer ran out. It'll start again tomorrow.
       </message>
 
       <!-- Bottom Tab Grid strings -->
diff --git a/chrome/android/java/strings/android_chrome_strings_grd/IDS_USAGE_STATS_SETTINGS.png.sha1 b/chrome/android/java/strings/android_chrome_strings_grd/IDS_USAGE_STATS_SETTINGS.png.sha1
new file mode 100644
index 0000000..440aa77
--- /dev/null
+++ b/chrome/android/java/strings/android_chrome_strings_grd/IDS_USAGE_STATS_SETTINGS.png.sha1
@@ -0,0 +1 @@
+2a3ebe6e93a81a36dd04d7d2be1faf0bef1f140c
\ No newline at end of file
diff --git a/chrome/android/java/strings/android_chrome_strings_grd/IDS_USAGE_STATS_SITE_PAUSED.png.sha1 b/chrome/android/java/strings/android_chrome_strings_grd/IDS_USAGE_STATS_SITE_PAUSED.png.sha1
new file mode 100644
index 0000000..440aa77
--- /dev/null
+++ b/chrome/android/java/strings/android_chrome_strings_grd/IDS_USAGE_STATS_SITE_PAUSED.png.sha1
@@ -0,0 +1 @@
+2a3ebe6e93a81a36dd04d7d2be1faf0bef1f140c
\ No newline at end of file
diff --git a/chrome/android/java/strings/android_chrome_strings_grd/IDS_USAGE_STATS_SITE_PAUSED_EXPLANATION.png.sha1 b/chrome/android/java/strings/android_chrome_strings_grd/IDS_USAGE_STATS_SITE_PAUSED_EXPLANATION.png.sha1
new file mode 100644
index 0000000..440aa77
--- /dev/null
+++ b/chrome/android/java/strings/android_chrome_strings_grd/IDS_USAGE_STATS_SITE_PAUSED_EXPLANATION.png.sha1
@@ -0,0 +1 @@
+2a3ebe6e93a81a36dd04d7d2be1faf0bef1f140c
\ No newline at end of file
diff --git a/chrome/android/java_sources.gni b/chrome/android/java_sources.gni
index 736772b..10e9b43 100644
--- a/chrome/android/java_sources.gni
+++ b/chrome/android/java_sources.gni
@@ -1527,6 +1527,7 @@
   "java/src/org/chromium/chrome/browser/tab/TabContextMenuItemDelegate.java",
   "java/src/org/chromium/chrome/browser/tab/TabContextMenuPopulator.java",
   "java/src/org/chromium/chrome/browser/tab/TabDelegateFactory.java",
+  "java/src/org/chromium/chrome/browser/tab/TabFavicon.java",
   "java/src/org/chromium/chrome/browser/tab/TabFullscreenHandler.java",
   "java/src/org/chromium/chrome/browser/tab/TabGestureStateListener.java",
   "java/src/org/chromium/chrome/browser/tab/TabIdManager.java",
@@ -1542,6 +1543,7 @@
   "java/src/org/chromium/chrome/browser/tab/TabWebContentsDelegateAndroid.java",
   "java/src/org/chromium/chrome/browser/tab/TabWebContentsObserver.java",
   "java/src/org/chromium/chrome/browser/tab/TabWebContentsUserData.java",
+  "java/src/org/chromium/chrome/browser/tab/TrustedCdn.java",
   "java/src/org/chromium/chrome/browser/tabmodel/AsyncTabParams.java",
   "java/src/org/chromium/chrome/browser/tabmodel/AsyncTabParamsManager.java",
   "java/src/org/chromium/chrome/browser/tabmodel/ChromeTabCreator.java",
@@ -1590,9 +1592,14 @@
   "java/src/org/chromium/chrome/browser/tabmodel/document/TabDelegate.java",
   "java/src/org/chromium/chrome/browser/tasks/TasksUma.java",
   "java/src/org/chromium/chrome/browser/tasks/tab_groups/TabGroupUtils.java",
+  "java/src/org/chromium/chrome/browser/tasks/tab_list_ui/TabStripToolbarViewProperties.java",
+  "java/src/org/chromium/chrome/browser/tasks/tab_list_ui/BottomTabListToolbarView.java",
+  "java/src/org/chromium/chrome/browser/tasks/tab_list_ui/BottomTabStripToolbarViewBinder.java",
+  "java/src/org/chromium/chrome/browser/tasks/tab_list_ui/TabStripBottomToolbarMediator.java",
+  "java/src/org/chromium/chrome/browser/tasks/tab_list_ui/TabStripToolbarCoordinator.java",
+  "java/src/org/chromium/chrome/browser/tasks/tab_list_ui/TabStripBottomToolbarCoordinator.java",
   "java/src/org/chromium/chrome/browser/tasks/tab_list_ui/BottomTabGridSheetToolbarProperties.java",
   "java/src/org/chromium/chrome/browser/tasks/tab_list_ui/BottomTabGridSheetToolbarViewBinder.java",
-  "java/src/org/chromium/chrome/browser/tasks/tab_list_ui/BottomTabGridSheetToolbarView.java",
   "java/src/org/chromium/chrome/browser/tasks/tab_list_ui/BottomTabGridSheetToolbarCoordinator.java",
   "java/src/org/chromium/chrome/browser/tasks/tab_list_ui/BottomTabGridCoordinator.java",
   "java/src/org/chromium/chrome/browser/tasks/tab_list_ui/BottomTabGridMediator.java",
@@ -1665,6 +1672,7 @@
   "java/src/org/chromium/chrome/browser/upgrade/UpgradeIntentService.java",
   "java/src/org/chromium/chrome/browser/usage_stats/EventTracker.java",
   "java/src/org/chromium/chrome/browser/usage_stats/PageViewObserver.java",
+  "java/src/org/chromium/chrome/browser/usage_stats/SuspendedTab.java",
   "java/src/org/chromium/chrome/browser/usage_stats/SuspensionTracker.java",
   "java/src/org/chromium/chrome/browser/usage_stats/TokenGenerator.java",
   "java/src/org/chromium/chrome/browser/usage_stats/TokenTracker.java",
@@ -2388,6 +2396,7 @@
   "junit/src/org/chromium/chrome/browser/ShadowDeviceConditions.java",
   "junit/src/org/chromium/chrome/browser/ShortcutHelperTest.java",
   "junit/src/org/chromium/chrome/browser/SSLClientCertificateRequestTest.java",
+  "junit/src/org/chromium/chrome/browser/autofill/AutofillUiUtilsTest.java",
   "junit/src/org/chromium/chrome/browser/autofill/keyboard_accessory/AccessorySheetControllerTest.java",
   "junit/src/org/chromium/chrome/browser/autofill/keyboard_accessory/KeyboardAccessoryControllerTest.java",
   "junit/src/org/chromium/chrome/browser/autofill/keyboard_accessory/KeyboardAccessoryTabLayoutControllerTest.java",
diff --git a/chrome/android/javatests/AndroidManifest.xml b/chrome/android/javatests/AndroidManifest.xml
index 653b9b1c7..3150180f 100644
--- a/chrome/android/javatests/AndroidManifest.xml
+++ b/chrome/android/javatests/AndroidManifest.xml
@@ -51,6 +51,7 @@
             android:exported="true"/>
 
         <activity android:name="org.chromium.chrome.test.ui.DummyUiActivity"
+            android:theme="@style/Theme.Chromium.Activity"
             android:exported="true"/>
 {% endblock %}
 
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/autofill/keyboard_accessory/ManualFillingIntegrationTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/autofill/keyboard_accessory/ManualFillingIntegrationTest.java
index b99f679..70e2a84 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/autofill/keyboard_accessory/ManualFillingIntegrationTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/autofill/keyboard_accessory/ManualFillingIntegrationTest.java
@@ -224,12 +224,13 @@
                 "https://www.example.com/", "Alan Turing", "", "Street Ave 4", "", "Capitaltown",
                 "", "80666", "", "Disneyland", "1", "a.turing@enigma.com", "DE"));
 
-        // Focus the field to bring up the autofill popup. We force a accessory here because the
-        // autofill popup doesn't trigger on password fields.
+        // Focus the field to bring up the autofill popup.
+        mHelper.clickEmailField(false);
+        DropdownPopupWindowInterface popup = mHelper.waitForAutofillPopup("a.tu");
+
+        // Force a accessory here because the autofill popup doesn't trigger on password fields.
         mHelper.clickEmailField(true);
         mHelper.waitForKeyboardAccessoryToBeShown();
-
-        DropdownPopupWindowInterface popup = mHelper.waitForAutofillPopup("a.tu");
         assertThat(popup.isShowing(), is(true));
 
         // Click the tab to show the sheet and hide keyboard and popup.
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchManagerTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchManagerTest.java
index 0d91730..e33a43d 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchManagerTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchManagerTest.java
@@ -205,7 +205,6 @@
         // TODO(donnd): find a better way to wait for page-ready, or at least reduce the delay!
         Thread.sleep(ACTIVITY_STARTUP_DELAY_MS);
         mManager = mActivityTestRule.getActivity().getContextualSearchManager();
-        mManager.suppressContextualSearchForSmartSelection(false);
 
         Assert.assertNotNull(mManager);
         mPanel = mManager.getContextualSearchPanel();
@@ -3175,49 +3174,6 @@
     }
 
     /**
-     * Tests that Contextual Search is suppressed on long-press only when Smart Selection is
-     * enabled, and that the Observers always get notified that text was selected.
-     */
-    @Test
-    @SmallTest
-    @Feature({"ContextualSearch"})
-    public void testSmartSelectSuppressesAndNotifiesObservers()
-            throws InterruptedException, TimeoutException {
-        // Mark the user undecided so we won't allow sending surroundings.
-        mPolicy.overrideDecidedStateForTesting(false);
-        TestContextualSearchObserver observer = new TestContextualSearchObserver();
-        mManager.addObserver(observer);
-        mFakeServer.reset();
-
-        longPressNodeWithoutWaiting("search");
-        waitForSelectActionBarVisible();
-        waitForPanelToPeek();
-        Assert.assertEquals(1, observer.getShowRedactedCount());
-        Assert.assertEquals(1, observer.getShowCount());
-        Assert.assertEquals(0, observer.getHideCount());
-        mManager.removeObserver(observer);
-
-        tapBasePageToClosePanel();
-
-        // Tell the ContextualSearchManager that Smart Selection is enabled.
-        mManager.suppressContextualSearchForSmartSelection(true);
-        observer = new TestContextualSearchObserver();
-        mManager.addObserver(observer);
-        mFakeServer.reset();
-
-        longPressNodeWithoutWaiting("search");
-        waitForSelectActionBarVisible();
-        assertPanelClosedOrUndefined();
-        Assert.assertEquals(1, observer.getShowRedactedCount());
-        Assert.assertEquals(1, observer.getShowCount());
-        // Note that this test is flawed because it doesn't wait until the selection is cleared
-        // and verify that a Hide notification is sent.  This is a bug because no hide is ever
-        // sent in this case, but should be.  Details in https://crbug.com/878006.
-        Assert.assertEquals(0, observer.getHideCount());
-        mManager.removeObserver(observer);
-    }
-
-    /**
      * Tests that expanding the selection during a Search Term Resolve notifies the observers before
      * and after the expansion.
      */
@@ -3272,34 +3228,6 @@
     }
 
     /**
-     * Tests a second Tap when Smart Selection is enabled.
-     */
-    @Test
-    @SmallTest
-    @Feature({"ContextualSearch"})
-    public void testSecondTapWithSmartSelection() throws InterruptedException, TimeoutException {
-        mManager.suppressContextualSearchForSmartSelection(true);
-        TestContextualSearchObserver observer = new TestContextualSearchObserver();
-        mManager.addObserver(observer);
-
-        clickWordNode("search");
-        Assert.assertEquals(1, observer.getShowCount());
-        Assert.assertEquals(0, observer.getHideCount());
-
-        clickNode("search");
-        waitForSelectActionBarVisible();
-
-        // Second Tap closes the panel automatically when Smart Selection is active.
-        waitForPanelToClose();
-
-        // Sometimes we get an additional Show notification on the second Tap, but not reliably in
-        // tests.  See crbug.com/776541.
-        assertValueIs1or2(observer.getShowCount());
-        Assert.assertEquals(1, observer.getHideCount());
-        mManager.removeObserver(observer);
-    }
-
-    /**
      * Tests Tab reparenting.  When a tab moves from one activity to another the
      * ContextualSearchTabHelper should detect the change and handle gestures for it too.  This
      * happens with multiwindow modes.
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 103b827..4cc2adf 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
@@ -357,26 +357,4 @@
         Assert.assertEquals(mPanelManager.getRequestPanelShowCount(), 0);
         Assert.assertEquals(mPanelManager.getPanelHideCount(), 0);
     }
-
-    /**
-     * Tests that a Long-press gesture suppresses the panel when Smart Selection is enabled.
-     */
-    @Test
-    @SmallTest
-    @Feature({"ContextualSearch"})
-    @Restriction(Restriction.RESTRICTION_TYPE_NON_LOW_END_DEVICE)
-    public void testLongpressWithSmartSelectionSuppresses() {
-        Assert.assertEquals(mPanelManager.getRequestPanelShowCount(), 0);
-
-        // Tell the ContextualSearchManager that Smart Selection is enabled.
-        mContextualSearchManager.suppressContextualSearchForSmartSelection(true);
-
-        // Fake a selection event.
-        mockLongpressText("text");
-        // Generate the surrounding-text-available callback.
-        // Surrounding text is gathered for longpress due to icing integration.
-        generateTextSurroundingSelectionAvailable();
-
-        Assert.assertEquals(mPanelManager.getRequestPanelShowCount(), 0);
-    }
 }
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/customtabs/TrustedCdnPublisherUrlTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/customtabs/TrustedCdnPublisherUrlTest.java
index eb07b24..500a19f 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/customtabs/TrustedCdnPublisherUrlTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/customtabs/TrustedCdnPublisherUrlTest.java
@@ -51,6 +51,7 @@
 import org.chromium.chrome.browser.omnibox.UrlBar;
 import org.chromium.chrome.browser.profiles.Profile;
 import org.chromium.chrome.browser.tab.Tab;
+import org.chromium.chrome.browser.tab.TrustedCdn;
 import org.chromium.chrome.browser.test.ScreenShooter;
 import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
 import org.chromium.chrome.test.util.browser.Features;
@@ -281,7 +282,7 @@
         CustomTabActivity customTabActivity = mCustomTabActivityTestRule.getActivity();
         final Tab tab = customTabActivity.getActivityTab();
         PostTask.postTask(UiThreadTaskTraits.DEFAULT, () -> {
-            Assert.assertEquals(publisherUrl, tab.getTrustedCdnPublisherUrl());
+            Assert.assertEquals(publisherUrl, TrustedCdn.getPublisherUrl(tab));
             customTabActivity.openCurrentUrlInBrowser(true);
             Assert.assertNull(customTabActivity.getActivityTab());
         });
@@ -298,7 +299,7 @@
         CriteriaHelper.pollUiThread(() -> newActivity.getActivityTab() == tab, "Tab did not load");
 
         ThreadUtils.runOnUiThreadBlocking(
-                () -> { Assert.assertNull(tab.getTrustedCdnPublisherUrl()); });
+                () -> { Assert.assertNull(TrustedCdn.getPublisherUrl(tab)); });
 
         String testUrl = mWebServer.getResponseUrl("/test.html");
         String expectedUrl = UrlFormatter.formatUrlForDisplayOmitScheme(testUrl);
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/download/home/DownloadActivityV2Test.java b/chrome/android/javatests/src/org/chromium/chrome/browser/download/home/DownloadActivityV2Test.java
index 4a0bb77..5ab4ff8 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/download/home/DownloadActivityV2Test.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/download/home/DownloadActivityV2Test.java
@@ -19,7 +19,6 @@
 
 import org.chromium.base.ThreadUtils;
 import org.chromium.base.test.util.Restriction;
-import org.chromium.chrome.R;
 import org.chromium.chrome.browser.ChromeFeatureList;
 import org.chromium.chrome.browser.UrlConstants;
 import org.chromium.chrome.browser.download.home.list.UiUtils;
@@ -29,7 +28,6 @@
 import org.chromium.chrome.browser.profiles.Profile;
 import org.chromium.chrome.browser.snackbar.SnackbarManager;
 import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
-import org.chromium.chrome.test.ui.DummyUiActivity;
 import org.chromium.chrome.test.ui.DummyUiActivityTestCase;
 import org.chromium.components.feature_engagement.Tracker;
 import org.chromium.components.offline_items_collection.OfflineItem;
@@ -59,7 +57,6 @@
     @BeforeClass
     public static void setUpBeforeActivityLaunched() {
         UiUtils.setDisableUrlFormattingForTests(true);
-        DummyUiActivity.setTestTheme(R.style.Theme_Chromium_Activity_Fullscreen);
     }
 
     @Override
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/tasks/tab_list_ui/TabGridContainerViewBinderTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/tasks/tab_list_ui/TabGridContainerViewBinderTest.java
index e7a544dc..926d5172 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/tasks/tab_list_ui/TabGridContainerViewBinderTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/tasks/tab_list_ui/TabGridContainerViewBinderTest.java
@@ -74,7 +74,7 @@
 
     @BeforeClass
     public static void setUpBeforeActivityLaunched() {
-        DummyUiActivity.setTestTheme(R.style.Theme_Chromium_Activity_Fullscreen);
+        DummyUiActivity.setTestLayout(R.layout.tab_list_recycler_view_layout);
     }
 
     @Override
@@ -82,8 +82,6 @@
         super.setUpTest();
 
         ThreadUtils.runOnUiThreadBlocking(() -> {
-            getActivity().setContentView(R.layout.tab_list_recycler_view_layout);
-
             mRecyclerView = getActivity().findViewById(R.id.tab_list_view);
         });
 
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/tasks/tab_list_ui/TabListViewHolderTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/tasks/tab_list_ui/TabListViewHolderTest.java
index 26ba2e31..e4ee53ef 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/tasks/tab_list_ui/TabListViewHolderTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/tasks/tab_list_ui/TabListViewHolderTest.java
@@ -18,16 +18,13 @@
 import android.widget.LinearLayout;
 
 import org.junit.Assert;
-import org.junit.BeforeClass;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
 import org.chromium.base.Callback;
 import org.chromium.base.ThreadUtils;
-import org.chromium.chrome.R;
 import org.chromium.chrome.browser.tab.Tab;
 import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
-import org.chromium.chrome.test.ui.DummyUiActivity;
 import org.chromium.chrome.test.ui.DummyUiActivityTestCase;
 import org.chromium.ui.modelutil.PropertyModel;
 import org.chromium.ui.modelutil.PropertyModelChangeProcessor;
@@ -78,11 +75,6 @@
     private AtomicBoolean mSelectClicked = new AtomicBoolean();
     private boolean mShouldReturnBitmap;
 
-    @BeforeClass
-    public static void setUpBeforeActivityLaunched() {
-        DummyUiActivity.setTestTheme(R.style.Theme_Chromium_Activity_Fullscreen);
-    }
-
     @Override
     public void setUpTest() throws Exception {
         super.setUpTest();
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/widget/ThumbnailDiskStorageTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/widget/ThumbnailDiskStorageTest.java
index b7ff2bb..8689aa6 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/widget/ThumbnailDiskStorageTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/widget/ThumbnailDiskStorageTest.java
@@ -17,6 +17,7 @@
 import org.junit.runner.RunWith;
 
 import org.chromium.base.ThreadUtils;
+import org.chromium.base.metrics.RecordHistogram;
 import org.chromium.base.test.util.UrlUtils;
 import org.chromium.chrome.browser.util.ConversionUtils;
 import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
@@ -156,6 +157,7 @@
 
     @Before
     public void setUp() throws Exception {
+        RecordHistogram.setDisabledForTests(true);
         mTestThumbnailStorageDelegate = new TestThumbnailStorageDelegate();
         mTestThumbnailGenerator = new TestThumbnailGenerator();
         ThreadUtils.runOnUiThreadBlocking(new Runnable() {
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/widget/ThumbnailProviderImplTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/widget/ThumbnailProviderImplTest.java
index 7828b74..0918c43 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/widget/ThumbnailProviderImplTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/widget/ThumbnailProviderImplTest.java
@@ -45,7 +45,8 @@
         mActivityTestRule.startMainActivityOnBlankPage();
         ThreadUtils.runOnUiThread(() -> {
             mReferencePool = new DiscardableReferencePool();
-            mThumbnailProvider = new ThumbnailProviderImpl(mReferencePool);
+            mThumbnailProvider = new ThumbnailProviderImpl(
+                    mReferencePool, ThumbnailProviderImpl.ClientType.NTP_SUGGESTIONS);
         });
     }
 
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/autofill/AutofillUiUtilsTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/autofill/AutofillUiUtilsTest.java
new file mode 100644
index 0000000..c43e899f
--- /dev/null
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/autofill/AutofillUiUtilsTest.java
@@ -0,0 +1,264 @@
+// 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.autofill;
+
+import android.annotation.SuppressLint;
+import android.content.Context;
+import android.support.test.filters.SmallTest;
+import android.widget.EditText;
+
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.RuntimeEnvironment;
+import org.robolectric.annotation.Config;
+
+import org.chromium.base.test.BaseRobolectricTestRunner;
+import org.chromium.chrome.browser.autofill.AutofillUiUtils.ErrorType;
+
+import java.util.Calendar;
+
+/**
+ * Tests the AutofillUiUtils's java code.
+ */
+@RunWith(BaseRobolectricTestRunner.class)
+@Config(manifest = Config.NONE)
+public class AutofillUiUtilsTest {
+    private Context mContext;
+    private EditText mMonthInput;
+    private EditText mYearInput;
+    private int mThisMonth;
+    private int mTwoDigitThisYear;
+
+    @Before
+    public void setUp() {
+        mContext = RuntimeEnvironment.application;
+        mMonthInput = new EditText(mContext);
+        mYearInput = new EditText(mContext);
+        mThisMonth = Calendar.getInstance().get(Calendar.MONTH) + 1;
+        mTwoDigitThisYear = Calendar.getInstance().get(Calendar.YEAR) % 100;
+    }
+
+    @Test
+    @SmallTest
+    @SuppressLint("SetTextI18n")
+    public void testExpirationDateErrorWithInvalidMonthReturnsExpirationMonthErrorType() {
+        mMonthInput.setText("20");
+        mYearInput.setText(String.valueOf(mTwoDigitThisYear));
+
+        int errorType = getExpirationDateErrorForUserEnteredMonthAndYear();
+
+        Assert.assertEquals(ErrorType.EXPIRATION_MONTH, errorType);
+    }
+
+    @Test
+    @SmallTest
+    public void testExpirationDateErrorWithInvalidYearReturnsExpirationYearErrorType() {
+        mMonthInput.setText(String.valueOf(mThisMonth));
+        mYearInput.setText(String.valueOf(mTwoDigitThisYear - 1));
+
+        int errorType = getExpirationDateErrorForUserEnteredMonthAndYear();
+
+        Assert.assertEquals(ErrorType.EXPIRATION_YEAR, errorType);
+    }
+
+    @Test
+    @SmallTest
+    public void testExpirationDateErrorWithInvalidFutureYearReturnsExpirationYearErrorType() {
+        mMonthInput.setText(String.valueOf(mThisMonth));
+        mYearInput.setText(String.valueOf(mTwoDigitThisYear + 21));
+
+        int errorType = getExpirationDateErrorForUserEnteredMonthAndYear();
+
+        Assert.assertEquals(ErrorType.EXPIRATION_YEAR, errorType);
+    }
+
+    @Test
+    @SmallTest
+    public void testExpirationDateErrorWithCurrentYearAndCurrentMonthReturnsNoneErrorType() {
+        mMonthInput.setText(String.valueOf(mThisMonth));
+        mYearInput.setText(String.valueOf(mTwoDigitThisYear));
+
+        int errorType = getExpirationDateErrorForUserEnteredMonthAndYear();
+
+        Assert.assertEquals(ErrorType.NONE, errorType);
+    }
+
+    @Test
+    @SmallTest
+    @SuppressLint("SetTextI18n")
+    public void
+    testExpirationDateErrorWithEditingMonthAndNotFocusedYearReturnsNotEnoughInfoErrorType() {
+        mMonthInput.setText("1");
+        mYearInput.setText(String.valueOf(""));
+        mMonthInput.requestFocus(); // currently being edited
+
+        int errorType = AutofillUiUtils.getExpirationDateErrorType(mMonthInput,
+                mYearInput, /*didFocusOnMonth=*/
+                true, /*didFocusOnYear=*/false);
+
+        Assert.assertEquals(ErrorType.NOT_ENOUGH_INFO, errorType);
+    }
+
+    @Test
+    @SmallTest
+    @SuppressLint("SetTextI18n")
+    public void
+    testExpirationDateErrorWithEditingMonthAndFocusedInvalidYearReturnsExpirationYearErrorType() {
+        mMonthInput.setText("1");
+        mYearInput.setText(String.valueOf(""));
+        mMonthInput.requestFocus(); // currently being edited
+
+        int errorType = AutofillUiUtils.getExpirationDateErrorType(mMonthInput,
+                mYearInput, /*didFocusOnMonth=*/
+                true, /*didFocusOnYear=*/true);
+
+        Assert.assertEquals(ErrorType.EXPIRATION_YEAR, errorType);
+    }
+
+    @Test
+    @SmallTest
+    @SuppressLint("SetTextI18n")
+    public void
+    testExpirationDateErrorWithValidMonthAndIncompleteYearReturnsNotEnoughInfoErrorType() {
+        mMonthInput.setText(String.valueOf(mThisMonth));
+        mYearInput.setText("1");
+        mYearInput.requestFocus(); // currently being edited
+
+        int errorType = getExpirationDateErrorForUserEnteredMonthAndYear();
+
+        Assert.assertEquals(ErrorType.NOT_ENOUGH_INFO, errorType);
+    }
+
+    @Test
+    @SmallTest
+    @SuppressLint("SetTextI18n")
+    public void testExpirationDateErrorWithValidMonthAndValidYearReturnsNoneErrorType() {
+        mMonthInput.setText(String.valueOf(mThisMonth));
+        mYearInput.setText(String.valueOf(mTwoDigitThisYear + 1));
+
+        int errorType = getExpirationDateErrorForUserEnteredMonthAndYear();
+
+        Assert.assertEquals(ErrorType.NONE, errorType);
+    }
+
+    @Test
+    @SmallTest
+    @SuppressLint("SetTextI18n")
+    public void testGetMonthWithNonNumericInputReturnsNegativeOne() {
+        mMonthInput.setText("MM");
+
+        int month = AutofillUiUtils.getMonth(mMonthInput);
+
+        Assert.assertEquals(-1, month);
+    }
+
+    @Test
+    @SmallTest
+    @SuppressLint("SetTextI18n")
+    public void testGetMonthWithNegativeNumberInputReturnsNegativeOne() {
+        mMonthInput.setText("-20");
+
+        int month = AutofillUiUtils.getMonth(mMonthInput);
+
+        Assert.assertEquals(-1, month);
+    }
+
+    @Test
+    @SmallTest
+    @SuppressLint("SetTextI18n")
+    public void testGetMonthWithZeroAsInputReturnsNegativeOne() {
+        mMonthInput.setText("0");
+
+        int month = AutofillUiUtils.getMonth(mMonthInput);
+
+        Assert.assertEquals(-1, month);
+    }
+
+    @Test
+    @SmallTest
+    @SuppressLint("SetTextI18n")
+    public void testGetMonthWithThirteenAsInputReturnsNegativeOne() {
+        mMonthInput.setText("13");
+
+        int month = AutofillUiUtils.getMonth(mMonthInput);
+
+        Assert.assertEquals(-1, month);
+    }
+
+    @Test
+    @SmallTest
+    @SuppressLint("SetTextI18n")
+    public void testGetFourDigitYearWithNonNumericInputReturnsNegativeOne() {
+        mYearInput.setText("YY");
+
+        int year = AutofillUiUtils.getMonth(mYearInput);
+
+        Assert.assertEquals(-1, year);
+    }
+
+    @Test
+    @SmallTest
+    @SuppressLint("SetTextI18n")
+    public void testGetFourDigitYearWithNegativeNumberInputReturnsNegativeOne() {
+        mYearInput.setText("-20");
+
+        int fourDigitYear = AutofillUiUtils.getFourDigitYear(mYearInput);
+
+        Assert.assertEquals(-1, fourDigitYear);
+    }
+
+    @Test
+    @SmallTest
+    public void testGetFourDigitYearForCurrentTwoDigitYearReturnsCurrentFourDigitYear() {
+        // Set the edit text value to be the current year in YY format.
+        mYearInput.setText(String.valueOf(mTwoDigitThisYear));
+
+        int fourDigitYear = AutofillUiUtils.getFourDigitYear(mYearInput);
+
+        Assert.assertEquals(Calendar.getInstance().get(Calendar.YEAR), fourDigitYear);
+    }
+
+    @Test
+    @SmallTest
+    public void testGetFourDigitYearForPreviousYearReturnsNegativeOne() {
+        // Set the edit text value to be the current year in YY format.
+        mYearInput.setText(String.valueOf(mTwoDigitThisYear - 1));
+
+        int fourDigitYear = AutofillUiUtils.getFourDigitYear(mYearInput);
+
+        Assert.assertEquals(-1, fourDigitYear);
+    }
+
+    @Test
+    @SmallTest
+    public void testGetFourDigitYearForTenYearsFromNowReturnsValidFourDigitYear() {
+        // Set the edit text value to be the current year in YY format.
+        mYearInput.setText(String.valueOf(mTwoDigitThisYear + 10));
+
+        int fourDigitYear = AutofillUiUtils.getFourDigitYear(mYearInput);
+
+        Assert.assertEquals(Calendar.getInstance().get(Calendar.YEAR) + 10, fourDigitYear);
+    }
+
+    @Test
+    @SmallTest
+    public void testGetFourDigitYearForElevenYearsFromNowReturnsNegativeOne() {
+        // Set the edit text value to be the current year in YY format.
+        mYearInput.setText(String.valueOf(mTwoDigitThisYear + 11));
+
+        int fourDigitYear = AutofillUiUtils.getFourDigitYear(mYearInput);
+
+        Assert.assertEquals(-1, fourDigitYear);
+    }
+
+    @ErrorType
+    private int getExpirationDateErrorForUserEnteredMonthAndYear() {
+        return AutofillUiUtils.getExpirationDateErrorType(mMonthInput,
+                mYearInput, /*didFocusOnMonth=*/
+                true, /*didFocusOnYear=*/true);
+    }
+}
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/autofill/keyboard_accessory/ManualFillingControllerTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/autofill/keyboard_accessory/ManualFillingControllerTest.java
index d06b1db..b00f92a 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/autofill/keyboard_accessory/ManualFillingControllerTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/autofill/keyboard_accessory/ManualFillingControllerTest.java
@@ -628,7 +628,8 @@
     }
 
     private AccessorySheetData createPasswordData(String text) {
-        AccessorySheetData sheetData = new AccessorySheetData("Passwords");
+        AccessorySheetData sheetData =
+                new AccessorySheetData(FallbackSheetType.PASSWORD, "Passwords");
         UserInfo userInfo = new UserInfo(null);
         userInfo.addField(new UserInfo.Field("(No username)", "No username", false, null));
         userInfo.addField(new UserInfo.Field(text, "Password", true, null));
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/autofill/keyboard_accessory/PasswordAccessorySheetControllerTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/autofill/keyboard_accessory/PasswordAccessorySheetControllerTest.java
index 1b4f2bd..851b8b4 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/autofill/keyboard_accessory/PasswordAccessorySheetControllerTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/autofill/keyboard_accessory/PasswordAccessorySheetControllerTest.java
@@ -91,12 +91,14 @@
         mCoordinator.registerDataProvider(testProvider);
 
         // If the coordinator receives a set of initial items, the model should report an insertion.
-        testProvider.notifyObservers(new AccessorySheetData("Passwords"));
+        testProvider.notifyObservers(
+                new AccessorySheetData(FallbackSheetType.PASSWORD, "Passwords"));
         verify(mMockItemListObserver).onItemRangeInserted(mSheetDataPieces, 0, 1);
         assertThat(mSheetDataPieces.size(), is(1));
 
         // If the coordinator receives a new set of items, the model should report a change.
-        testProvider.notifyObservers(new AccessorySheetData("Other Passwords"));
+        testProvider.notifyObservers(
+                new AccessorySheetData(FallbackSheetType.PASSWORD, "Other Passwords"));
         verify(mMockItemListObserver).onItemRangeChanged(mSheetDataPieces, 0, 1, null);
         assertThat(mSheetDataPieces.size(), is(1));
 
@@ -114,7 +116,8 @@
     public void testSplitsTabDataToList() {
         setAutofillFeature(false);
         final PropertyProvider<AccessorySheetData> testProvider = new PropertyProvider<>();
-        final AccessorySheetData testData = new AccessorySheetData("Passwords for this site");
+        final AccessorySheetData testData =
+                new AccessorySheetData(FallbackSheetType.PASSWORD, "Passwords for this site");
         testData.getUserInfoList().add(new UserInfo(null));
         testData.getUserInfoList().get(0).addField(new UserInfo.Field("Name", "Name", false, null));
         testData.getUserInfoList().get(0).addField(
@@ -137,7 +140,8 @@
     public void testUsesTabTitleOnlyForEmptyListsForModernDesign() {
         setAutofillFeature(true);
         final PropertyProvider<AccessorySheetData> testProvider = new PropertyProvider<>();
-        final AccessorySheetData testData = new AccessorySheetData("No passwords for this");
+        final AccessorySheetData testData =
+                new AccessorySheetData(FallbackSheetType.PASSWORD, "No passwords for this");
         mCoordinator.registerDataProvider(testProvider);
 
         // Providing only FooterCommands and no User Info shows the title as empty state:
@@ -183,7 +187,8 @@
         assertThat(getSuggestionsImpressions(AccessoryTabType.ALL, 0), is(0));
 
         // If the tab is shown without interactive item, log "0" samples.
-        AccessorySheetData accessorySheetData = new AccessorySheetData("No passwords!");
+        AccessorySheetData accessorySheetData =
+                new AccessorySheetData(FallbackSheetType.PASSWORD, "No passwords!");
         accessorySheetData.getFooterCommands().add(new FooterCommand("Manage all passwords", null));
         accessorySheetData.getFooterCommands().add(new FooterCommand("Generate password", null));
         testProvider.notifyObservers(accessorySheetData);
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/offlinepages/prefetch/PrefetchBackgroundTaskUnitTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/offlinepages/prefetch/PrefetchBackgroundTaskUnitTest.java
index 0adb2b0f..e36e5ad 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/offlinepages/prefetch/PrefetchBackgroundTaskUnitTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/offlinepages/prefetch/PrefetchBackgroundTaskUnitTest.java
@@ -18,7 +18,6 @@
 import android.os.Bundle;
 
 import org.junit.Before;
-import org.junit.Ignore;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.ArgumentCaptor;
@@ -154,9 +153,7 @@
     /**
      * Tests that the background task is scheduled when limitless prefetching is enabled:
      * the waiting delay is shorter but the provided backoff time should be respected.
-     * TODO(https://crbug.com/803584): fix limitless mode or fully remove it.
      */
-    @Ignore
     @Test
     public void scheduleTaskLimitless() {
         final int additionalDelaySeconds = 20;
@@ -215,9 +212,7 @@
     /**
      * Tests that the background task is correctly started when conditions are sufficient for
      * limitless prefetching.
-     * TODO(https://crbug.com/803584): fix limitless mode or fully remove it.
      */
-    @Ignore
     @Test
     public void createNativeTaskLimitless() {
         final ArrayList<Boolean> reschedules = new ArrayList<>();
@@ -310,9 +305,7 @@
     /**
      * Tests that the background task is not started (rescheduled) when there's no connection and
      * limitless prefetching is enabled.
-     * TODO(https://crbug.com/803584): fix limitless mode or fully remove it.
      */
-    @Ignore
     @Test
     public void testNoNetworkLimitless() throws Exception {
         // Setup no network conditions.
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/tasks/tab_list_ui/GridTabSwitcherMediatorUnitTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/tasks/tab_list_ui/GridTabSwitcherMediatorUnitTest.java
index 4fac63d..fd40528 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/tasks/tab_list_ui/GridTabSwitcherMediatorUnitTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/tasks/tab_list_ui/GridTabSwitcherMediatorUnitTest.java
@@ -20,7 +20,6 @@
 
 import android.content.Context;
 import android.content.res.Resources;
-import android.graphics.Bitmap;
 import android.view.View;
 
 import org.junit.After;
@@ -356,7 +355,6 @@
         doReturn(id).when(tab).getId();
         doReturn("").when(tab).getUrl();
         doReturn(title).when(tab).getTitle();
-        doReturn(Bitmap.createBitmap(1, 1, Bitmap.Config.ARGB_8888)).when(tab).getFavicon();
         doReturn(false).when(tab).isClosing();
         return tab;
     }
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/tasks/tab_list_ui/TabListMediatorUnitTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/tasks/tab_list_ui/TabListMediatorUnitTest.java
index 7deda0cf..eabd91c 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/tasks/tab_list_ui/TabListMediatorUnitTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/tasks/tab_list_ui/TabListMediatorUnitTest.java
@@ -287,7 +287,6 @@
         doReturn(id).when(tab).getId();
         doReturn("").when(tab).getUrl();
         doReturn(title).when(tab).getTitle();
-        doReturn(Bitmap.createBitmap(1, 1, Bitmap.Config.ARGB_8888)).when(tab).getFavicon();
         return tab;
     }
 }
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/usage_stats/PageViewObserverTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/usage_stats/PageViewObserverTest.java
index e4a3468..d55df2d 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/usage_stats/PageViewObserverTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/usage_stats/PageViewObserverTest.java
@@ -4,6 +4,7 @@
 
 package org.chromium.chrome.browser.usage_stats;
 
+import static org.junit.Assert.assertTrue;
 import static org.mockito.ArgumentMatchers.argThat;
 import static org.mockito.Matchers.any;
 import static org.mockito.Mockito.doReturn;
@@ -57,6 +58,8 @@
     private EventTracker mEventTracker;
     @Mock
     private TokenTracker mTokenTracker;
+    @Mock
+    private SuspensionTracker mSuspensionTracker;
     @Captor
     private ArgumentCaptor<TabObserver> mTabObserverCaptor;
     @Captor
@@ -194,9 +197,36 @@
         verify(mEventTracker, times(0)).addWebsiteEvent(argThat(isStopEvent(DIFFERENT_FQDN)));
     }
 
+    @Test
+    public void navigationToSuspendedDomain_suspendedTabShown() {
+        PageViewObserver observer = createPageViewObserver();
+        onUpdateUrl(mTab, STARTING_URL);
+
+        doReturn(true).when(mSuspensionTracker).isWebsiteSuspended(DIFFERENT_FQDN);
+        onUpdateUrl(mTab, DIFFERENT_URL);
+
+        verify(mTab, times(2)).addObserver(mTabObserverCaptor.capture());
+        assertTrue(mTabObserverCaptor.getValue() instanceof SuspendedTab);
+    }
+
+    @Test
+    public void navigationToUnsuspendedDomain_suspendedTabRemoved() {
+        PageViewObserver observer = createPageViewObserver();
+        onUpdateUrl(mTab, STARTING_URL);
+
+        doReturn(true).when(mSuspensionTracker).isWebsiteSuspended(DIFFERENT_FQDN);
+        onUpdateUrl(mTab, DIFFERENT_URL);
+
+        verify(mTab, times(2)).addObserver(mTabObserverCaptor.capture());
+        SuspendedTab suspendedTab = (SuspendedTab) mTabObserverCaptor.getValue();
+
+        suspendedTab.onPageLoadStarted(mTab, STARTING_URL);
+        verify(mTab, times(1)).removeObserver(suspendedTab);
+    }
+
     private PageViewObserver createPageViewObserver() {
-        PageViewObserver observer =
-                new PageViewObserver(mActivity, mTabModelSelector, mEventTracker, mTokenTracker);
+        PageViewObserver observer = new PageViewObserver(
+                mActivity, mTabModelSelector, mEventTracker, mTokenTracker, mSuspensionTracker);
         verify(mTabModel, times(1)).addObserver(mTabModelObserverCaptor.capture());
         if (mTabModelSelector.getCurrentTab() != null) {
             verify(mTabModelSelector.getCurrentTab(), times(1))
diff --git a/chrome/android/profiles/newest.txt b/chrome/android/profiles/newest.txt
index de3eb573..ad7eb14 100644
--- a/chrome/android/profiles/newest.txt
+++ b/chrome/android/profiles/newest.txt
@@ -1 +1 @@
-chromeos-chrome-amd64-74.0.3722.0_rc-r1-merged.afdo.bz2
\ No newline at end of file
+chromeos-chrome-amd64-74.0.3725.0_rc-r1-merged.afdo.bz2
\ No newline at end of file
diff --git a/chrome/app/bookmarks_strings.grdp b/chrome/app/bookmarks_strings.grdp
index 2d46be6..09b0e50 100644
--- a/chrome/app/bookmarks_strings.grdp
+++ b/chrome/app/bookmarks_strings.grdp
@@ -58,12 +58,6 @@
     <message name="IDS_BOOKMARK_BAR_OPEN_ALL" desc="Menu title for opening all urls in a bookmark folder">
       &amp;Open all bookmarks
     </message>
-    <message name="IDS_BOOKMARK_BAR_OPEN_ALL_NEW_WINDOW" desc="Menu title for opening all urls in a bookmark folder in a new window">
-      Open all bookmarks in &amp;new window
-    </message>
-    <message name="IDS_BOOKMARK_BAR_OPEN_ALL_INCOGNITO" desc="Menu description for opening all urls in a bookmark folder in an incognito window">
-        Open all bookmarks in &amp;incognito window
-    </message>
     <message name="IDS_BOOKMARK_BAR_OPEN_ALL_COUNT" desc="Menu title for opening all urls in a bookmark folder">
         {COUNT, plural,
           =0 {&amp;Open all}
@@ -116,12 +110,6 @@
     <message name="IDS_BOOKMARK_BAR_OPEN_ALL" desc="In Title Case: Menu title for opening all urls in a bookmark folder">
         &amp;Open All Bookmarks
     </message>
-    <message name="IDS_BOOKMARK_BAR_OPEN_ALL_NEW_WINDOW" desc="In Title Case: Menu title for opening all urls in a bookmark folder in a new window">
-        Open All Bookmarks in &amp;New Window
-    </message>
-    <message name="IDS_BOOKMARK_BAR_OPEN_ALL_INCOGNITO" desc="In Title Case: Menu description for opening all urls in a bookmark folder in an incognito window">
-        Open All Bookmarks in &amp;Incognito Window
-    </message>
     <message name="IDS_BOOKMARK_BAR_OPEN_ALL_COUNT" desc="In Title Case: Menu title for opening all urls in a bookmark folder">
         {COUNT, plural,
           =0 {&amp;Open All}
@@ -314,65 +302,23 @@
     <message name="IDS_BOOKMARK_MANAGER" desc="The label of the menu item that shows the bookmark manager">
       &amp;Bookmark manager
     </message>
-    <message name="IDS_BOOKMARK_MANAGER_FOLDERS_MENU" desc="Title of the folders menu in the bookmark manager.">
-      Folders
-    </message>
     <message name="IDS_BOOKMARK_MANAGER_ORGANIZE_MENU" desc="Title of the organize menu in the bookmark manager.">
       Organize
     </message>
-    <message name="IDS_BOOKMARK_MANAGER_SHOW_IN_FOLDER" desc="The label of the menu item in the bookmark manager context menu that changes the selection in the tree to match the folder of the selected item in the table.">
-      Show in folder
-    </message>
-    <message name="IDS_BOOKMARK_MANAGER_SORT" desc="The label of the menu item in the bookmark manager organize menu that alphabetizes the bookmarks/folders by title.">
-      Reorder by title
-    </message>
-    <message name="IDS_BOOKMARK_MANAGER_IMPORT_MENU" desc="Title of the import menu item in the bookmark manager.">
-      Import bookmarks from HTML file...
-    </message>
-    <message name="IDS_BOOKMARK_MANAGER_EXPORT_MENU" desc="Title of the export menu item in the bookmark manager.">
-      Export bookmarks to HTML file...
-    </message>
     <message name="IDS_BOOKMARK_MANAGER_INVALID_URL" desc="Error message to display when the user tries to edit or create a bookmark with an invalid URL.">
       Invalid URL
     </message>
-    <message name="IDS_BOOKMARK_MANAGER_RECENT" desc="The label for the tree item that shows the recently added bookmarks.">
-      Recent
-    </message>
-    <message name="IDS_BOOKMARK_MANAGER_SEARCH" desc="The label for the tree item that shows the search results for the bookmarks.">
-      Search
-    </message>
   </if>
   <if expr="use_titlecase">
     <message name="IDS_BOOKMARK_MANAGER" desc="In Title Case: The label of the menu item that shows the bookmark manager">
       &amp;Bookmark Manager
     </message>
-    <message name="IDS_BOOKMARK_MANAGER_FOLDERS_MENU" desc="In Title Case: Title of the folders menu in the bookmark manager.">
-      Folders
-    </message>
     <message name="IDS_BOOKMARK_MANAGER_ORGANIZE_MENU" desc="In Title Case: Title of the organize menu in the bookmark manager.">
       Organize
     </message>
-    <message name="IDS_BOOKMARK_MANAGER_SHOW_IN_FOLDER" desc="In Title Case: The label of the menu item in the bookmark manager context menu that changes the selection in the tree to match the folder of the selected item in the table.">
-      Show in Folder
-    </message>
-    <message name="IDS_BOOKMARK_MANAGER_SORT" desc="In Title Case: The label of the menu item in the bookmark manager organize menu that alphabetizes the bookmarks/folders by title.">
-      Reorder by Title
-    </message>
-    <message name="IDS_BOOKMARK_MANAGER_IMPORT_MENU" desc="In Title Case: Title of the import menu item in the bookmark manager.">
-      Import Bookmarks from HTML File...
-    </message>
-    <message name="IDS_BOOKMARK_MANAGER_EXPORT_MENU" desc="In Title Case: Title of the export menu item in the bookmark manager.">
-      Export Bookmarks to HTML File...
-    </message>
     <message name="IDS_BOOKMARK_MANAGER_INVALID_URL" desc="In Title Case: Error message to display when the user tries to edit or create a bookmark with an invalid URL.">
       Invalid URL.
     </message>
-    <message name="IDS_BOOKMARK_MANAGER_RECENT" desc="In Title Case: The label for the tree item that shows the recently added bookmarks.">
-      Recent
-    </message>
-    <message name="IDS_BOOKMARK_MANAGER_SEARCH" desc="In Title Case: The label for the tree item that shows the search results for the bookmarks.">
-      Search
-    </message>
   </if>
   <message name="IDS_EXPORT_BOOKMARKS_DEFAULT_FILENAME" desc="Filename that pre-populates the filename field when user attempts to export their bookmarks from Bookmark Manager.">
     bookmarks_<ph name="DATESTAMP">$1<ex>02_11_11</ex></ph>.html
diff --git a/chrome/app/generated_resources.grd b/chrome/app/generated_resources.grd
index fd6b56c..760a498 100644
--- a/chrome/app/generated_resources.grd
+++ b/chrome/app/generated_resources.grd
@@ -330,9 +330,6 @@
       </message>
 
       <!-- Generic terms -->
-      <message name="IDS_UNDO_DELETE" desc="Description for a command to undo a delete command.">
-        Undo Delete
-      </message>
       <message name="IDS_EDIT" desc="Edit menu item">
         &amp;Edit
       </message>
@@ -538,9 +535,6 @@
           <message name="IDS_CONTENT_CONTEXT_PASTE_AND_MATCH_STYLE" desc="The name of the 'Paste as plain text' command in the content area context menu">
             Paste as plain text
           </message>
-          <message name="IDS_CONTENT_CONTEXT_DELETE" desc="The name of the Delete command in the content area context menu">
-            &amp;Delete
-          </message>
           <message name="IDS_CONTENT_CONTEXT_ADD_TO_DICTIONARY" desc="The name of the Add to dictionary command in the content area context menu">
             &amp;Add to dictionary
           </message>
@@ -777,9 +771,6 @@
               Paste As Plain Text
             </message>
           </if>
-          <message name="IDS_CONTENT_CONTEXT_DELETE" desc="In Title Case: The name of the Delete command in the content area context menu">
-            &amp;Delete
-          </message>
           <message name="IDS_CONTENT_CONTEXT_ADD_TO_DICTIONARY" desc="In Title Case: The name of the Add to dictionary command in the content area context menu">
             &amp;Add to Dictionary
           </message>
@@ -1986,6 +1977,17 @@
           Add <ph name="APP_NAME">$1<ex>Google Maps</ex></ph> to Home screen
         </message>
       </if>
+
+      <!-- Desktop omnibox PWA install icon -->
+      <if expr="not is_android">
+        <message name="IDS_OMNIBOX_PWA_INSTALL_ICON_LABEL" desc="String for the omnibox icon label promoting an app installation">
+          Install
+        </message>
+        <message name="IDS_OMNIBOX_PWA_INSTALL_ICON_TOOLTIP" desc="Tooltip for the omnibox icon label promoting an app installation">
+          Install <ph name="APP_NAME">$1<ex>Google Maps</ex></ph>
+        </message>
+      </if>
+
       <!-- Bookmark app bubble -->
       <if expr="use_titlecase">
         <message name="IDS_ADD_TO_OS_LAUNCH_SURFACE_BUBBLE_TITLE" desc="In Title Case: Title of the bubble for adding a bookmark app shortcut to the operating system.">
diff --git a/chrome/app/generated_resources_grd/IDS_OMNIBOX_PWA_INSTALL_ICON_LABEL.png.sha1 b/chrome/app/generated_resources_grd/IDS_OMNIBOX_PWA_INSTALL_ICON_LABEL.png.sha1
new file mode 100644
index 0000000..ee8b9d10
--- /dev/null
+++ b/chrome/app/generated_resources_grd/IDS_OMNIBOX_PWA_INSTALL_ICON_LABEL.png.sha1
@@ -0,0 +1 @@
+5e68f5c5515d452dee093e581dc9925f528458be
\ No newline at end of file
diff --git a/chrome/app/generated_resources_grd/IDS_OMNIBOX_PWA_INSTALL_ICON_TOOLTIP.png.sha1 b/chrome/app/generated_resources_grd/IDS_OMNIBOX_PWA_INSTALL_ICON_TOOLTIP.png.sha1
new file mode 100644
index 0000000..a3ea0ba
--- /dev/null
+++ b/chrome/app/generated_resources_grd/IDS_OMNIBOX_PWA_INSTALL_ICON_TOOLTIP.png.sha1
@@ -0,0 +1 @@
+45973c65415db61492e9869afea809ad1513b315
\ No newline at end of file
diff --git a/chrome/browser/BUILD.gn b/chrome/browser/BUILD.gn
index 3f1a887..715cce3 100644
--- a/chrome/browser/BUILD.gn
+++ b/chrome/browser/BUILD.gn
@@ -1009,8 +1009,6 @@
     "page_load_metrics/observers/omnibox_suggestion_used_page_load_metrics_observer.h",
     "page_load_metrics/observers/page_capping_page_load_metrics_observer.cc",
     "page_load_metrics/observers/page_capping_page_load_metrics_observer.h",
-    "page_load_metrics/observers/prerender_page_load_metrics_observer.cc",
-    "page_load_metrics/observers/prerender_page_load_metrics_observer.h",
     "page_load_metrics/observers/previews_lite_page_redirect_metrics_observer.cc",
     "page_load_metrics/observers/previews_lite_page_redirect_metrics_observer.h",
     "page_load_metrics/observers/previews_page_load_metrics_observer.cc",
@@ -1855,6 +1853,7 @@
     "//components/component_updater",
     "//components/component_updater:crl_set_remover",
     "//components/consent_auditor",
+    "//components/content_capture/browser",
     "//components/content_settings/core/browser",
     "//components/content_settings/core/common",
     "//components/contextual_search/content:browser",
@@ -2265,6 +2264,8 @@
       "android/download/download_location_dialog_bridge.h",
       "android/download/download_location_dialog_bridge_impl.cc",
       "android/download/download_location_dialog_bridge_impl.h",
+      "android/download/download_manager_bridge.cc",
+      "android/download/download_manager_bridge.h",
       "android/download/download_manager_service.cc",
       "android/download/download_manager_service.h",
       "android/download/download_media_parser.cc",
@@ -2505,6 +2506,8 @@
       "android/subresource_filter/test_subresource_filter_publisher.cc",
       "android/tab_android.cc",
       "android/tab_android.h",
+      "android/tab_favicon.cc",
+      "android/tab_favicon.h",
       "android/tab_printer.cc",
       "android/tab_printer.h",
       "android/tab_state.cc",
@@ -2700,6 +2703,7 @@
       "//chrome/services/media_gallery_util/public/cpp",
       "//components/autofill_assistant/browser",
       "//components/cdm/browser",
+      "//components/content_capture/android",
       "//components/crash/android:crash_android",
       "//components/embedder_support/android:web_contents_delegate",
       "//components/feed:buildflags",
@@ -3139,6 +3143,8 @@
       "search/search_suggest/search_suggest_service_factory.cc",
       "search/search_suggest/search_suggest_service_factory.h",
       "search/search_suggest/search_suggest_service_observer.h",
+      "send_tab_to_self/desktop_notification_handler.cc",
+      "send_tab_to_self/desktop_notification_handler.h",
       "send_tab_to_self/send_tab_to_self_util.cc",
       "send_tab_to_self/send_tab_to_self_util.h",
       "serial/chrome_serial_delegate.cc",
@@ -3445,6 +3451,8 @@
     sources += [
       "badging/badge_manager_delegate_win.cc",
       "badging/badge_manager_delegate_win.h",
+      "browser_switcher/browser_switcher_policy_migrator.cc",
+      "browser_switcher/browser_switcher_policy_migrator.h",
       "browser_switcher/browser_switcher_service_win.cc",
       "browser_switcher/browser_switcher_service_win.h",
       "downgrade/user_data_downgrade.cc",
diff --git a/chrome/browser/about_flags.cc b/chrome/browser/about_flags.cc
index 394df8a..eb4a547 100644
--- a/chrome/browser/about_flags.cc
+++ b/chrome/browser/about_flags.cc
@@ -80,8 +80,6 @@
 #include "components/previews/core/previews_switches.h"
 #include "components/printing/browser/features.h"
 #include "components/safe_browsing/features.h"
-#include "components/search_provider_logos/features.h"
-#include "components/search_provider_logos/switches.h"
 #include "components/security_state/core/features.h"
 #include "components/security_state/core/security_state.h"
 #include "components/services/heap_profiling/public/cpp/switches.h"
@@ -866,22 +864,6 @@
      cc::switches::kDisableCheckerImaging, ""},
 };
 
-#if defined(OS_ANDROID)
-const FeatureEntry::Choice kUseDdljsonApiChoices[] = {
-    {flags_ui::kGenericExperimentChoiceDefault, "", ""},
-    {"(force test doodle 0)", search_provider_logos::switches::kGoogleDoodleUrl,
-     "https://www.gstatic.com/chrome/ntp/doodle_test/ddljson_android0.json"},
-    {"(force test doodle 1)", search_provider_logos::switches::kGoogleDoodleUrl,
-     "https://www.gstatic.com/chrome/ntp/doodle_test/ddljson_android1.json"},
-    {"(force test doodle 2)", search_provider_logos::switches::kGoogleDoodleUrl,
-     "https://www.gstatic.com/chrome/ntp/doodle_test/ddljson_android2.json"},
-    {"(force test doodle 3)", search_provider_logos::switches::kGoogleDoodleUrl,
-     "https://www.gstatic.com/chrome/ntp/doodle_test/ddljson_android3.json"},
-    {"(force test doodle 4)", search_provider_logos::switches::kGoogleDoodleUrl,
-     "https://www.gstatic.com/chrome/ntp/doodle_test/ddljson_android4.json"},
-};
-#endif  // defined(OS_ANDROID)
-
 const FeatureEntry::FeatureParam kMarkHttpAsDangerous[] = {
     {security_state::features::kMarkHttpAsFeatureParameterName,
      security_state::features::kMarkHttpAsParameterDangerous}};
@@ -2969,10 +2951,6 @@
      FEATURE_VALUE_TYPE(network::features::kOutOfBlinkCors)},
 
 #if defined(OS_ANDROID)
-    {"use-ddljson-api", flag_descriptions::kUseDdljsonApiName,
-     flag_descriptions::kUseDdljsonApiDescription, kOsAll,
-     MULTI_VALUE_TYPE(kUseDdljsonApiChoices)},
-
     {"spannable-inline-autocomplete",
      flag_descriptions::kSpannableInlineAutocompleteName,
      flag_descriptions::kSpannableInlineAutocompleteDescription, kOsAndroid,
@@ -3204,7 +3182,7 @@
      FEATURE_WITH_PARAMS_VALUE_TYPE(
          security_state::features::kMarkHttpAsFeature,
          kMarkHttpAsFeatureVariations,
-         "HTTPBadPhase3")},
+         "HTTPReallyBadFinal")},
 
 #if !defined(OS_ANDROID)
     {"enable-web-authentication-testing-api",
@@ -3371,9 +3349,9 @@
                                     "TabSwitcherOnReturn")},
 #endif
 
-    {"enable-layered-api", flag_descriptions::kLayeredAPIName,
-     flag_descriptions::kLayeredAPIDescription, kOsAll,
-     FEATURE_VALUE_TYPE(features::kLayeredAPI)},
+    {"enable-built-in-module-all", flag_descriptions::kBuiltInModuleAllName,
+     flag_descriptions::kBuiltInModuleAllDescription, kOsAll,
+     FEATURE_VALUE_TYPE(features::kBuiltInModuleAll)},
 
     {"enable-blink-gen-property-trees",
      flag_descriptions::kEnableBlinkGenPropertyTreesName,
@@ -4084,6 +4062,19 @@
      FEATURE_VALUE_TYPE(chrome::android::kCCTTargetTranslateLanguage)},
 #endif
 
+    {"enable-built-in-module-infra", flag_descriptions::kBuiltInModuleInfraName,
+     flag_descriptions::kBuiltInModuleInfraDescription, kOsAll,
+     FEATURE_VALUE_TYPE(features::kBuiltInModuleInfra)},
+
+    {"enable-built-in-module-kv-storage",
+     flag_descriptions::kBuiltInModuleKvStorageName,
+     flag_descriptions::kBuiltInModuleKvStorageDescription, kOsAll,
+     FEATURE_VALUE_TYPE(features::kBuiltInModuleKvStorage)},
+
+    {"native-filesystem-api", flag_descriptions::kNativeFilesystemAPIName,
+     flag_descriptions::kNativeFilesystemAPIDescription, kOsAll,
+     FEATURE_VALUE_TYPE(blink::features::kNativeFilesystemAPI)},
+
     // NOTE: Adding a new flag requires adding a corresponding entry to enum
     // "LoginCustomFlags" in tools/metrics/histograms/enums.xml. See "Flag
     // Histograms" in tools/metrics/histograms/README.md (run the
diff --git a/chrome/browser/accessibility/accessibility_labels_service.cc b/chrome/browser/accessibility/accessibility_labels_service.cc
index 50918ff..5c75bc3 100644
--- a/chrome/browser/accessibility/accessibility_labels_service.cc
+++ b/chrome/browser/accessibility/accessibility_labels_service.cc
@@ -15,6 +15,7 @@
 #include "chrome/common/pref_names.h"
 #include "components/pref_registry/pref_registry_syncable.h"
 #include "components/prefs/pref_service.h"
+#include "components/sync_preferences/pref_service_syncable.h"
 #include "content/public/browser/browser_accessibility_state.h"
 #include "content/public/common/content_features.h"
 #include "ui/accessibility/ax_action_data.h"
@@ -32,6 +33,15 @@
       user_prefs::PrefRegistrySyncable::SYNCABLE_PREF);
 }
 
+// static
+void AccessibilityLabelsService::InitOffTheRecordPrefs(Profile* profile) {
+  DCHECK(profile->IsOffTheRecord());
+  profile->GetPrefs()->SetBoolean(prefs::kAccessibilityImageLabelsEnabled,
+                                  false);
+  profile->GetPrefs()->SetBoolean(prefs::kAccessibilityImageLabelsOptInAccepted,
+                                  false);
+}
+
 void AccessibilityLabelsService::Init() {
   // Hidden behind a feature flag.
   if (!base::FeatureList::IsEnabled(features::kExperimentalAccessibilityLabels))
diff --git a/chrome/browser/accessibility/accessibility_labels_service.h b/chrome/browser/accessibility/accessibility_labels_service.h
index 8068449d..949e948 100644
--- a/chrome/browser/accessibility/accessibility_labels_service.h
+++ b/chrome/browser/accessibility/accessibility_labels_service.h
@@ -26,6 +26,9 @@
 
   static void RegisterProfilePrefs(user_prefs::PrefRegistrySyncable* registry);
 
+  // Off the record profiles will default to having the feature disabled.
+  static void InitOffTheRecordPrefs(Profile* profile);
+
   void Init();
 
   ui::AXMode GetAXMode();
diff --git a/chrome/browser/android/autofill_assistant/client_android.cc b/chrome/browser/android/autofill_assistant/client_android.cc
index c22602fc..06f15c8 100644
--- a/chrome/browser/android/autofill_assistant/client_android.cc
+++ b/chrome/browser/android/autofill_assistant/client_android.cc
@@ -16,6 +16,7 @@
 #include "base/metrics/field_trial_params.h"
 #include "base/no_destructor.h"
 #include "base/task/post_task.h"
+#include "base/time/default_tick_clock.h"
 #include "chrome/browser/android/chrome_feature_list.h"
 #include "chrome/browser/autofill/android/personal_data_manager_android.h"
 #include "chrome/browser/autofill/personal_data_manager_factory.h"
@@ -287,7 +288,8 @@
   if (controller_) {
     return;
   }
-  controller_ = std::make_unique<Controller>(web_contents_, /* client= */ this);
+  controller_ = std::make_unique<Controller>(
+      web_contents_, /* client= */ this, base::DefaultTickClock::GetInstance());
 }
 
 void ClientAndroid::DestroyController() {
diff --git a/chrome/browser/android/chrome_feature_list.cc b/chrome/browser/android/chrome_feature_list.cc
index 2d396532..3b450d7 100644
--- a/chrome/browser/android/chrome_feature_list.cc
+++ b/chrome/browser/android/chrome_feature_list.cc
@@ -64,6 +64,7 @@
     &contextual_suggestions::kContextualSuggestionsIPHReverseScroll,
     &contextual_suggestions::kContextualSuggestionsOptOut,
     &download::features::kDownloadAutoResumptionNative,
+    &download::features::kUseDownloadOfflineContentProvider,
     &features::kAllowStartingServiceManagerOnly,
     &features::kAppNotificationStatusMessaging,
     &features::kClearOldBrowsingData,
diff --git a/chrome/browser/android/download/available_offline_content_provider_unittest.cc b/chrome/browser/android/download/available_offline_content_provider_unittest.cc
index 1f5e92d..116de2c 100644
--- a/chrome/browser/android/download/available_offline_content_provider_unittest.cc
+++ b/chrome/browser/android/download/available_offline_content_provider_unittest.cc
@@ -36,11 +36,6 @@
 using testing::_;
 const char kProviderNamespace[] = "offline_pages";
 
-std::unique_ptr<KeyedService> BuildOfflineContentAggregator(
-    content::BrowserContext* context) {
-  return std::make_unique<OfflineContentAggregator>();
-}
-
 OfflineItem UninterestingImageItem() {
   OfflineItem item;
   item.original_url = GURL("https://uninteresting");
@@ -129,11 +124,8 @@
  protected:
   void SetUp() override {
     scoped_feature_list_->InitAndEnableFeature(features::kNewNetErrorPageUI);
-    // To control the items in the aggregator, we create it and register a
-    // single MockOfflineContentProvider.
-    aggregator_ = static_cast<OfflineContentAggregator*>(
-        OfflineContentAggregatorFactory::GetInstance()->SetTestingFactoryAndUse(
-            &profile_, base::BindRepeating(&BuildOfflineContentAggregator)));
+    aggregator_ =
+        OfflineContentAggregatorFactory::GetForBrowserContext(nullptr);
     aggregator_->RegisterProvider(kProviderNamespace, &content_provider_);
     content_provider_.SetVisuals({});
   }
diff --git a/chrome/browser/android/download/download_manager_bridge.cc b/chrome/browser/android/download/download_manager_bridge.cc
new file mode 100644
index 0000000..e4f494a
--- /dev/null
+++ b/chrome/browser/android/download/download_manager_bridge.cc
@@ -0,0 +1,81 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/android/download/download_manager_bridge.h"
+
+#include <memory>
+
+#include "base/android/jni_android.h"
+#include "base/android/jni_string.h"
+#include "base/android/scoped_java_ref.h"
+#include "base/feature_list.h"
+#include "components/download/public/common/download_features.h"
+#include "content/public/browser/browser_thread.h"
+#include "jni/DownloadManagerBridge_jni.h"
+#include "url/gurl.h"
+
+using base::android::ConvertUTF8ToJavaString;
+using base::android::JavaParamRef;
+using base::android::ScopedJavaLocalRef;
+using content::BrowserThread;
+using download::DownloadItem;
+
+static void JNI_DownloadManagerBridge_OnAddCompletedDownloadDone(
+    JNIEnv* env,
+    jlong callback_id,
+    jlong download_id) {
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
+  DCHECK(callback_id);
+
+  // Convert java long long int to c++ pointer, take ownership.
+  std::unique_ptr<AddCompletedDownloadCallback> cb(
+      reinterpret_cast<AddCompletedDownloadCallback*>(callback_id));
+  std::move(*cb).Run(download_id);
+}
+
+void DownloadManagerBridge::AddCompletedDownload(
+    download::DownloadItem* download,
+    AddCompletedDownloadCallback callback) {
+  if (!base::FeatureList::IsEnabled(
+          download::features::kUseDownloadOfflineContentProvider)) {
+    return;
+  }
+
+  JNIEnv* env = base::android::AttachCurrentThread();
+  ScopedJavaLocalRef<jstring> jfile_name =
+      ConvertUTF8ToJavaString(env, download->GetFileNameToReportUser().value());
+  ScopedJavaLocalRef<jstring> jmime_type =
+      ConvertUTF8ToJavaString(env, download->GetMimeType());
+  ScopedJavaLocalRef<jstring> jfile_path =
+      ConvertUTF8ToJavaString(env, download->GetTargetFilePath().value());
+  int64_t file_size = download->GetReceivedBytes();
+  ScopedJavaLocalRef<jstring> joriginal_url =
+      ConvertUTF8ToJavaString(env, download->GetOriginalUrl().spec());
+  ScopedJavaLocalRef<jstring> jreferer = base::android::ConvertUTF8ToJavaString(
+      env, download->GetReferrerUrl().spec());
+  ScopedJavaLocalRef<jstring> jdownload_guid =
+      base::android::ConvertUTF8ToJavaString(env, download->GetGuid());
+
+  // Make copy on the heap so we can pass the pointer through JNI.
+  intptr_t callback_id = reinterpret_cast<intptr_t>(
+      new AddCompletedDownloadCallback(std::move(callback)));
+
+  Java_DownloadManagerBridge_addCompletedDownload(
+      env, jfile_name, jfile_name, jmime_type, jfile_path, file_size,
+      joriginal_url, jreferer, jdownload_guid, callback_id);
+}
+
+void DownloadManagerBridge::RemoveCompletedDownload(
+    download::DownloadItem* download) {
+  if (!base::FeatureList::IsEnabled(
+          download::features::kUseDownloadOfflineContentProvider)) {
+    return;
+  }
+
+  JNIEnv* env = base::android::AttachCurrentThread();
+  ScopedJavaLocalRef<jstring> jdownload_guid =
+      base::android::ConvertUTF8ToJavaString(env, download->GetGuid());
+  Java_DownloadManagerBridge_removeCompletedDownload(
+      env, jdownload_guid, download->GetFileExternallyRemoved());
+}
diff --git a/chrome/browser/android/download/download_manager_bridge.h b/chrome/browser/android/download/download_manager_bridge.h
new file mode 100644
index 0000000..5c87f8c
--- /dev/null
+++ b/chrome/browser/android/download/download_manager_bridge.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 CHROME_BROWSER_ANDROID_DOWNLOAD_DOWNLOAD_MANAGER_BRIDGE_H_
+#define CHROME_BROWSER_ANDROID_DOWNLOAD_DOWNLOAD_MANAGER_BRIDGE_H_
+
+#include "base/callback.h"
+#include "components/download/public/common/download_item.h"
+
+using DownloadItem = download::DownloadItem;
+using AddCompletedDownloadCallback = base::OnceCallback<void(int64_t)>;
+
+// This class pairs with DownloadManagerBridge on Java side, that handles all
+// the android DownloadManager related functionalities. Both classes have only
+// static functions.
+class DownloadManagerBridge {
+ public:
+  static void AddCompletedDownload(DownloadItem* download,
+                                   AddCompletedDownloadCallback callback);
+  static void RemoveCompletedDownload(DownloadItem* download);
+
+  DISALLOW_COPY_AND_ASSIGN(DownloadManagerBridge);
+};
+
+#endif  // CHROME_BROWSER_ANDROID_DOWNLOAD_DOWNLOAD_MANAGER_BRIDGE_H_
diff --git a/chrome/browser/android/download/download_manager_service.cc b/chrome/browser/android/download/download_manager_service.cc
index 82e49e9..bc3f674 100644
--- a/chrome/browser/android/download/download_manager_service.cc
+++ b/chrome/browser/android/download/download_manager_service.cc
@@ -646,6 +646,18 @@
     resume_callback_for_testing_.Run(false);
 }
 
+void DownloadManagerService::GetAllDownloads(
+    content::DownloadManager::DownloadVector* all_items,
+    bool is_off_the_record) {
+  if (in_progress_manager_) {
+    in_progress_manager_->GetAllDownloads(all_items);
+  } else {
+    content::DownloadManager* manager = GetDownloadManager(is_off_the_record);
+    if (manager)
+      manager->GetAllDownloads(all_items);
+  }
+}
+
 download::DownloadItem* DownloadManagerService::GetDownload(
     const std::string& download_guid,
     bool is_off_the_record) {
diff --git a/chrome/browser/android/download/download_manager_service.h b/chrome/browser/android/download/download_manager_service.h
index 9cc4386..edb7adb 100644
--- a/chrome/browser/android/download/download_manager_service.h
+++ b/chrome/browser/android/download/download_manager_service.h
@@ -165,6 +165,14 @@
   download::InProgressDownloadManager* RetriveInProgressDownloadManager(
       content::BrowserContext* context);
 
+  // Get all downloads from DownloadManager or InProgressManager.
+  void GetAllDownloads(content::DownloadManager::DownloadVector* all_items,
+                       bool is_off_the_record);
+
+  // Gets a download item from DownloadManager or InProgressManager.
+  download::DownloadItem* GetDownload(const std::string& download_guid,
+                                      bool is_off_the_record);
+
  protected:
   // Called to get the content::DownloadManager instance.
   virtual content::DownloadManager* GetDownloadManager(bool is_off_the_record);
@@ -204,10 +212,6 @@
 
   void OnResumptionFailedInternal(const std::string& download_guid);
 
-  // Gets a download item from DownloadManager or InProgressManager.
-  download::DownloadItem* GetDownload(const std::string& download_guid,
-                                      bool is_off_the_record);
-
   // Creates the InProgressDownloadmanager when running with ServiceManager
   // only mode.
   void CreateInProgressDownloadManager();
diff --git a/chrome/browser/android/password_manager/manual_filling_view_android.cc b/chrome/browser/android/password_manager/manual_filling_view_android.cc
index 016df38..f0906c5d 100644
--- a/chrome/browser/android/password_manager/manual_filling_view_android.cc
+++ b/chrome/browser/android/password_manager/manual_filling_view_android.cc
@@ -142,7 +142,8 @@
     const AccessorySheetData& tab_data) {
   ScopedJavaLocalRef<jobject> j_tab_data =
       Java_ManualFillingBridge_createAccessorySheetData(
-          env, ConvertUTF16ToJavaString(env, tab_data.title()));
+          env, static_cast<int>(tab_data.get_sheet_type()),
+          ConvertUTF16ToJavaString(env, tab_data.title()));
 
   for (const UserInfo& user_info : tab_data.user_info_list()) {
     ScopedJavaLocalRef<jobject> j_user_info =
diff --git a/chrome/browser/android/tab_android.cc b/chrome/browser/android/tab_android.cc
index a4d277e8..a82a0fa 100644
--- a/chrome/browser/android/tab_android.cc
+++ b/chrome/browser/android/tab_android.cc
@@ -18,13 +18,11 @@
 #include "chrome/browser/android/metrics/uma_utils.h"
 #include "chrome/browser/android/tab_printer.h"
 #include "chrome/browser/android/tab_web_contents_delegate_android.h"
-#include "chrome/browser/android/trusted_cdn.h"
 #include "chrome/browser/bookmarks/bookmark_model_factory.h"
 #include "chrome/browser/bookmarks/managed_bookmark_service_factory.h"
 #include "chrome/browser/browser_about_handler.h"
 #include "chrome/browser/history/history_service_factory.h"
 #include "chrome/browser/infobars/infobar_service.h"
-#include "chrome/browser/offline_pages/offline_page_utils.h"
 #include "chrome/browser/prerender/prerender_contents.h"
 #include "chrome/browser/prerender/prerender_manager.h"
 #include "chrome/browser/prerender/prerender_manager_factory.h"
@@ -50,7 +48,6 @@
 #include "components/bookmarks/browser/bookmark_utils.h"
 #include "components/bookmarks/managed/managed_bookmark_service.h"
 #include "components/dom_distiller/core/url_utils.h"
-#include "components/favicon/content/content_favicon_driver.h"
 #include "components/navigation_interception/intercept_navigation_delegate.h"
 #include "components/navigation_interception/navigation_params.h"
 #include "components/sessions/content/content_live_tab.h"
@@ -71,17 +68,11 @@
 #include "jni/Tab_jni.h"
 #include "net/base/escape.h"
 #include "services/service_manager/public/cpp/interface_provider.h"
-#include "skia/ext/image_operations.h"
 #include "third_party/blink/public/common/associated_interfaces/associated_interface_provider.h"
 #include "ui/android/view_android.h"
 #include "ui/android/window_android.h"
 #include "ui/base/resource/resource_bundle.h"
 #include "ui/base/window_open_disposition.h"
-#include "ui/display/display.h"
-#include "ui/display/screen.h"
-#include "ui/gfx/android/java_bitmap.h"
-#include "ui/gfx/favicon_size.h"
-#include "ui/gfx/image/image_skia.h"
 
 using base::android::AttachCurrentThread;
 using base::android::ConvertUTF8ToJavaString;
@@ -125,35 +116,6 @@
 
 WEB_CONTENTS_USER_DATA_KEY_IMPL(TabAndroidHelper)
 
-GURL GetPublisherURLForTrustedCDN(
-    content::NavigationHandle* navigation_handle) {
-  if (!trusted_cdn::IsTrustedCDN(navigation_handle->GetURL()))
-    return GURL();
-
-  // Offline pages don't have headers when they are loaded.
-  // TODO(bauerb): Consider storing the publisher URL on the offline page item.
-  if (offline_pages::OfflinePageUtils::GetOfflinePageFromWebContents(
-          navigation_handle->GetWebContents())) {
-    return GURL();
-  }
-
-  const net::HttpResponseHeaders* headers =
-      navigation_handle->GetResponseHeaders();
-  if (!headers) {
-    // TODO(https://crbug.com/829323): In some cases other than offline pages
-    // we don't have headers.
-    LOG(WARNING) << "No headers for navigation to "
-                 << navigation_handle->GetURL();
-    return GURL();
-  }
-
-  std::string publisher_url;
-  if (!headers->GetNormalizedHeader("x-amp-cache", &publisher_url))
-    return GURL();
-
-  return GURL(publisher_url);
-}
-
 }  // namespace
 
 TabAndroid* TabAndroid::FromWebContents(
@@ -310,25 +272,6 @@
   return false;
 }
 
-void TabAndroid::OnFaviconUpdated(favicon::FaviconDriver* favicon_driver,
-                                  NotificationIconType notification_icon_type,
-                                  const GURL& icon_url,
-                                  bool icon_url_changed,
-                                  const gfx::Image& image) {
-  if (notification_icon_type != NON_TOUCH_LARGEST &&
-      notification_icon_type != TOUCH_LARGEST) {
-    return;
-  }
-
-  SkBitmap favicon = image.AsImageSkia().GetRepresentation(1.0f).GetBitmap();
-  if (favicon.empty())
-    return;
-
-  JNIEnv* env = base::android::AttachCurrentThread();
-  Java_Tab_onFaviconAvailable(env, weak_java_tab_.get(env),
-                              gfx::ConvertToJavaBitmap(&favicon));
-}
-
 bool TabAndroid::IsCurrentlyACustomTab() {
   JNIEnv* env = base::android::AttachCurrentThread();
   return Java_Tab_isCurrentlyACustomTab(env, weak_java_tab_.get(env));
@@ -352,7 +295,6 @@
 
   TabAndroidHelper::SetTabForWebContents(web_contents(), this);
   AttachTabHelpers(web_contents_.get());
-  WebContentsObserver::Observe(web_contents_.get());
 
   SetWindowSessionID(session_window_id_);
 
@@ -366,12 +308,6 @@
   web_contents_delegate_->LoadProgressChanged(web_contents(), 0);
   web_contents()->SetDelegate(web_contents_delegate_.get());
 
-  favicon::FaviconDriver* favicon_driver =
-      favicon::ContentFaviconDriver::FromWebContents(web_contents_.get());
-
-  if (favicon_driver)
-    favicon_driver->AddObserver(this);
-
   synced_tab_delegate_->SetWebContents(web_contents(), jparent_tab_id);
 
   // Verify that the WebContents this tab represents matches the expected
@@ -409,14 +345,6 @@
   if (web_contents()->GetNativeView())
     web_contents()->GetNativeView()->GetLayer()->RemoveFromParent();
 
-  WebContentsObserver::Observe(nullptr);
-
-  favicon::FaviconDriver* favicon_driver =
-      favicon::ContentFaviconDriver::FromWebContents(web_contents_.get());
-
-  if (favicon_driver)
-    favicon_driver->RemoveObserver(this);
-
   web_contents()->SetDelegate(nullptr);
 
   if (delete_native) {
@@ -583,36 +511,6 @@
     entry->SetTitle(title);
 }
 
-ScopedJavaLocalRef<jobject> TabAndroid::GetFavicon(
-    JNIEnv* env,
-    const JavaParamRef<jobject>& obj) {
-  ScopedJavaLocalRef<jobject> bitmap;
-  favicon::FaviconDriver* favicon_driver =
-      favicon::ContentFaviconDriver::FromWebContents(web_contents_.get());
-
-  if (!favicon_driver)
-    return bitmap;
-
-  // Always return the default favicon in Android.
-  SkBitmap favicon = favicon_driver->GetFavicon().AsBitmap();
-  if (!favicon.empty()) {
-    const float device_scale_factor =
-        display::Screen::GetScreen()->GetPrimaryDisplay().device_scale_factor();
-    int target_size_dip = device_scale_factor * gfx::kFaviconSize;
-    if (favicon.width() != target_size_dip ||
-        favicon.height() != target_size_dip) {
-      favicon =
-          skia::ImageOperations::Resize(favicon,
-                                        skia::ImageOperations::RESIZE_BEST,
-                                        target_size_dip,
-                                        target_size_dip);
-    }
-
-    bitmap = gfx::ConvertToJavaBitmap(&favicon);
-  }
-  return bitmap;
-}
-
 prerender::PrerenderManager* TabAndroid::GetPrerenderManager() const {
   Profile* profile = GetProfile();
   if (!profile)
@@ -826,26 +724,6 @@
     tab_content_manager_->NativeRemoveTabThumbnail(GetAndroidId());
 }
 
-void TabAndroid::DidFinishNavigation(
-    content::NavigationHandle* navigation_handle) {
-  // Skip subframe, same-document, or non-committed navigations (downloads or
-  // 204/205 responses).
-  if (!navigation_handle->IsInMainFrame() ||
-      navigation_handle->IsSameDocument() ||
-      !navigation_handle->HasCommitted()) {
-    return;
-  }
-
-  GURL publisher_url = GetPublisherURLForTrustedCDN(navigation_handle);
-  JNIEnv* env = base::android::AttachCurrentThread();
-  base::android::ScopedJavaLocalRef<jstring> j_publisher_url;
-  if (publisher_url.is_valid())
-    j_publisher_url = ConvertUTF8ToJavaString(env, publisher_url.spec());
-
-  Java_Tab_setTrustedCdnPublisherUrl(env, weak_java_tab_.get(env),
-                                     j_publisher_url);
-}
-
 bool TabAndroid::AreRendererInputEventsIgnored(
     JNIEnv* env,
     const base::android::JavaParamRef<jobject>& obj) {
diff --git a/chrome/browser/android/tab_android.h b/chrome/browser/android/tab_android.h
index 4f25b07..0b4b450 100644
--- a/chrome/browser/android/tab_android.h
+++ b/chrome/browser/android/tab_android.h
@@ -17,11 +17,9 @@
 #include "base/strings/string16.h"
 #include "chrome/browser/android/tab_state.h"
 #include "chrome/browser/sync/glue/synced_tab_delegate_android.h"
-#include "components/favicon/core/favicon_driver_observer.h"
 #include "components/infobars/core/infobar_manager.h"
 #include "components/omnibox/browser/location_bar_model.h"
 #include "components/sessions/core/session_id.h"
-#include "content/public/browser/web_contents_observer.h"
 
 class GURL;
 class Profile;
@@ -39,7 +37,6 @@
 
 namespace content {
 class DevToolsAgentHost;
-class NavigationHandle;
 class WebContents;
 }
 
@@ -47,8 +44,7 @@
 class PrerenderManager;
 }
 
-class TabAndroid : public favicon::FaviconDriverObserver,
-                   public content::WebContentsObserver {
+class TabAndroid {
  public:
   // A Java counterpart will be generated for this enum.
   // GENERATED_JAVA_ENUM_PACKAGE: org.chromium.chrome.browser
@@ -72,7 +68,7 @@
   static void AttachTabHelpers(content::WebContents* web_contents);
 
   TabAndroid(JNIEnv* env, const base::android::JavaRef<jobject>& obj);
-  ~TabAndroid() override;
+  ~TabAndroid();
 
   base::android::ScopedJavaLocalRef<jobject> GetJavaObject();
 
@@ -115,13 +111,6 @@
 
   bool HasPrerenderedUrl(GURL gurl);
 
-  // Overridden from favicon::FaviconDriverObserver:
-  void OnFaviconUpdated(favicon::FaviconDriver* favicon_driver,
-                        NotificationIconType notification_icon_type,
-                        const GURL& icon_url,
-                        bool icon_url_changed,
-                        const gfx::Image& image) override;
-
   // Returns true if this tab is currently presented in the context of custom
   // tabs. Tabs can be moved between different activities so the returned value
   // might change over the lifetime of the tab.
@@ -251,9 +240,6 @@
   void AttachDetachedTab(JNIEnv* env,
                          const base::android::JavaParamRef<jobject>& obj);
 
-  void DidFinishNavigation(
-      content::NavigationHandle* navigation_handle) override;
-
   bool AreRendererInputEventsIgnored(
       JNIEnv* env,
       const base::android::JavaParamRef<jobject>& obj);
diff --git a/chrome/browser/android/tab_favicon.cc b/chrome/browser/android/tab_favicon.cc
new file mode 100644
index 0000000..f800f58
--- /dev/null
+++ b/chrome/browser/android/tab_favicon.cc
@@ -0,0 +1,94 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/android/tab_favicon.h"
+
+#include "components/favicon/content/content_favicon_driver.h"
+#include "content/public/browser/web_contents.h"
+#include "jni/TabFavicon_jni.h"
+#include "skia/ext/image_operations.h"
+#include "ui/display/display.h"
+#include "ui/display/screen.h"
+#include "ui/gfx/android/java_bitmap.h"
+#include "ui/gfx/favicon_size.h"
+#include "ui/gfx/image/image_skia.h"
+
+using base::android::JavaParamRef;
+using base::android::ScopedJavaLocalRef;
+
+TabFavicon::TabFavicon(JNIEnv* env, const JavaParamRef<jobject>& obj)
+    : jobj_(env, obj) {}
+
+TabFavicon::~TabFavicon() = default;
+
+void TabFavicon::SetWebContents(JNIEnv* env,
+                                const JavaParamRef<jobject>& obj,
+                                const JavaParamRef<jobject>& jweb_contents) {
+  favicon_driver_ = favicon::ContentFaviconDriver::FromWebContents(
+      content::WebContents::FromJavaWebContents(jweb_contents));
+  if (favicon_driver_)
+    favicon_driver_->AddObserver(this);
+}
+
+void TabFavicon::ResetWebContents(JNIEnv* env,
+                                  const JavaParamRef<jobject>& obj) {
+  if (favicon_driver_) {
+    favicon_driver_->RemoveObserver(this);
+    favicon_driver_ = nullptr;
+  }
+}
+
+void TabFavicon::OnDestroyed(JNIEnv* env, const JavaParamRef<jobject>& obj) {
+  delete this;
+}
+
+ScopedJavaLocalRef<jobject> TabFavicon::GetFavicon(
+    JNIEnv* env,
+    const JavaParamRef<jobject>& obj) {
+  ScopedJavaLocalRef<jobject> bitmap;
+
+  if (!favicon_driver_)
+    return bitmap;
+
+  // Always return the default favicon in Android.
+  SkBitmap favicon = favicon_driver_->GetFavicon().AsBitmap();
+  if (!favicon.empty()) {
+    const float device_scale_factor =
+        display::Screen::GetScreen()->GetPrimaryDisplay().device_scale_factor();
+    int target_size_dip = device_scale_factor * gfx::kFaviconSize;
+    if (favicon.width() != target_size_dip ||
+        favicon.height() != target_size_dip) {
+      favicon = skia::ImageOperations::Resize(
+          favicon, skia::ImageOperations::RESIZE_BEST, target_size_dip,
+          target_size_dip);
+    }
+
+    bitmap = gfx::ConvertToJavaBitmap(&favicon);
+  }
+  return bitmap;
+}
+
+void TabFavicon::OnFaviconUpdated(favicon::FaviconDriver* favicon_driver,
+                                  NotificationIconType notification_icon_type,
+                                  const GURL& icon_url,
+                                  bool icon_url_changed,
+                                  const gfx::Image& image) {
+  if (notification_icon_type != NON_TOUCH_LARGEST &&
+      notification_icon_type != TOUCH_LARGEST) {
+    return;
+  }
+
+  SkBitmap favicon = image.AsImageSkia().GetRepresentation(1.0f).GetBitmap();
+  if (favicon.empty())
+    return;
+
+  JNIEnv* env = base::android::AttachCurrentThread();
+  Java_TabFavicon_onFaviconAvailable(env, jobj_,
+                                     gfx::ConvertToJavaBitmap(&favicon));
+}
+
+static jlong JNI_TabFavicon_Init(JNIEnv* env,
+                                 const JavaParamRef<jobject>& obj) {
+  return reinterpret_cast<intptr_t>(new TabFavicon(env, obj));
+}
diff --git a/chrome/browser/android/tab_favicon.h b/chrome/browser/android/tab_favicon.h
new file mode 100644
index 0000000..1aeea8e
--- /dev/null
+++ b/chrome/browser/android/tab_favicon.h
@@ -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.
+
+#ifndef CHROME_BROWSER_ANDROID_TAB_FAVICON_H_
+#define CHROME_BROWSER_ANDROID_TAB_FAVICON_H_
+
+#include "base/android/scoped_java_ref.h"
+#include "components/favicon/core/favicon_driver_observer.h"
+
+namespace favicon {
+class FaviconDriver;
+}
+
+// Native Favicon provider for Tab. Managed by Java layer.
+class TabFavicon : public favicon::FaviconDriverObserver {
+ public:
+  TabFavicon(JNIEnv* env, const base::android::JavaParamRef<jobject>& obj);
+  ~TabFavicon() override;
+
+  void SetWebContents(
+      JNIEnv* env,
+      const base::android::JavaParamRef<jobject>& obj,
+      const base::android::JavaParamRef<jobject>& jweb_contents);
+  void ResetWebContents(JNIEnv* env,
+                        const base::android::JavaParamRef<jobject>& obj);
+  void OnDestroyed(JNIEnv* env,
+                   const base::android::JavaParamRef<jobject>& obj);
+  base::android::ScopedJavaLocalRef<jobject> GetFavicon(
+      JNIEnv* env,
+      const base::android::JavaParamRef<jobject>& obj);
+
+  // favicon::FaviconDriverObserver
+  void OnFaviconUpdated(favicon::FaviconDriver* favicon_driver,
+                        NotificationIconType notification_icon_type,
+                        const GURL& icon_url,
+                        bool icon_url_changed,
+                        const gfx::Image& image) override;
+
+ private:
+  base::android::ScopedJavaGlobalRef<jobject> jobj_;
+  favicon::FaviconDriver* favicon_driver_;
+};
+
+#endif  // CHROME_BROWSER_ANDROID_TAB_FAVICON_H_
diff --git a/chrome/browser/android/trusted_cdn.cc b/chrome/browser/android/trusted_cdn.cc
index d8c1076b..546ffc6 100644
--- a/chrome/browser/android/trusted_cdn.cc
+++ b/chrome/browser/android/trusted_cdn.cc
@@ -4,17 +4,33 @@
 
 #include "chrome/browser/android/trusted_cdn.h"
 
+#include <string>
+
+#include "base/android/jni_android.h"
+#include "base/android/jni_string.h"
 #include "base/command_line.h"
 #include "base/feature_list.h"
 #include "base/no_destructor.h"
+#include "chrome/browser/android/tab_android.h"
+#include "chrome/browser/offline_pages/offline_page_utils.h"
 #include "chrome/common/chrome_features.h"
 #include "chrome/common/chrome_switches.h"
+#include "content/public/browser/navigation_handle.h"
+#include "content/public/browser/web_contents.h"
+#include "jni/TrustedCdn_jni.h"
 #include "url/gurl.h"
 
-namespace trusted_cdn {
+using base::android::ConvertUTF8ToJavaString;
+using base::android::JavaParamRef;
+using base::android::ScopedJavaLocalRef;
+using content::WebContents;
 
 constexpr char kDefaultTrustedCDNBaseURL[] = "https://cdn.ampproject.org";
 
+namespace {
+// Returns whether the given URL is hosted by a trusted CDN. This can be turned
+// off via a Feature, and the base URL to trust can be set via a command line
+// flag for testing.
 bool IsTrustedCDN(const GURL& url) {
   if (!base::FeatureList::IsEnabled(features::kShowTrustedPublisherURL))
     return false;
@@ -43,4 +59,76 @@
          (url.EffectiveIntPort() == trusted_cdn_base_url->EffectiveIntPort());
 }
 
-}  // namespace trusted_cdn
+GURL GetPublisherURL(content::NavigationHandle* navigation_handle) {
+  if (!IsTrustedCDN(navigation_handle->GetURL()))
+    return GURL();
+
+  // Offline pages don't have headers when they are loaded.
+  // TODO(bauerb): Consider storing the publisher URL on the offline page item.
+  if (offline_pages::OfflinePageUtils::GetOfflinePageFromWebContents(
+          navigation_handle->GetWebContents())) {
+    return GURL();
+  }
+
+  const net::HttpResponseHeaders* headers =
+      navigation_handle->GetResponseHeaders();
+  if (!headers) {
+    // TODO(https://crbug.com/829323): In some cases other than offline pages
+    // we don't have headers.
+    LOG(WARNING) << "No headers for navigation to "
+                 << navigation_handle->GetURL();
+    return GURL();
+  }
+
+  std::string publisher_url;
+  if (!headers->GetNormalizedHeader("x-amp-cache", &publisher_url))
+    return GURL();
+
+  return GURL(publisher_url);
+}
+
+}  // namespace
+
+TrustedCdn::TrustedCdn(JNIEnv* env, const JavaParamRef<jobject>& obj)
+    : jobj_(env, obj) {}
+
+TrustedCdn::~TrustedCdn() = default;
+
+void TrustedCdn::SetWebContents(JNIEnv* env,
+                                const JavaParamRef<jobject>& obj,
+                                const JavaParamRef<jobject>& jweb_contents) {
+  WebContentsObserver::Observe(WebContents::FromJavaWebContents(jweb_contents));
+}
+
+void TrustedCdn::ResetWebContents(JNIEnv* env,
+                                  const JavaParamRef<jobject>& obj) {
+  WebContentsObserver::Observe(nullptr);
+}
+
+void TrustedCdn::OnDestroyed(JNIEnv* env, const JavaParamRef<jobject>& obj) {
+  delete this;
+}
+
+void TrustedCdn::DidFinishNavigation(
+    content::NavigationHandle* navigation_handle) {
+  // Skip subframe, same-document, or non-committed navigations (downloads or
+  // 204/205 responses).
+  if (!navigation_handle->IsInMainFrame() ||
+      navigation_handle->IsSameDocument() ||
+      !navigation_handle->HasCommitted()) {
+    return;
+  }
+
+  GURL publisher_url = GetPublisherURL(navigation_handle);
+  JNIEnv* env = base::android::AttachCurrentThread();
+  base::android::ScopedJavaLocalRef<jstring> j_publisher_url;
+  if (publisher_url.is_valid())
+    j_publisher_url = ConvertUTF8ToJavaString(env, publisher_url.spec());
+
+  Java_TrustedCdn_setPublisherUrl(env, jobj_, j_publisher_url);
+}
+
+static jlong JNI_TrustedCdn_Init(JNIEnv* env,
+                                 const JavaParamRef<jobject>& obj) {
+  return reinterpret_cast<intptr_t>(new TrustedCdn(env, obj));
+}
diff --git a/chrome/browser/android/trusted_cdn.h b/chrome/browser/android/trusted_cdn.h
index b29c61a..51635520 100644
--- a/chrome/browser/android/trusted_cdn.h
+++ b/chrome/browser/android/trusted_cdn.h
@@ -5,15 +5,34 @@
 #ifndef CHROME_BROWSER_ANDROID_TRUSTED_CDN_H_
 #define CHROME_BROWSER_ANDROID_TRUSTED_CDN_H_
 
-class GURL;
+#include "base/android/scoped_java_ref.h"
+#include "content/public/browser/web_contents_observer.h"
 
-namespace trusted_cdn {
+namespace content {
+class NavigationHandle;
+}
 
-// Returns whether the given URL is hosted by a trusted CDN. This can be turned
-// off via a Feature, and the base URL to trust can be set via a command line
-// flag for testing.
-bool IsTrustedCDN(const GURL& url);
+// Native part of Trusted CDN publisher URL provider. Managed by Java layer.
+class TrustedCdn : public content::WebContentsObserver {
+ public:
+  TrustedCdn(JNIEnv* env, const base::android::JavaParamRef<jobject>& obj);
+  ~TrustedCdn() override;
 
-}  // namespace trusted_cdn
+  void SetWebContents(
+      JNIEnv* env,
+      const base::android::JavaParamRef<jobject>& obj,
+      const base::android::JavaParamRef<jobject>& jweb_contents);
+  void ResetWebContents(JNIEnv* env,
+                        const base::android::JavaParamRef<jobject>& obj);
+  void OnDestroyed(JNIEnv* env,
+                   const base::android::JavaParamRef<jobject>& obj);
+
+  // content::WebContentsObserver
+  void DidFinishNavigation(
+      content::NavigationHandle* navigation_handle) override;
+
+ private:
+  base::android::ScopedJavaGlobalRef<jobject> jobj_;
+};
 
 #endif  // CHROME_BROWSER_ANDROID_TRUSTED_CDN_H_
diff --git a/chrome/browser/autofill/manual_filling_controller_impl_unittest.cc b/chrome/browser/autofill/manual_filling_controller_impl_unittest.cc
index b6a75f6..e2069de7 100644
--- a/chrome/browser/autofill/manual_filling_controller_impl_unittest.cc
+++ b/chrome/browser/autofill/manual_filling_controller_impl_unittest.cc
@@ -82,6 +82,7 @@
 autofill::AccessorySheetData dummy_accessory_sheet_data() {
   constexpr char kExampleAccessorySheetDataTitle[] = "Example title";
   return autofill::AccessorySheetData(
+      autofill::FallbackSheetType::CREDIT_CARD,
       base::ASCIIToUTF16(kExampleAccessorySheetDataTitle));
 }
 
diff --git a/chrome/browser/banners/app_banner_manager.cc b/chrome/browser/banners/app_banner_manager.cc
index 9f36217..272b7b5 100644
--- a/chrome/browser/banners/app_banner_manager.cc
+++ b/chrome/browser/banners/app_banner_manager.cc
@@ -245,6 +245,7 @@
       load_finished_(false),
       triggered_by_devtools_(false),
       status_reporter_(std::make_unique<NullStatusReporter>()),
+      install_animation_pending_(false),
       installable_(Installable::UNKNOWN),
       weak_factory_(this) {
   DCHECK(manager_);
@@ -465,6 +466,8 @@
     return;
 
   installable_ = installable;
+  install_animation_pending_ = IsInstallable();
+
   for (Observer& observer : observer_list_)
     observer.OnInstallabilityUpdated();
 }
@@ -635,16 +638,21 @@
 base::string16 AppBannerManager::GetInstallableAppName(
     content::WebContents* web_contents) {
   AppBannerManager* manager = FromWebContents(web_contents);
-  if (!manager || manager->installable_ != Installable::INSTALLABLE_YES)
+  if (!manager || !manager->IsInstallable())
     return base::string16();
   return manager->GetAppName();
 }
 
-// static
-bool AppBannerManager::IsWebContentsInstallable(
-    content::WebContents* web_contents) {
-  AppBannerManager* manager = FromWebContents(web_contents);
-  return manager && manager->installable_ == Installable::INSTALLABLE_YES;
+bool AppBannerManager::IsInstallable() const {
+  return installable_ == Installable::INSTALLABLE_YES;
+}
+
+bool AppBannerManager::MaybeConsumeInstallAnimation() {
+  DCHECK(IsInstallable());
+  if (!install_animation_pending_)
+    return false;
+  install_animation_pending_ = false;
+  return true;
 }
 
 void AppBannerManager::RecordCouldShowBanner() {
diff --git a/chrome/browser/banners/app_banner_manager.h b/chrome/browser/banners/app_banner_manager.h
index ba1c4a96..6933ca6 100644
--- a/chrome/browser/banners/app_banner_manager.h
+++ b/chrome/browser/banners/app_banner_manager.h
@@ -144,9 +144,13 @@
   static base::string16 GetInstallableAppName(
       content::WebContents* web_contents);
 
-  // Returns whether the |web_contents| has passed installability checks (e.g.
-  // having a service worker fetch event).
-  static bool IsWebContentsInstallable(content::WebContents* web_contents);
+  // Returns whether installability checks have passed (e.g. having a service
+  // worker fetch event).
+  bool IsInstallable() const;
+
+  // Each successful installability check gets to show one animation prompt,
+  // this returns and consumes the animation prompt if it is available.
+  bool MaybeConsumeInstallAnimation();
 
   // Requests an app banner. If |is_debug_mode| is true, any failure in the
   // pipeline will be reported to the devtools console.
@@ -388,6 +392,7 @@
   bool triggered_by_devtools_;
 
   std::unique_ptr<StatusReporter> status_reporter_;
+  bool install_animation_pending_;
   Installable installable_;
 
   base::ObserverList<Observer, true> observer_list_;
diff --git a/chrome/browser/browser_resources.grd b/chrome/browser/browser_resources.grd
index 8e16351..c4b65f1 100644
--- a/chrome/browser/browser_resources.grd
+++ b/chrome/browser/browser_resources.grd
@@ -223,7 +223,6 @@
 
       <!-- App Management. -->
       <if expr="not is_android">
-        <include name="IDR_APP_MANAGEMENT_BIG_BUFFER_MOJO_LITE_JS" file="${root_gen_dir}\mojo\public\mojom\base\big_buffer.mojom-lite.js" use_base_dir="false" type="BINDATA" />
         <include name="IDR_APP_MANAGEMENT_BITMAP_MOJO_LITE_JS" file="${root_gen_dir}\skia\public\interfaces\bitmap.mojom-lite.js" use_base_dir="false" type="BINDATA" />
         <include name="IDR_APP_MANAGEMENT_IMAGE_INFO_MOJO_LITE_JS" file="${root_gen_dir}\skia\public\interfaces\image_info.mojom-lite.js" use_base_dir="false" type="BINDATA" />
         <include name="IDR_APP_MANAGEMENT_IMAGE_MOJO_LITE_JS" file="${root_gen_dir}\ui\gfx\image\mojo\image.mojom-lite.js" use_base_dir="false" type="BINDATA" />
diff --git a/chrome/browser/browser_switcher/browser_switcher_policy_migrator.cc b/chrome/browser/browser_switcher/browser_switcher_policy_migrator.cc
new file mode 100644
index 0000000..6bfdaf0
--- /dev/null
+++ b/chrome/browser/browser_switcher/browser_switcher_policy_migrator.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/browser_switcher/browser_switcher_policy_migrator.h"
+
+#include <string>
+#include <utility>
+
+#include "base/bind.h"
+#include "components/policy/core/common/policy_bundle.h"
+#include "components/policy/core/common/policy_map.h"
+#include "components/policy/core/common/policy_namespace.h"
+#include "components/policy/policy_constants.h"
+#include "extensions/common/hashed_extension_id.h"
+
+namespace browser_switcher {
+
+namespace {
+
+const char kLBSExtensionId[] = "heildphpnddilhkemkielfhnkaagiabh";
+
+void SecondsToMilliseconds(base::Value* val) {
+  const int ms_per_second = 1000;
+  *val = base::Value(val->GetInt() * ms_per_second);
+}
+
+// Transforms the string policy to a list policy (containing 1 string).
+//
+// The LBS extension's command-line parameter policies are single strings,
+// because on Windows the command-line parameters are passed as a single string
+// to the program. The parameters are parsed by the program, not the shell.
+//
+// On other platforms though, parameter parsing is done by the shell, not the
+// program. So the new policies are string-lists that are given to the program
+// pre-parsed. This is why we need to convert the string to a list, when
+// migrating from the old policy.
+void StringToList(base::Value* val) {
+  std::string str = val->GetString();
+  *val = base::Value(base::Value::Type::LIST);
+  val->GetList().push_back(base::Value(std::move(str)));
+}
+
+}  // namespace
+
+BrowserSwitcherPolicyMigrator::BrowserSwitcherPolicyMigrator() = default;
+BrowserSwitcherPolicyMigrator::~BrowserSwitcherPolicyMigrator() = default;
+
+void BrowserSwitcherPolicyMigrator::Migrate(policy::PolicyBundle* bundle) {
+  policy::PolicyMap& extension_map = bundle->Get(policy::PolicyNamespace(
+      policy::POLICY_DOMAIN_EXTENSIONS, kLBSExtensionId));
+  policy::PolicyMap& chrome_map =
+      bundle->Get(policy::PolicyNamespace(policy::POLICY_DOMAIN_CHROME, ""));
+  if (extension_map.empty())
+    return;
+
+  const auto* entry = chrome_map.Get("BrowserSwitcherEnabled");
+  if (!entry || !entry->value || !entry->value->GetBool())
+    return;
+  extension_map.Set("browser_switcher_enabled", entry->DeepCopy());
+
+  using Migration = policy::ExtensionPolicyMigrator::Migration;
+  const Migration migrations[] = {
+      Migration("alternative_browser_path",
+                policy::key::kAlternativeBrowserPath),
+      Migration("chrome_path", policy::key::kBrowserSwitcherChromePath),
+      Migration("url_list", policy::key::kBrowserSwitcherUrlList),
+      Migration("url_greylist", policy::key::kBrowserSwitcherUrlGreylist),
+      Migration("keep_last_chrome_tab",
+                policy::key::kBrowserSwitcherKeepLastChromeTab),
+      Migration("use_ie_site_list", policy::key::kBrowserSwitcherUseIeSitelist),
+      Migration("show_transition_screen", policy::key::kBrowserSwitcherDelay,
+                base::BindRepeating(&SecondsToMilliseconds)),
+      Migration("chrome_arguments",
+                policy::key::kBrowserSwitcherChromeParameters,
+                base::BindRepeating(&StringToList)),
+      Migration("alternative_browser_arguments",
+                policy::key::kAlternativeBrowserParameters,
+                base::BindRepeating(&StringToList)),
+  };
+
+  CopyPoliciesIfUnset(bundle,
+                      extensions::HashedExtensionId(kLBSExtensionId).value(),
+                      migrations);
+}
+
+}  // namespace browser_switcher
diff --git a/chrome/browser/browser_switcher/browser_switcher_policy_migrator.h b/chrome/browser/browser_switcher/browser_switcher_policy_migrator.h
new file mode 100644
index 0000000..b2b91f2
--- /dev/null
+++ b/chrome/browser/browser_switcher/browser_switcher_policy_migrator.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 CHROME_BROWSER_BROWSER_SWITCHER_BROWSER_SWITCHER_POLICY_MIGRATOR_H_
+#define CHROME_BROWSER_BROWSER_SWITCHER_BROWSER_SWITCHER_POLICY_MIGRATOR_H_
+
+#include "chrome/browser/policy/chrome_extension_policy_migrator.h"
+
+namespace browser_switcher {
+
+class BrowserSwitcherPolicyMigrator
+    : public policy::ChromeExtensionPolicyMigrator {
+ public:
+  BrowserSwitcherPolicyMigrator();
+  ~BrowserSwitcherPolicyMigrator() override;
+
+  void Migrate(policy::PolicyBundle* bundle) override;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(BrowserSwitcherPolicyMigrator);
+};
+
+}  // namespace browser_switcher
+
+#endif  // CHROME_BROWSER_BROWSER_SWITCHER_BROWSER_SWITCHER_POLICY_MIGRATOR_H_
diff --git a/chrome/browser/browser_switcher/browser_switcher_service_browsertest.cc b/chrome/browser/browser_switcher/browser_switcher_service_browsertest.cc
index 6ce5785..3bc44931 100644
--- a/chrome/browser/browser_switcher/browser_switcher_service_browsertest.cc
+++ b/chrome/browser/browser_switcher/browser_switcher_service_browsertest.cc
@@ -46,6 +46,12 @@
     "<rules version=\"1\"><docMode><domain docMode=\"9\">"
     "docs.google.com</domain></docMode></rules>";
 
+#if defined(OS_WIN)
+const char kOtherSItelistXml[] =
+    "<rules version=\"1\"><docMode><domain docMode=\"9\">"
+    "yahoo.com</domain></docMode></rules>";
+#endif
+
 bool ReturnValidXml(content::URLLoaderInterceptor::RequestParams* params) {
   std::string headers = "HTTP/1.1 200 OK\nContent-Type: text/html\n\n";
   content::URLLoaderInterceptor::WriteResponse(
@@ -64,11 +70,16 @@
   return service->sitelist()->ShouldSwitch(url);
 }
 
+void SetPolicy(policy::PolicyMap* policies,
+               const char* key,
+               std::unique_ptr<base::Value> value) {
+  policies->Set(key, policy::POLICY_LEVEL_MANDATORY, policy::POLICY_SCOPE_USER,
+                policy::POLICY_SOURCE_PLATFORM, std::move(value), nullptr);
+}
+
 void EnableBrowserSwitcher(policy::PolicyMap* policies) {
-  policies->Set(policy::key::kBrowserSwitcherEnabled,
-                policy::POLICY_LEVEL_MANDATORY, policy::POLICY_SCOPE_USER,
-                policy::POLICY_SOURCE_PLATFORM,
-                std::make_unique<base::Value>(true), nullptr);
+  SetPolicy(policies, policy::key::kBrowserSwitcherEnabled,
+            std::make_unique<base::Value>(true));
 }
 
 }  // namespace
@@ -84,18 +95,17 @@
     policy::BrowserPolicyConnector::SetPolicyProviderForTesting(&provider_);
     BrowserSwitcherService::SetFetchDelayForTesting(base::TimeDelta());
 #if defined(OS_WIN)
-    ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
-    base::PathService::Override(base::DIR_LOCAL_APP_DATA, temp_dir_.GetPath());
+    ASSERT_TRUE(fake_appdata_dir_.CreateUniqueTempDir());
+    base::PathService::Override(base::DIR_LOCAL_APP_DATA,
+                                fake_appdata_dir_.GetPath());
 #endif
   }
 
   void SetUseIeSitelist(bool use_ie_sitelist) {
     policy::PolicyMap policies;
     EnableBrowserSwitcher(&policies);
-    policies.Set(policy::key::kBrowserSwitcherUseIeSitelist,
-                 policy::POLICY_LEVEL_MANDATORY, policy::POLICY_SCOPE_USER,
-                 policy::POLICY_SOURCE_PLATFORM,
-                 std::make_unique<base::Value>(use_ie_sitelist), nullptr);
+    SetPolicy(&policies, policy::key::kBrowserSwitcherUseIeSitelist,
+              std::make_unique<base::Value>(use_ie_sitelist));
     provider_.UpdateChromePolicy(policies);
     base::RunLoop().RunUntilIdle();
   }
@@ -103,19 +113,25 @@
   void SetExternalUrl(const std::string& url) {
     policy::PolicyMap policies;
     EnableBrowserSwitcher(&policies);
-    policies.Set(policy::key::kBrowserSwitcherExternalSitelistUrl,
-                 policy::POLICY_LEVEL_MANDATORY, policy::POLICY_SCOPE_USER,
-                 policy::POLICY_SOURCE_PLATFORM,
-                 std::make_unique<base::Value>(url), nullptr);
+    SetPolicy(&policies, policy::key::kBrowserSwitcherExternalSitelistUrl,
+              std::make_unique<base::Value>(url));
     provider_.UpdateChromePolicy(policies);
     base::RunLoop().RunUntilIdle();
   }
 
+  policy::MockConfigurationPolicyProvider& policy_provider() {
+    return provider_;
+  }
+
+#if defined(OS_WIN)
+  const base::FilePath& appdata_dir() { return fake_appdata_dir_.GetPath(); }
+#endif
+
  private:
   policy::MockConfigurationPolicyProvider provider_;
 
 #if defined(OS_WIN)
-  base::ScopedTempDir temp_dir_;
+  base::ScopedTempDir fake_appdata_dir_;
 #endif
 
   DISALLOW_COPY_AND_ASSIGN(BrowserSwitcherServiceTest);
@@ -363,6 +379,136 @@
       TestTimeouts::action_timeout());
   run_loop.Run();
 }
+
+IN_PROC_BROWSER_TEST_F(BrowserSwitcherServiceTest, WritesPrefsToCacheFile) {
+  policy::PolicyMap policies;
+  EnableBrowserSwitcher(&policies);
+  SetPolicy(&policies, policy::key::kAlternativeBrowserPath,
+            std::make_unique<base::Value>("IExplore.exe"));
+  auto alt_params = std::make_unique<base::ListValue>();
+  alt_params->GetList().push_back(base::Value("--bogus-flag"));
+  SetPolicy(&policies, policy::key::kAlternativeBrowserParameters,
+            std::move(alt_params));
+  SetPolicy(&policies, policy::key::kBrowserSwitcherChromePath,
+            std::make_unique<base::Value>("chrome.exe"));
+  auto chrome_params = std::make_unique<base::ListValue>();
+  chrome_params->GetList().push_back(base::Value("--force-dark-mode"));
+  SetPolicy(&policies, policy::key::kBrowserSwitcherChromeParameters,
+            std::move(chrome_params));
+  auto url_list = std::make_unique<base::ListValue>();
+  url_list->GetList().push_back(base::Value("example.com"));
+  SetPolicy(&policies, policy::key::kBrowserSwitcherUrlList,
+            std::move(url_list));
+  auto greylist = std::make_unique<base::ListValue>();
+  greylist->GetList().push_back(base::Value("foo.example.com"));
+  SetPolicy(&policies, policy::key::kBrowserSwitcherUrlGreylist,
+            std::move(greylist));
+  policy_provider().UpdateChromePolicy(policies);
+  base::RunLoop().RunUntilIdle();
+
+  base::FilePath cache_file_path = appdata_dir()
+                                       .AppendASCII("Google")
+                                       .AppendASCII("BrowserSwitcher")
+                                       .AppendASCII("cache.dat");
+
+  // Execute everything and check "cache.dat" file contents.
+  BrowserSwitcherServiceFactory::GetForBrowserContext(browser()->profile());
+  base::RunLoop run_loop;
+  base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
+      FROM_HERE,
+      base::BindOnce(
+          [](base::FilePath path, base::OnceClosure quit) {
+            base::ScopedAllowBlockingForTesting allow_blocking;
+            base::File file(path,
+                            base::File::FLAG_OPEN | base::File::FLAG_READ);
+            ASSERT_TRUE(file.IsValid());
+
+            const char expected_output[] =
+                "1\n"
+                "IExplore.exe\n"
+                "--bogus-flag\n"
+                "chrome.exe\n"
+                "--force-dark-mode\n"
+                "1\n"
+                "example.com\n"
+                "1\n"
+                "foo.example.com\n";
+
+            std::unique_ptr<char[]> buffer(new char[file.GetLength() + 1]);
+            buffer.get()[file.GetLength()] = '\0';
+            file.Read(0, buffer.get(), file.GetLength());
+            // Check that there's no space in the URL (i.e. replaced with %20).
+            EXPECT_EQ(std::string(expected_output), std::string(buffer.get()));
+
+            std::move(quit).Run();
+          },
+          cache_file_path, run_loop.QuitClosure()),
+      TestTimeouts::action_timeout());
+  run_loop.Run();
+}
+
+IN_PROC_BROWSER_TEST_F(BrowserSwitcherServiceTest, WritesSitelistsToCacheFile) {
+  base::ScopedAllowBlockingForTesting allow_blocking;
+
+  base::ScopedTempDir dir;
+  ASSERT_TRUE(dir.CreateUniqueTempDir());
+  base::FilePath ieem_sitelist_path =
+      dir.GetPath().AppendASCII("ieem_sitelist.xml");
+  base::WriteFile(ieem_sitelist_path, kSitelistXml, strlen(kSitelistXml));
+
+  base::FilePath external_sitelist_path =
+      dir.GetPath().AppendASCII("external_sitelist.xml");
+  base::WriteFile(external_sitelist_path, kOtherSItelistXml,
+                  strlen(kOtherSItelistXml));
+
+  policy::PolicyMap policies;
+  EnableBrowserSwitcher(&policies);
+  SetPolicy(&policies, policy::key::kBrowserSwitcherExternalSitelistUrl,
+            std::make_unique<base::Value>(
+                net::FilePathToFileURL(external_sitelist_path).spec()));
+  SetPolicy(&policies, policy::key::kBrowserSwitcherUseIeSitelist,
+            std::make_unique<base::Value>(true));
+  policy_provider().UpdateChromePolicy(policies);
+  base::RunLoop().RunUntilIdle();
+  BrowserSwitcherServiceWin::SetIeemSitelistUrlForTesting(
+      net::FilePathToFileURL(ieem_sitelist_path).spec());
+
+  base::FilePath cache_file_path = appdata_dir()
+                                       .AppendASCII("Google")
+                                       .AppendASCII("BrowserSwitcher")
+                                       .AppendASCII("sitelistcache.dat");
+
+  // Execute everything and check "sitelistcache.dat" file contents. It should
+  // contain the *union* of both sitelists, not just one of them.
+  BrowserSwitcherServiceFactory::GetForBrowserContext(browser()->profile());
+  base::RunLoop run_loop;
+  base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
+      FROM_HERE,
+      base::BindOnce(
+          [](base::FilePath path, base::OnceClosure quit) {
+            base::ScopedAllowBlockingForTesting allow_blocking;
+            base::File file(path,
+                            base::File::FLAG_OPEN | base::File::FLAG_READ);
+            ASSERT_TRUE(file.IsValid());
+
+            const char expected_output[] =
+                "1\n"
+                "2\n"
+                "docs.google.com\n"
+                "yahoo.com\n";
+
+            std::unique_ptr<char[]> buffer(new char[file.GetLength() + 1]);
+            buffer.get()[file.GetLength()] = '\0';
+            file.Read(0, buffer.get(), file.GetLength());
+            // Check that there's no space in the URL (i.e. replaced with %20).
+            EXPECT_EQ(std::string(expected_output), std::string(buffer.get()));
+
+            std::move(quit).Run();
+          },
+          cache_file_path, run_loop.QuitClosure()),
+      TestTimeouts::action_timeout());
+  run_loop.Run();
+}
 #endif
 
 }  // namespace browser_switcher
diff --git a/chrome/browser/browsing_data/navigation_entry_remover.cc b/chrome/browser/browsing_data/navigation_entry_remover.cc
index e0d821fb..3067ff9 100644
--- a/chrome/browser/browsing_data/navigation_entry_remover.cc
+++ b/chrome/browser/browsing_data/navigation_entry_remover.cc
@@ -4,6 +4,8 @@
 
 #include "chrome/browser/browsing_data/navigation_entry_remover.h"
 
+#include <functional>
+
 #include "base/bind.h"
 #include "build/build_config.h"
 #include "chrome/browser/profiles/profile.h"
@@ -98,18 +100,18 @@
   auto predicate = time_range.IsValid()
                        ? base::BindRepeating(
                              &ShouldDeleteNavigationEntry, time_range.begin(),
-                             time_range.end(), base::ConstRef(restrict_urls))
+                             time_range.end(), std::cref(restrict_urls))
                        : base::BindRepeating(&UrlMatcherForNavigationEntry,
-                                             base::ConstRef(url_set));
+                                             std::cref(url_set));
 
 #if defined(OS_ANDROID)
   auto session_predicate =
       time_range.IsValid()
           ? base::BindRepeating(&ShouldDeleteSerializedNavigationEntry,
                                 time_range.begin(), time_range.end(),
-                                base::ConstRef(restrict_urls))
+                                std::cref(restrict_urls))
           : base::BindRepeating(&UrlMatcherForSerializedNavigationEntry,
-                                base::ConstRef(url_set));
+                                std::cref(url_set));
 
   for (auto it = TabModelList::begin(); it != TabModelList::end(); ++it) {
     TabModel* tab_model = *it;
diff --git a/chrome/browser/chrome_content_browser_client.cc b/chrome/browser/chrome_content_browser_client.cc
index 1c2c1c6..63111f6d 100644
--- a/chrome/browser/chrome_content_browser_client.cc
+++ b/chrome/browser/chrome_content_browser_client.cc
@@ -192,6 +192,7 @@
 #include "components/cdm/browser/cdm_message_filter_android.h"
 #include "components/certificate_matching/certificate_principal_pattern.h"
 #include "components/cloud_devices/common/cloud_devices_switches.h"
+#include "components/content_capture/browser/content_capture_receiver_manager.h"
 #include "components/content_settings/core/browser/content_settings_utils.h"
 #include "components/content_settings/core/browser/cookie_settings.h"
 #include "components/content_settings/core/browser/host_content_settings_map.h"
@@ -2107,6 +2108,9 @@
             switches::kUnsafelyTreatInsecureOriginAsSecure,
             prefs->GetString(prefs::kUnsafelyTreatInsecureOriginAsSecure));
       }
+
+      if (prefs->HasPrefPath(prefs::kAllowPopupsDuringPageUnload))
+        command_line->AppendSwitch(switches::kAllowPopupsDuringPageUnload);
     }
 
     if (IsAutoReloadEnabled())
@@ -3719,6 +3723,13 @@
         autofill::mojom::AutofillDriverAssociatedRequest(std::move(*handle)),
         render_frame_host);
     return true;
+  } else if (interface_name ==
+             content_capture::mojom::ContentCaptureReceiver::Name_) {
+    content_capture::ContentCaptureReceiverManager::BindContentCaptureReceiver(
+        content_capture::mojom::ContentCaptureReceiverAssociatedRequest(
+            std::move(*handle)),
+        render_frame_host);
+    return true;
   }
 
   return false;
diff --git a/chrome/browser/chrome_site_per_process_browsertest.cc b/chrome/browser/chrome_site_per_process_browsertest.cc
index 5e0b04f..38ceccd 100644
--- a/chrome/browser/chrome_site_per_process_browsertest.cc
+++ b/chrome/browser/chrome_site_per_process_browsertest.cc
@@ -15,6 +15,7 @@
 #include "base/strings/stringprintf.h"
 #include "base/strings/utf_string_conversions.h"
 #include "base/task/post_task.h"
+#include "build/build_config.h"
 #include "chrome/browser/chrome_content_browser_client.h"
 #include "chrome/browser/external_protocol/external_protocol_handler.h"
 #include "chrome/browser/profiles/profile.h"
@@ -947,7 +948,6 @@
     TextReceived(text);
   }
 
-  void ToggleSpellCheck(bool, bool) override {}
   void CheckSpelling(const base::string16& word,
                      int,
                      CheckSpellingCallback) override {}
@@ -955,6 +955,11 @@
                           FillSuggestionListCallback) override {}
 #endif
 
+#if defined(OS_ANDROID)
+  // spellcheck::mojom::SpellCheckHost:
+  void DisconnectSessionBridge() override {}
+#endif
+
   content::RenderProcessHost* process_host_;
   bool text_received_ = false;
   base::string16 text_;
diff --git a/chrome/browser/chromeos/BUILD.gn b/chrome/browser/chromeos/BUILD.gn
index 405597d..6aab1ef 100644
--- a/chrome/browser/chromeos/BUILD.gn
+++ b/chrome/browser/chromeos/BUILD.gn
@@ -94,6 +94,7 @@
     "//chromeos/dbus:metrics_event_proto",
     "//chromeos/dbus:oobe_config_proto",
     "//chromeos/dbus:plugin_vm_service_proto",
+    "//chromeos/dbus/constants",
     "//chromeos/dbus/services:services",
     "//chromeos/dbus/system_clock",
     "//chromeos/disks",
@@ -1691,6 +1692,8 @@
     "power/auto_screen_brightness/fake_als_reader.h",
     "power/auto_screen_brightness/fake_brightness_monitor.cc",
     "power/auto_screen_brightness/fake_brightness_monitor.h",
+    "power/auto_screen_brightness/fake_model_config_loader.cc",
+    "power/auto_screen_brightness/fake_model_config_loader.h",
     "power/auto_screen_brightness/gaussian_trainer.cc",
     "power/auto_screen_brightness/gaussian_trainer.h",
     "power/auto_screen_brightness/metrics_reporter.cc",
@@ -1749,12 +1752,6 @@
     "preferences.h",
     "prefs/pref_connector_service.cc",
     "prefs/pref_connector_service.h",
-    "printing/bulk_printers_calculator.cc",
-    "printing/bulk_printers_calculator.h",
-    "printing/bulk_printers_calculator_factory.cc",
-    "printing/bulk_printers_calculator_factory.h",
-    "printing/calculators_policies_binder.cc",
-    "printing/calculators_policies_binder.h",
     "printing/cups_print_job.cc",
     "printing/cups_print_job.h",
     "printing/cups_print_job_manager.cc",
@@ -1769,8 +1766,17 @@
     "printing/cups_printers_manager.h",
     "printing/cups_printers_manager_factory.cc",
     "printing/cups_printers_manager_factory.h",
-    "printing/enterprise_printers_provider.cc",
-    "printing/enterprise_printers_provider.h",
+    "printing/device_external_printers_factory.cc",
+    "printing/device_external_printers_factory.h",
+    "printing/device_external_printers_settings_bridge.cc",
+    "printing/device_external_printers_settings_bridge.h",
+    "printing/external_printers.cc",
+    "printing/external_printers.h",
+    "printing/external_printers_factory.cc",
+    "printing/external_printers_factory.h",
+    "printing/external_printers_policies.h",
+    "printing/external_printers_pref_bridge.cc",
+    "printing/external_printers_pref_bridge.h",
     "printing/ppd_provider_factory.cc",
     "printing/ppd_provider_factory.h",
     "printing/printer_configurer.cc",
@@ -2467,8 +2473,8 @@
     "power/process_data_collector_unittest.cc",
     "power/renderer_freezer_unittest.cc",
     "preferences_unittest.cc",
-    "printing/bulk_printers_calculator_unittest.cc",
     "printing/cups_printers_manager_unittest.cc",
+    "printing/external_printers_unittest.cc",
     "printing/printer_detector_test_util.h",
     "printing/printer_event_tracker_unittest.cc",
     "printing/printers_sync_bridge_unittest.cc",
@@ -2695,6 +2701,10 @@
       "$chrome_device_policy_pb2_dir/chrome_device_policy_pb2.py"
 
   sources = [
+    # It's important to explicitly list which files provided by this action's
+    # dependencies are actually used by this action, because the action is not
+    # automatically re-run on changes in one of the public_deps. See
+    # https://crbug.com/933359 for more information.
     chrome_device_policy_pb2_path,
   ]
 
diff --git a/chrome/browser/chromeos/apps/intent_helper/apps_navigation_throttle.cc b/chrome/browser/chromeos/apps/intent_helper/apps_navigation_throttle.cc
index f968bce6..39432bfc 100644
--- a/chrome/browser/chromeos/apps/intent_helper/apps_navigation_throttle.cc
+++ b/chrome/browser/chromeos/apps/intent_helper/apps_navigation_throttle.cc
@@ -26,7 +26,6 @@
 #include "chrome/browser/ui/browser_window.h"
 #include "chrome/browser/ui/extensions/application_launch.h"
 #include "chrome/common/chrome_features.h"
-#include "chrome/services/app_service/public/mojom/types.mojom.h"
 #include "components/arc/intent_helper/arc_intent_helper_bridge.h"
 #include "components/arc/metrics/arc_metrics_constants.h"
 #include "content/public/browser/browser_context.h"
@@ -210,7 +209,7 @@
       // TODO(crbug.com/826982): This workaround can be removed when preferences
       // are no longer persisted within the ARC container, it was necessary
       // since chrome browser is neither a PWA or ARC app.
-      if (close_reason == chromeos::IntentPickerCloseReason::STAY_IN_CHROME &&
+      if (close_reason == IntentPickerCloseReason::STAY_IN_CHROME &&
           should_persist) {
         arc::ArcIntentPickerAppFetcher::MaybeLaunchOrPersistArcApp(
             url, launch_name, /*should_launch_app=*/false,
@@ -219,8 +218,7 @@
       // We reach here if the picker was closed without an app being chosen,
       // e.g. due to the tab being closed. Keep count of this scenario so we can
       // stop the UI from showing after 2+ dismissals.
-      if (close_reason ==
-          chromeos::IntentPickerCloseReason::DIALOG_DEACTIVATED) {
+      if (close_reason == IntentPickerCloseReason::DIALOG_DEACTIVATED) {
         if (ui_auto_display_service)
           ui_auto_display_service->IncrementCounter(url);
       }
diff --git a/chrome/browser/chromeos/apps/intent_helper/apps_navigation_throttle.h b/chrome/browser/chromeos/apps/intent_helper/apps_navigation_throttle.h
index 4774f5ab..afebc9ea 100644
--- a/chrome/browser/chromeos/apps/intent_helper/apps_navigation_throttle.h
+++ b/chrome/browser/chromeos/apps/intent_helper/apps_navigation_throttle.h
@@ -13,7 +13,6 @@
 #include "base/gtest_prod_util.h"
 #include "base/macros.h"
 #include "base/memory/weak_ptr.h"
-#include "chrome/browser/chromeos/apps/intent_helper/apps_navigation_types.h"
 #include "chrome/services/app_service/public/mojom/types.mojom.h"
 #include "content/public/browser/navigation_throttle.h"
 #include "url/gurl.h"
@@ -27,6 +26,10 @@
 
 namespace chromeos {
 
+enum class AppsNavigationAction;
+enum class IntentPickerCloseReason;
+struct IntentPickerAppInfo;
+
 // Allows navigation to be routed to an installed app on Chrome OS, and provides
 // a static method for showing an intent picker for the current URL to display
 // any handling apps.
diff --git a/chrome/browser/chromeos/apps/intent_helper/apps_navigation_throttle_unittest.cc b/chrome/browser/chromeos/apps/intent_helper/apps_navigation_throttle_unittest.cc
index 3cc228a..f5c994e 100644
--- a/chrome/browser/chromeos/apps/intent_helper/apps_navigation_throttle_unittest.cc
+++ b/chrome/browser/chromeos/apps/intent_helper/apps_navigation_throttle_unittest.cc
@@ -3,6 +3,7 @@
 // found in the LICENSE file.
 
 #include "chrome/browser/chromeos/apps/intent_helper/apps_navigation_throttle.h"
+#include "chrome/browser/chromeos/apps/intent_helper/apps_navigation_types.h"
 #include "components/arc/intent_helper/arc_intent_helper_bridge.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "url/gurl.h"
diff --git a/chrome/browser/chromeos/extensions/autotest_private/autotest_private_api.cc b/chrome/browser/chromeos/extensions/autotest_private/autotest_private_api.cc
index 4c24ad3..78db9e7 100644
--- a/chrome/browser/chromeos/extensions/autotest_private/autotest_private_api.cc
+++ b/chrome/browser/chromeos/extensions/autotest_private/autotest_private_api.cc
@@ -1065,69 +1065,32 @@
 // AutotestPrivateGetPrinterListFunction
 ///////////////////////////////////////////////////////////////////////////////
 
-AutotestPrivateGetPrinterListFunction::AutotestPrivateGetPrinterListFunction()
-    : results_(std::make_unique<base::Value>(base::Value::Type::LIST)) {}
-
 AutotestPrivateGetPrinterListFunction::
-    ~AutotestPrivateGetPrinterListFunction() {
-  printers_manager_->RemoveObserver(this);
-}
+    ~AutotestPrivateGetPrinterListFunction() = default;
 
 ExtensionFunction::ResponseAction AutotestPrivateGetPrinterListFunction::Run() {
   DVLOG(1) << "AutotestPrivateGetPrinterListFunction";
 
+  auto values = std::make_unique<base::ListValue>();
   Profile* profile = Profile::FromBrowserContext(browser_context());
-  printers_manager_ = chromeos::CupsPrintersManager::Create(profile);
-  printers_manager_->AddObserver(this);
-
-  // Set up a timer to finish waiting after 10 seconds
-  timeout_timer_.Start(
-      FROM_HERE, base::TimeDelta::FromSeconds(10),
-      base::BindOnce(
-          &AutotestPrivateGetPrinterListFunction::RespondWithTimeoutError,
-          this));
-
-  return RespondLater();
-}
-
-void AutotestPrivateGetPrinterListFunction::RespondWithTimeoutError() {
-  if (did_respond())
-    return;
-  Respond(Error("Timeout occured before Enterprise printers were initialized"));
-}
-
-void AutotestPrivateGetPrinterListFunction::RespondWithSuccess() {
-  if (did_respond())
-    return;
-  Respond(OneArgument(std::move(results_)));
-}
-
-void AutotestPrivateGetPrinterListFunction::OnEnterprisePrintersInitialized() {
-  // We are ready to get the list of printers and finish.
+  std::unique_ptr<chromeos::CupsPrintersManager> printers_manager =
+      chromeos::CupsPrintersManager::Create(profile);
   std::vector<chromeos::CupsPrintersManager::PrinterClass> printer_type = {
       chromeos::CupsPrintersManager::PrinterClass::kConfigured,
       chromeos::CupsPrintersManager::PrinterClass::kEnterprise,
       chromeos::CupsPrintersManager::PrinterClass::kAutomatic};
-  base::Value::ListStorage& vresults = results_->GetList();
   for (const auto& type : printer_type) {
     std::vector<chromeos::Printer> printer_list =
-        printers_manager_->GetPrinters(type);
+        printers_manager->GetPrinters(type);
     for (const auto& printer : printer_list) {
-      vresults.push_back(base::Value(base::Value::Type::DICTIONARY));
-      base::Value& result = vresults.back();
-      result.SetKey("printerName", base::Value(printer.display_name()));
-      result.SetKey("printerId", base::Value(printer.id()));
-      result.SetKey("printerType", base::Value(GetPrinterType(type)));
+      auto result = std::make_unique<base::DictionaryValue>();
+      result->SetString("printerName", printer.display_name());
+      result->SetString("printerId", printer.id());
+      result->SetString("printerType", GetPrinterType(type));
+      values->Append(std::move(result));
     }
   }
-  // We have to respond in separate task, because it will cause a destruction of
-  // CupsPrintersManager
-  const bool posted = PostTask(
-      FROM_HERE,
-      base::BindOnce(&AutotestPrivateGetPrinterListFunction::RespondWithSuccess,
-                     this));
-  if (posted)
-    timeout_timer_.AbandonAndStop();
+  return RespondNow(OneArgument(std::move(values)));
 }
 
 ///////////////////////////////////////////////////////////////////////////////
diff --git a/chrome/browser/chromeos/extensions/autotest_private/autotest_private_api.h b/chrome/browser/chromeos/extensions/autotest_private/autotest_private_api.h
index 61948d2..dfb0592 100644
--- a/chrome/browser/chromeos/extensions/autotest_private/autotest_private_api.h
+++ b/chrome/browser/chromeos/extensions/autotest_private/autotest_private_api.h
@@ -382,24 +382,14 @@
                        scoped_refptr<base::RefCountedMemory> png_data);
 };
 
-class AutotestPrivateGetPrinterListFunction
-    : public UIThreadExtensionFunction,
-      public chromeos::CupsPrintersManager::Observer {
+class AutotestPrivateGetPrinterListFunction : public UIThreadExtensionFunction {
  public:
   DECLARE_EXTENSION_FUNCTION("autotestPrivate.getPrinterList",
                              AUTOTESTPRIVATE_GETPRINTERLIST)
-  AutotestPrivateGetPrinterListFunction();
 
  private:
   ~AutotestPrivateGetPrinterListFunction() override;
   ResponseAction Run() override;
-  void RespondWithTimeoutError();
-  void RespondWithSuccess();
-  // chromeos::CupsPrintersManager::Observer
-  void OnEnterprisePrintersInitialized() override;
-  std::unique_ptr<base::Value> results_;
-  std::unique_ptr<chromeos::CupsPrintersManager> printers_manager_;
-  base::OneShotTimer timeout_timer_;
 };
 
 class AutotestPrivateUpdatePrinterFunction : public UIThreadExtensionFunction {
diff --git a/chrome/browser/chromeos/extensions/file_manager/private_api_base.cc b/chrome/browser/chromeos/extensions/file_manager/private_api_base.cc
index 299ff601..581d480 100644
--- a/chrome/browser/chromeos/extensions/file_manager/private_api_base.cc
+++ b/chrome/browser/chromeos/extensions/file_manager/private_api_base.cc
@@ -53,11 +53,11 @@
     LOG(WARNING) << log_message;
     if (logger) {
       logger->LogRawString(logging::LOG_ERROR,
-                           "PEFORMANCE WARNING: " + log_message);
+                           "PERFORMANCE WARNING: " + log_message);
     }
   } else if (logger && elapsed >= slow_threshold_) {
     logger->Log(logging::LOG_WARNING,
-                "PEFORMANCE WARNING: %s[%d] was slow. (elapsed time: %" PRId64
+                "PERFORMANCE WARNING: %s[%d] was slow. (elapsed time: %" PRId64
                 "ms)",
                 name(), request_id(), elapsed.InMilliseconds());
   }
diff --git a/chrome/browser/chromeos/file_manager/file_manager_browsertest.cc b/chrome/browser/chromeos/file_manager/file_manager_browsertest.cc
index a90d98f..8e8a98a 100644
--- a/chrome/browser/chromeos/file_manager/file_manager_browsertest.cc
+++ b/chrome/browser/chromeos/file_manager/file_manager_browsertest.cc
@@ -576,7 +576,9 @@
         TestCase("dirCreateWithKeyboard"),
         TestCase("dirCreateWithKeyboard").EnableMyFilesVolume(),
         TestCase("dirCreateWithoutChangingCurrent").EnableMyFilesVolume(),
-        TestCase("dirCreateWithoutChangingCurrent")));
+        TestCase("dirCreateWithoutChangingCurrent"),
+        TestCase("dirContextMenuRecent"),
+        TestCase("dirContextMenuShortcut")));
 
 WRAPPED_INSTANTIATE_TEST_SUITE_P(
     DriveSpecific, /* drive_specific.js */
diff --git a/chrome/browser/chromeos/login/screens/encryption_migration_screen.cc b/chrome/browser/chromeos/login/screens/encryption_migration_screen.cc
index f4a77b5..77b0519 100644
--- a/chrome/browser/chromeos/login/screens/encryption_migration_screen.cc
+++ b/chrome/browser/chromeos/login/screens/encryption_migration_screen.cc
@@ -37,10 +37,6 @@
     view_->Hide();
 }
 
-void EncryptionMigrationScreen::OnExit() {
-  Finish(ScreenExitCode::ENCRYPTION_MIGRATION_FINISHED);
-}
-
 void EncryptionMigrationScreen::OnViewDestroyed(
     EncryptionMigrationScreenView* view) {
   if (view_ == view)
diff --git a/chrome/browser/chromeos/login/screens/encryption_migration_screen.h b/chrome/browser/chromeos/login/screens/encryption_migration_screen.h
index f63416c6..e652953 100644
--- a/chrome/browser/chromeos/login/screens/encryption_migration_screen.h
+++ b/chrome/browser/chromeos/login/screens/encryption_migration_screen.h
@@ -30,7 +30,6 @@
   void Hide() override;
 
   // EncryptionMigrationScreenView::Delegate implementation:
-  void OnExit() override;
   void OnViewDestroyed(EncryptionMigrationScreenView* view) override;
 
   // Sets the UserContext for a user whose cryptohome should be migrated.
diff --git a/chrome/browser/chromeos/login/screens/encryption_migration_screen_view.h b/chrome/browser/chromeos/login/screens/encryption_migration_screen_view.h
index c4b0f32..63edc324 100644
--- a/chrome/browser/chromeos/login/screens/encryption_migration_screen_view.h
+++ b/chrome/browser/chromeos/login/screens/encryption_migration_screen_view.h
@@ -22,9 +22,6 @@
    public:
     virtual ~Delegate() {}
 
-    // Called when screen is exited.
-    virtual void OnExit() = 0;
-
     // This method is called, when view is being destroyed. Note, if Delegate is
     // destroyed earlier then it has to call SetDelegate(NULL).
     virtual void OnViewDestroyed(EncryptionMigrationScreenView* view) = 0;
diff --git a/chrome/browser/chromeos/login/screens/eula_screen.cc b/chrome/browser/chromeos/login/screens/eula_screen.cc
index 17ef7503..42025716 100644
--- a/chrome/browser/chromeos/login/screens/eula_screen.cc
+++ b/chrome/browser/chromeos/login/screens/eula_screen.cc
@@ -23,17 +23,22 @@
 constexpr const char kUserActionBackButtonClicked[] = "back-button";
 constexpr const char kContextKeyUsageStatsEnabled[] = "usageStatsEnabled";
 
+// Reflects the value of usage statistics reporting checkbox shown in eula
+// UI. The value is expected to survive EulaScreen res-hows within a single
+// session. For example if a user unchecks the checkbox, goes back, and then
+// gets to EULA screen again, the checkbox should be unchecked.
+bool g_usage_statistics_reporting_enabled = true;
+
 }  // namespace
 
 EulaScreen::EulaScreen(BaseScreenDelegate* base_screen_delegate,
-                       Delegate* delegate,
-                       EulaView* view)
+                       EulaView* view,
+                       const ScreenExitCallback& exit_callback)
     : BaseScreen(base_screen_delegate, OobeScreen::SCREEN_OOBE_EULA),
-      delegate_(delegate),
       view_(view),
+      exit_callback_(exit_callback),
       password_fetcher_(this) {
   DCHECK(view_);
-  DCHECK(delegate_);
   if (view_)
     view_->Bind(this);
 }
@@ -71,7 +76,7 @@
 }
 
 bool EulaScreen::IsUsageStatsEnabled() const {
-  return delegate_ && delegate_->GetUsageStatisticsReporting();
+  return g_usage_statistics_reporting_enabled;
 }
 
 void EulaScreen::OnViewDestroyed(EulaView* view) {
@@ -95,19 +100,22 @@
 }
 
 void EulaScreen::OnUserAction(const std::string& action_id) {
-  if (action_id == kUserActionAcceptButtonClicked)
-    Finish(ScreenExitCode::EULA_ACCEPTED);
-  else if (action_id == kUserActionBackButtonClicked)
-    Finish(ScreenExitCode::EULA_BACK);
-  else
+  if (action_id == kUserActionAcceptButtonClicked) {
+    exit_callback_.Run(g_usage_statistics_reporting_enabled
+                           ? Result::ACCEPTED_WITH_USAGE_STATS_REPORTING
+                           : Result::ACCEPTED_WITHOUT_USAGE_STATS_REPORTING);
+  } else if (action_id == kUserActionBackButtonClicked) {
+    exit_callback_.Run(Result::BACK);
+  } else {
     BaseScreen::OnUserAction(action_id);
+  }
 }
 
 void EulaScreen::OnContextKeyUpdated(
     const ::login::ScreenContext::KeyType& key) {
-  if (key == kContextKeyUsageStatsEnabled && delegate_) {
-    delegate_->SetUsageStatisticsReporting(
-        context_.GetBoolean(kContextKeyUsageStatsEnabled));
+  if (key == kContextKeyUsageStatsEnabled) {
+    g_usage_statistics_reporting_enabled =
+        context_.GetBoolean(kContextKeyUsageStatsEnabled);
   }
 }
 
diff --git a/chrome/browser/chromeos/login/screens/eula_screen.h b/chrome/browser/chromeos/login/screens/eula_screen.h
index c7423d1..d30a8f0 100644
--- a/chrome/browser/chromeos/login/screens/eula_screen.h
+++ b/chrome/browser/chromeos/login/screens/eula_screen.h
@@ -7,6 +7,7 @@
 
 #include <string>
 
+#include "base/callback.h"
 #include "base/compiler_specific.h"
 #include "base/macros.h"
 #include "chrome/browser/chromeos/login/screens/base_screen.h"
@@ -23,18 +24,19 @@
 // to users.
 class EulaScreen : public BaseScreen, public TpmPasswordFetcherDelegate {
  public:
-  class Delegate {
-   public:
-    virtual ~Delegate() {}
-
-    // Whether usage statistics reporting is enabled on EULA screen.
-    virtual void SetUsageStatisticsReporting(bool val) = 0;
-    virtual bool GetUsageStatisticsReporting() const = 0;
+  enum class Result {
+    // The user accepted EULA, and enabled usage stats reporting.
+    ACCEPTED_WITH_USAGE_STATS_REPORTING,
+    // The user accepted EULA, and disabled usage stats reporting.
+    ACCEPTED_WITHOUT_USAGE_STATS_REPORTING,
+    // The usage did not accept EULA - they clicked back button instead.
+    BACK
   };
 
+  using ScreenExitCallback = base::RepeatingCallback<void(Result result)>;
   EulaScreen(BaseScreenDelegate* base_screen_delegate,
-             Delegate* delegate,
-             EulaView* view);
+             EulaView* view,
+             const ScreenExitCallback& exit_callback);
   ~EulaScreen() override;
 
   // Returns URL of the OEM EULA page that should be displayed using current
@@ -52,6 +54,9 @@
   // is destroyed earlier then it has to call SetModel(NULL).
   void OnViewDestroyed(EulaView* view);
 
+ protected:
+  ScreenExitCallback* exit_callback() { return &exit_callback_; }
+
  private:
   // BaseScreen implementation:
   void Show() override;
@@ -72,10 +77,10 @@
   // it's destroyed.
   std::string tpm_password_;
 
-  Delegate* delegate_;
-
   EulaView* view_;
 
+  ScreenExitCallback exit_callback_;
+
   TpmPasswordFetcher password_fetcher_;
 
   DISALLOW_COPY_AND_ASSIGN(EulaScreen);
diff --git a/chrome/browser/chromeos/login/screens/hid_detection_screen.h b/chrome/browser/chromeos/login/screens/hid_detection_screen.h
index 9807b0f..230aa91 100644
--- a/chrome/browser/chromeos/login/screens/hid_detection_screen.h
+++ b/chrome/browser/chromeos/login/screens/hid_detection_screen.h
@@ -48,12 +48,6 @@
   using InputDeviceInfoPtr = device::mojom::InputDeviceInfoPtr;
   using DeviceMap = std::map<std::string, InputDeviceInfoPtr>;
 
-  class Delegate {
-   public:
-    virtual ~Delegate() {}
-    virtual void OnHIDScreenNecessityCheck(bool screen_needed) = 0;
-  };
-
   HIDDetectionScreen(BaseScreenDelegate* base_screen_delegate,
                      HIDDetectionView* view);
   ~HIDDetectionScreen() override;
diff --git a/chrome/browser/chromeos/login/screens/mock_eula_screen.cc b/chrome/browser/chromeos/login/screens/mock_eula_screen.cc
index 7422cde..9a22b03 100644
--- a/chrome/browser/chromeos/login/screens/mock_eula_screen.cc
+++ b/chrome/browser/chromeos/login/screens/mock_eula_screen.cc
@@ -10,12 +10,16 @@
 using ::testing::_;
 
 MockEulaScreen::MockEulaScreen(BaseScreenDelegate* base_screen_delegate,
-                               Delegate* delegate,
-                               EulaView* view)
-    : EulaScreen(base_screen_delegate, delegate, view) {}
+                               EulaView* view,
+                               const ScreenExitCallback& exit_callback)
+    : EulaScreen(base_screen_delegate, view, exit_callback) {}
 
 MockEulaScreen::~MockEulaScreen() {}
 
+void MockEulaScreen::ExitScreen(Result result) {
+  exit_callback()->Run(result);
+}
+
 MockEulaView::MockEulaView() {
   EXPECT_CALL(*this, MockBind(_)).Times(AtLeast(1));
 }
diff --git a/chrome/browser/chromeos/login/screens/mock_eula_screen.h b/chrome/browser/chromeos/login/screens/mock_eula_screen.h
index 37caac4d..d00faf7 100644
--- a/chrome/browser/chromeos/login/screens/mock_eula_screen.h
+++ b/chrome/browser/chromeos/login/screens/mock_eula_screen.h
@@ -15,12 +15,14 @@
 class MockEulaScreen : public EulaScreen {
  public:
   MockEulaScreen(BaseScreenDelegate* base_screen_delegate,
-                 Delegate* delegate,
-                 EulaView* view);
+                 EulaView* view,
+                 const ScreenExitCallback& exit_callback);
   ~MockEulaScreen() override;
 
   MOCK_METHOD0(Show, void());
   MOCK_METHOD0(Hide, void());
+
+  void ExitScreen(Result result);
 };
 
 class MockEulaView : public EulaView {
diff --git a/chrome/browser/chromeos/login/screens/screen_exit_code.cc b/chrome/browser/chromeos/login/screens/screen_exit_code.cc
index cbb4a6cd..9735d450 100644
--- a/chrome/browser/chromeos/login/screens/screen_exit_code.cc
+++ b/chrome/browser/chromeos/login/screens/screen_exit_code.cc
@@ -14,7 +14,7 @@
       return "WELCOME_CONTINUED";
     case ScreenExitCode::HID_DETECTION_COMPLETED:
       return "HID_DETECTION_COMPLETED";
-    case ScreenExitCode::CONNECTION_FAILED:
+    case ScreenExitCode::DEPRECATED_CONNECTION_FAILED:
       return "CONNECTION_FAILED";
     case ScreenExitCode::DEPRECATED_UPDATE_INSTALLED:
       return "UPDATE_INSTALLED";
@@ -24,11 +24,11 @@
       return "UPDATE_ERROR_CHECKING_FOR_UPDATE";
     case ScreenExitCode::DEPRECATED_UPDATE_ERROR_UPDATING:
       return "UPDATE_ERROR_UPDATING";
-    case ScreenExitCode::USER_IMAGE_SELECTED:
+    case ScreenExitCode::DEPRECATED_USER_IMAGE_SELECTED:
       return "USER_IMAGE_SELECTED";
-    case ScreenExitCode::EULA_ACCEPTED:
+    case ScreenExitCode::DEPRECATED_EULA_ACCEPTED:
       return "EULA_ACCEPTED";
-    case ScreenExitCode::EULA_BACK:
+    case ScreenExitCode::DEPRECATED_EULA_BACK:
       return "EULA_BACK";
     case ScreenExitCode::DEPRECATED_ENTERPRISE_AUTO_ENROLLMENT_CHECK_COMPLETED:
       return "ENTERPRISE_AUTO_ENROLLMENT_CHECK_COMPLETED";
diff --git a/chrome/browser/chromeos/login/screens/screen_exit_code.h b/chrome/browser/chromeos/login/screens/screen_exit_code.h
index 721b74f..ac1814fb 100644
--- a/chrome/browser/chromeos/login/screens/screen_exit_code.h
+++ b/chrome/browser/chromeos/login/screens/screen_exit_code.h
@@ -23,7 +23,7 @@
   WELCOME_CONTINUED = 0,
   HID_DETECTION_COMPLETED = 1,
   // Connection failed while trying to load a WebPageScreen.
-  CONNECTION_FAILED = 2,
+  DEPRECATED_CONNECTION_FAILED = 2,
   DEPRECATED_UPDATE_INSTALLED = 3,
   // This exit code means EITHER that there was no update, OR that there
   // was an update, but that it was not a "critical" update. "Critical" updates
@@ -31,9 +31,9 @@
   DEPRECATED_UPDATE_NOUPDATE = 4,
   DEPRECATED_UPDATE_ERROR_CHECKING_FOR_UPDATE = 5,
   DEPRECATED_UPDATE_ERROR_UPDATING = 6,
-  USER_IMAGE_SELECTED = 7,
-  EULA_ACCEPTED = 8,
-  EULA_BACK = 9,
+  DEPRECATED_USER_IMAGE_SELECTED = 7,
+  DEPRECATED_EULA_ACCEPTED = 8,
+  DEPRECATED_EULA_BACK = 9,
   DEPRECATED_ENTERPRISE_AUTO_ENROLLMENT_CHECK_COMPLETED = 10,
   DEPRECATED_ENTERPRISE_ENROLLMENT_COMPLETED = 11,
   DEPRECATED_ENTERPRISE_ENROLLMENT_BACK = 12,
@@ -49,8 +49,8 @@
   ARC_TERMS_OF_SERVICE_SKIPPED = 23,
   ARC_TERMS_OF_SERVICE_ACCEPTED = 24,
   DEPRECATED_UPDATE_ERROR_UPDATING_CRITICAL_UPDATE = 25,
-  ENCRYPTION_MIGRATION_FINISHED = 26,
-  ENCRYPTION_MIGRATION_SKIPPED = 27,
+  DEPRECATED_ENCRYPTION_MIGRATION_FINISHED = 26,
+  DEPRECATED_ENCRYPTION_MIGRATION_SKIPPED = 27,
   SYNC_CONSENT_FINISHED = 32,
   DEMO_MODE_SETUP_FINISHED = 33,
   DEMO_MODE_SETUP_CANCELED = 34,
diff --git a/chrome/browser/chromeos/login/screens/user_image_screen.cc b/chrome/browser/chromeos/login/screens/user_image_screen.cc
index e19795b..1a7a2f1 100644
--- a/chrome/browser/chromeos/login/screens/user_image_screen.cc
+++ b/chrome/browser/chromeos/login/screens/user_image_screen.cc
@@ -353,7 +353,6 @@
   sync_timer_.reset();
   if (UserImageSyncObserver* sync_observer = GetSyncObserver())
     sync_observer->RemoveObserver(this);
-  Finish(ScreenExitCode::USER_IMAGE_SELECTED);
 }
 
 void UserImageScreen::ReportSyncResult(SyncResult timed_out) const {
diff --git a/chrome/browser/chromeos/login/users/chrome_user_manager_impl.cc b/chrome/browser/chromeos/login/users/chrome_user_manager_impl.cc
index 99defc4..0ca250f0 100644
--- a/chrome/browser/chromeos/login/users/chrome_user_manager_impl.cc
+++ b/chrome/browser/chromeos/login/users/chrome_user_manager_impl.cc
@@ -52,8 +52,8 @@
 #include "chrome/browser/chromeos/login/users/supervised_user_manager_impl.h"
 #include "chrome/browser/chromeos/policy/browser_policy_connector_chromeos.h"
 #include "chrome/browser/chromeos/policy/device_network_configuration_updater.h"
-#include "chrome/browser/chromeos/printing/bulk_printers_calculator.h"
-#include "chrome/browser/chromeos/printing/bulk_printers_calculator_factory.h"
+#include "chrome/browser/chromeos/printing/external_printers.h"
+#include "chrome/browser/chromeos/printing/external_printers_factory.h"
 #include "chrome/browser/chromeos/profiles/profile_helper.h"
 #include "chrome/browser/chromeos/session_length_limiter.h"
 #include "chrome/browser/chromeos/settings/cros_settings.h"
@@ -194,9 +194,9 @@
       ->GetMinimumVersionPolicyHandler();
 }
 
-base::WeakPtr<BulkPrintersCalculator> GetBulkPrintersCalculator(
+base::WeakPtr<ExternalPrinters> GetExternalPrinters(
     const AccountId& account_id) {
-  return BulkPrintersCalculatorFactory::Get()->GetForAccountId(account_id);
+  return ExternalPrintersFactory::Get()->GetForAccountId(account_id);
 }
 
 // Starts bluetooth logging service for accounts ending with |kGoogleDotCom|
@@ -466,7 +466,7 @@
   wallpaper_policy_observer_.reset();
   // Remove the observer before shutting down the printer policy objects.
   printers_policy_observer_.reset();
-  BulkPrintersCalculatorFactory::Get()->ShutdownProfiles();
+  ExternalPrintersFactory::Get()->Shutdown();
   registrar_.RemoveAll();
 }
 
@@ -712,7 +712,7 @@
   if (policy == policy::key::kUserAvatarImage)
     GetUserImageManager(account_id)->OnExternalDataSet(policy);
   else if (policy == policy::key::kNativePrintersBulkConfiguration)
-    GetBulkPrintersCalculator(account_id)->ClearData();
+    GetExternalPrinters(account_id)->ClearData();
   else if (policy != policy::key::kWallpaperImage)
     NOTREACHED();
 }
@@ -724,7 +724,7 @@
   if (policy == policy::key::kUserAvatarImage)
     GetUserImageManager(account_id)->OnExternalDataCleared(policy);
   else if (policy == policy::key::kNativePrintersBulkConfiguration)
-    GetBulkPrintersCalculator(account_id)->ClearData();
+    GetExternalPrinters(account_id)->ClearData();
   else if (policy == policy::key::kWallpaperImage)
     WallpaperControllerClient::Get()->RemovePolicyWallpaper(account_id);
   else
@@ -742,7 +742,7 @@
     GetUserImageManager(account_id)
         ->OnExternalDataFetched(policy, std::move(data));
   } else if (policy == policy::key::kNativePrintersBulkConfiguration) {
-    GetBulkPrintersCalculator(account_id)->SetData(std::move(data));
+    GetExternalPrinters(account_id)->SetData(std::move(data));
   } else if (policy == policy::key::kWallpaperImage) {
     WallpaperControllerClient::Get()->SetPolicyWallpaper(account_id,
                                                          std::move(data));
@@ -1163,7 +1163,7 @@
   // |known_user::RemovePrefs|. See https://crbug.com/778077.
   WallpaperControllerClient::Get()->RemoveUserWallpaper(account_id);
   GetUserImageManager(account_id)->DeleteUserImage();
-  BulkPrintersCalculatorFactory::Get()->RemoveForUserId(account_id);
+  ExternalPrintersFactory::Get()->RemoveForUserId(account_id);
   // TODO(tbarzic): Forward data removal request to ash::HammerDeviceHandler,
   // instead of removing the prefs value here.
   if (GetLocalState()->FindPreference(ash::prefs::kDetachableBaseDevices)) {
diff --git a/chrome/browser/chromeos/login/users/chrome_user_manager_impl.h b/chrome/browser/chromeos/login/users/chrome_user_manager_impl.h
index a7dae9b..e75c886 100644
--- a/chrome/browser/chromeos/login/users/chrome_user_manager_impl.h
+++ b/chrome/browser/chromeos/login/users/chrome_user_manager_impl.h
@@ -26,6 +26,7 @@
 #include "chrome/browser/chromeos/policy/device_local_account.h"
 #include "chrome/browser/chromeos/policy/device_local_account_policy_service.h"
 #include "chrome/browser/chromeos/policy/minimum_version_policy_handler.h"
+#include "chrome/browser/chromeos/printing/external_printers.h"
 #include "chrome/browser/chromeos/settings/cros_settings.h"
 #include "chrome/browser/chromeos/settings/device_settings_service.h"
 #include "components/account_id/account_id.h"
diff --git a/chrome/browser/chromeos/login/wizard_controller.cc b/chrome/browser/chromeos/login/wizard_controller.cc
index 96dcc3d9..23e5f7b1 100644
--- a/chrome/browser/chromeos/login/wizard_controller.cc
+++ b/chrome/browser/chromeos/login/wizard_controller.cc
@@ -55,6 +55,7 @@
 #include "chrome/browser/chromeos/login/screens/error_screen.h"
 #include "chrome/browser/chromeos/login/screens/eula_screen.h"
 #include "chrome/browser/chromeos/login/screens/fingerprint_setup_screen.h"
+#include "chrome/browser/chromeos/login/screens/hid_detection_screen.h"
 #include "chrome/browser/chromeos/login/screens/hid_detection_view.h"
 #include "chrome/browser/chromeos/login/screens/kiosk_autolaunch_screen.h"
 #include "chrome/browser/chromeos/login/screens/kiosk_enable_screen.h"
@@ -69,7 +70,6 @@
 #include "chrome/browser/chromeos/login/screens/terms_of_service_screen.h"
 #include "chrome/browser/chromeos/login/screens/update_required_screen.h"
 #include "chrome/browser/chromeos/login/screens/update_screen.h"
-#include "chrome/browser/chromeos/login/screens/user_image_screen.h"
 #include "chrome/browser/chromeos/login/screens/welcome_view.h"
 #include "chrome/browser/chromeos/login/screens/wrong_hwid_screen.h"
 #include "chrome/browser/chromeos/login/session/user_session_manager.h"
@@ -386,10 +386,11 @@
         this, oobe_ui->GetUpdateView(),
         base::BindRepeating(&WizardController::OnUpdateScreenExit,
                             weak_factory_.GetWeakPtr()));
-  } else if (screen == OobeScreen::SCREEN_USER_IMAGE_PICKER) {
-    return std::make_unique<UserImageScreen>(this, oobe_ui->GetUserImageView());
   } else if (screen == OobeScreen::SCREEN_OOBE_EULA) {
-    return std::make_unique<EulaScreen>(this, this, oobe_ui->GetEulaView());
+    return std::make_unique<EulaScreen>(
+        this, oobe_ui->GetEulaView(),
+        base::BindRepeating(&WizardController::OnEulaScreenExit,
+                            weak_factory_.GetWeakPtr()));
   } else if (screen == OobeScreen::SCREEN_OOBE_ENROLLMENT) {
     return std::make_unique<EnrollmentScreen>(
         this, oobe_ui->GetEnrollmentScreenView(),
@@ -513,15 +514,6 @@
   SetCurrentScreen(previous_screen_);
 }
 
-void WizardController::ShowUserImageScreen() {
-  VLOG(1) << "Showing user image screen.";
-  // Status area has been already shown at sign in screen so it
-  // doesn't make sense to hide it here and then show again at user session as
-  // this produces undesired UX transitions.
-  UpdateStatusAreaVisibilityForScreen(OobeScreen::SCREEN_USER_IMAGE_PICKER);
-  SetCurrentScreen(GetScreen(OobeScreen::SCREEN_USER_IMAGE_PICKER));
-}
-
 void WizardController::ShowEulaScreen() {
   VLOG(1) << "Showing EULA screen.";
   UpdateStatusAreaVisibilityForScreen(OobeScreen::SCREEN_OOBE_EULA);
@@ -804,6 +796,46 @@
   return false;
 }
 
+void WizardController::OnEulaScreenExit(EulaScreen::Result result) {
+  VLOG(1) << "EULA screen exit: " << static_cast<int>(result);
+  OnScreenExit(OobeScreen::SCREEN_OOBE_EULA);
+
+  switch (result) {
+    case EulaScreen::Result::ACCEPTED_WITH_USAGE_STATS_REPORTING:
+      OnEulaAccepted(true /*usage_statistics_reporting_enabled*/);
+      break;
+    case EulaScreen::Result::ACCEPTED_WITHOUT_USAGE_STATS_REPORTING:
+      OnEulaAccepted(false /*usage_statistics_reporting_enabled*/);
+      break;
+    case EulaScreen::Result::BACK:
+      ShowNetworkScreen();
+      break;
+  }
+}
+
+void WizardController::OnEulaAccepted(bool usage_statistics_reporting_enabled) {
+  time_eula_accepted_ = base::Time::Now();
+  StartupUtils::MarkEulaAccepted();
+  ChangeMetricsReportingStateWithReply(
+      usage_statistics_reporting_enabled,
+      base::BindRepeating(&WizardController::OnChangedMetricsReportingState,
+                          weak_factory_.GetWeakPtr()));
+  PerformPostEulaActions();
+
+  if (arc::IsArcTermsOfServiceOobeNegotiationNeeded()) {
+    ShowArcTermsOfServiceScreen();
+    return;
+  } else if (demo_setup_controller_) {
+    ShowDemoModeSetupScreen();
+  }
+
+  if (skip_update_enroll_after_eula_) {
+    ShowAutoEnrollmentCheckScreen();
+  } else {
+    InitiateOOBEUpdate();
+  }
+}
+
 void WizardController::OnUpdateScreenExit(UpdateScreen::Result result) {
   VLOG(1) << "Update screen exit: " << static_cast<int>(result);
   OnScreenExit(OobeScreen::SCREEN_OOBE_UPDATE);
@@ -890,38 +922,6 @@
   ShowNetworkScreen();
 }
 
-void WizardController::OnConnectionFailed() {
-  // TODO(dpolukhin): show error message after login screen is displayed.
-  ShowLoginScreen(LoginScreenContext());
-}
-
-void WizardController::OnEulaAccepted() {
-  time_eula_accepted_ = base::Time::Now();
-  StartupUtils::MarkEulaAccepted();
-  ChangeMetricsReportingStateWithReply(
-      usage_statistics_reporting_,
-      base::Bind(&WizardController::OnChangedMetricsReportingState,
-                 weak_factory_.GetWeakPtr()));
-  PerformPostEulaActions();
-
-  if (arc::IsArcTermsOfServiceOobeNegotiationNeeded()) {
-    ShowArcTermsOfServiceScreen();
-    return;
-  } else if (demo_setup_controller_) {
-    ShowDemoModeSetupScreen();
-  }
-
-  if (skip_update_enroll_after_eula_) {
-    ShowAutoEnrollmentCheckScreen();
-  } else {
-    InitiateOOBEUpdate();
-  }
-}
-
-void WizardController::OnEulaBack() {
-  ShowNetworkScreen();
-}
-
 void WizardController::OnChangedMetricsReportingState(bool enabled) {
   StatsReportingController::Get()->SetEnabled(
       ProfileManager::GetActiveUserProfile(), enabled);
@@ -934,10 +934,6 @@
 #endif
 }
 
-void WizardController::OnUserImageSelected() {
-  OnOobeFlowFinished();
-}
-
 void WizardController::OnDeviceModificationCanceled() {
   if (previous_screen_) {
     SetCurrentScreen(previous_screen_);
@@ -1340,8 +1336,6 @@
     ShowLoginScreen(LoginScreenContext());
   } else if (screen == OobeScreen::SCREEN_OOBE_UPDATE) {
     InitiateOOBEUpdate();
-  } else if (screen == OobeScreen::SCREEN_USER_IMAGE_PICKER) {
-    ShowUserImageScreen();
   } else if (screen == OobeScreen::SCREEN_OOBE_EULA) {
     ShowEulaScreen();
   } else if (screen == OobeScreen::SCREEN_OOBE_RESET) {
@@ -1440,18 +1434,6 @@
     case ScreenExitCode::WELCOME_CONTINUED:
       OnWelcomeContinued();
       break;
-    case ScreenExitCode::CONNECTION_FAILED:
-      OnConnectionFailed();
-      break;
-    case ScreenExitCode::USER_IMAGE_SELECTED:
-      OnUserImageSelected();
-      break;
-    case ScreenExitCode::EULA_ACCEPTED:
-      OnEulaAccepted();
-      break;
-    case ScreenExitCode::EULA_BACK:
-      OnEulaBack();
-      break;
     case ScreenExitCode::ENABLE_DEBUGGING_CANCELED:
       OnDeviceModificationCanceled();
       break;
@@ -1546,14 +1528,6 @@
   SetCurrentScreen(parent_screen);
 }
 
-void WizardController::SetUsageStatisticsReporting(bool val) {
-  usage_statistics_reporting_ = val;
-}
-
-bool WizardController::GetUsageStatisticsReporting() const {
-  return usage_statistics_reporting_;
-}
-
 void WizardController::OnEnableDebuggingScreenRequested() {
   if (!login_screen_started())
     AdvanceToScreen(OobeScreen::SCREEN_OOBE_ENABLE_DEBUGGING);
diff --git a/chrome/browser/chromeos/login/wizard_controller.h b/chrome/browser/chromeos/login/wizard_controller.h
index c69b058..d2d1f4e 100644
--- a/chrome/browser/chromeos/login/wizard_controller.h
+++ b/chrome/browser/chromeos/login/wizard_controller.h
@@ -25,6 +25,7 @@
 #include "chrome/browser/chromeos/login/screens/base_screen_delegate.h"
 #include "chrome/browser/chromeos/login/screens/eula_screen.h"
 #include "chrome/browser/chromeos/login/screens/hid_detection_screen.h"
+#include "chrome/browser/chromeos/login/screens/kiosk_autolaunch_screen.h"
 #include "chrome/browser/chromeos/login/screens/network_screen.h"
 #include "chrome/browser/chromeos/login/screens/reset_screen.h"
 #include "chrome/browser/chromeos/login/screens/update_screen.h"
@@ -51,9 +52,7 @@
 // Class that manages control flow between wizard screens. Wizard controller
 // interacts with screen controllers to move the user between screens.
 class WizardController : public BaseScreenDelegate,
-                         public EulaScreen::Delegate,
-                         public WelcomeScreen::Delegate,
-                         public HIDDetectionScreen::Delegate {
+                         public WelcomeScreen::Delegate {
  public:
   WizardController();
   ~WizardController() override;
@@ -119,10 +118,6 @@
   // Skip update, go straight to enrollment after EULA is accepted.
   void SkipUpdateEnrollAfterEula();
 
-  // TODO(antrim) : temporary hack. Should be removed once screen system is
-  // reworked at hackaton.
-  void EnableUserImageScreenReturnToPreviousHack();
-
   // Returns current DemoSetupController if demo setup flow is in progress or
   // nullptr otherwise.
   DemoSetupController* demo_setup_controller() const {
@@ -159,7 +154,6 @@
   // Show specific screen.
   void ShowWelcomeScreen();
   void ShowNetworkScreen();
-  void ShowUserImageScreen();
   void ShowEulaScreen();
   void ShowEnrollmentScreen();
   void ShowDemoModeSetupScreen();
@@ -199,6 +193,8 @@
   // Exit handlers:
   void OnNetworkScreenExit(NetworkScreen::Result result);
   bool ShowEulaOrArcTosAfterNetworkScreen();
+  void OnEulaScreenExit(EulaScreen::Result result);
+  void OnEulaAccepted(bool usage_statistics_reporting_enabled);
   void OnUpdateScreenExit(UpdateScreen::Result result);
   void OnUpdateCompleted();
   void OnAutoEnrollmentCheckScreenExit();
@@ -206,10 +202,6 @@
   void OnEnrollmentDone();
   void OnHIDDetectionCompleted();
   void OnWelcomeContinued();
-  void OnConnectionFailed();
-  void OnEulaAccepted();
-  void OnEulaBack();
-  void OnUserImageSelected();
   void OnDeviceModificationCanceled();
   void OnKioskAutolaunchCanceled();
   void OnKioskAutolaunchConfirmed();
@@ -230,7 +222,6 @@
   void OnDemoSetupCanceled();
   void OnDemoPreferencesContinued();
   void OnDemoPreferencesCanceled();
-  void OnWaitForContainerReadyFinished();
   void OnSupervisionTransitionFinished();
   void OnAssistantOptInFlowFinished();
   void OnMultiDeviceSetupFinished();
@@ -265,15 +256,10 @@
   void ShowErrorScreen() override;
   void HideErrorScreen(BaseScreen* parent_screen) override;
 
-  // Overridden from EulaScreen::Delegate:
-  void SetUsageStatisticsReporting(bool val) override;
-  bool GetUsageStatisticsReporting() const override;
-
   // Override from WelcomeScreen::Delegate:
   void OnEnableDebuggingScreenRequested() override;
 
-  // Override from HIDDetectionScreen::Delegate
-  void OnHIDScreenNecessityCheck(bool screen_needed) override;
+  void OnHIDScreenNecessityCheck(bool screen_needed);
 
   // Notification of a change in the state of an accessibility setting.
   void OnAccessibilityStatusChanged(
@@ -374,10 +360,6 @@
 
   base::OneShotTimer smooth_show_timer_;
 
-  // State of Usage stat/error reporting checkbox on EULA screen
-  // during wizard lifetime.
-  bool usage_statistics_reporting_ = true;
-
   // If true then update check is cancelled and enrollment is started after
   // EULA is accepted.
   bool skip_update_enroll_after_eula_ = false;
diff --git a/chrome/browser/chromeos/login/wizard_controller_browsertest.cc b/chrome/browser/chromeos/login/wizard_controller_browsertest.cc
index fb30bc12..baf05d9 100644
--- a/chrome/browser/chromeos/login/wizard_controller_browsertest.cc
+++ b/chrome/browser/chromeos/login/wizard_controller_browsertest.cc
@@ -617,7 +617,9 @@
     mock_eula_view_ = std::make_unique<MockEulaView>();
     mock_eula_screen_ =
         MockScreenExpectLifecycle(std::make_unique<MockEulaScreen>(
-            wizard_controller, wizard_controller, mock_eula_view_.get()));
+            wizard_controller, mock_eula_view_.get(),
+            base::BindRepeating(&WizardController::OnEulaScreenExit,
+                                base::Unretained(wizard_controller))));
 
     mock_enrollment_screen_view_ = std::make_unique<MockEnrollmentScreenView>();
     mock_enrollment_screen_ =
@@ -771,7 +773,8 @@
     EXPECT_CALL(*mock_update_screen_, Show()).Times(1);
     // Enable TimeZone resolve
     InitTimezoneResolver();
-    OnExit(ScreenExitCode::EULA_ACCEPTED);
+    mock_eula_screen_->ExitScreen(
+        EulaScreen::Result::ACCEPTED_WITHOUT_USAGE_STATS_REPORTING);
     EXPECT_TRUE(GetGeolocationProvider());
 
     // Let update screen smooth time process (time = 0ms).
@@ -873,7 +876,8 @@
   EXPECT_CALL(*mock_eula_screen_, Hide()).Times(1);
   EXPECT_CALL(*mock_update_screen_, StartNetworkCheck()).Times(1);
   EXPECT_CALL(*mock_update_screen_, Show()).Times(1);
-  OnExit(ScreenExitCode::EULA_ACCEPTED);
+  mock_eula_screen_->ExitScreen(
+      EulaScreen::Result::ACCEPTED_WITHOUT_USAGE_STATS_REPORTING);
 
   // Let update screen smooth time process (time = 0ms).
   content::RunAllPendingInMessageLoop();
@@ -913,7 +917,8 @@
   EXPECT_CALL(*mock_eula_screen_, Hide()).Times(1);
   EXPECT_CALL(*mock_update_screen_, StartNetworkCheck()).Times(1);
   EXPECT_CALL(*mock_update_screen_, Show()).Times(1);
-  OnExit(ScreenExitCode::EULA_ACCEPTED);
+  mock_eula_screen_->ExitScreen(
+      EulaScreen::Result::ACCEPTED_WITHOUT_USAGE_STATS_REPORTING);
 
   // Let update screen smooth time process (time = 0ms).
   content::RunAllPendingInMessageLoop();
@@ -953,7 +958,8 @@
                   EnrollmentModeMatches(policy::EnrollmentConfig::MODE_MANUAL)))
       .Times(1);
   EXPECT_CALL(*mock_auto_enrollment_check_screen_, Show()).Times(1);
-  OnExit(ScreenExitCode::EULA_ACCEPTED);
+  mock_eula_screen_->ExitScreen(
+      EulaScreen::Result::ACCEPTED_WITHOUT_USAGE_STATS_REPORTING);
   content::RunAllPendingInMessageLoop();
 
   CheckCurrentScreen(OobeScreen::SCREEN_AUTO_ENROLLMENT_CHECK);
@@ -985,7 +991,7 @@
   CheckCurrentScreen(OobeScreen::SCREEN_OOBE_EULA);
   EXPECT_CALL(*mock_network_screen_, Show()).Times(1);
   EXPECT_CALL(*mock_eula_screen_, Hide()).Times(1);
-  OnExit(ScreenExitCode::EULA_BACK);
+  mock_eula_screen_->ExitScreen(EulaScreen::Result::BACK);
 
   CheckCurrentScreen(OobeScreen::SCREEN_OOBE_NETWORK);
 }
@@ -1066,7 +1072,8 @@
   EXPECT_CALL(*mock_eula_screen_, Hide()).Times(1);
   EXPECT_CALL(*mock_update_screen_, StartNetworkCheck()).Times(1);
   EXPECT_CALL(*mock_update_screen_, Show()).Times(1);
-  OnExit(ScreenExitCode::EULA_ACCEPTED);
+  mock_eula_screen_->ExitScreen(
+      EulaScreen::Result::ACCEPTED_WITHOUT_USAGE_STATS_REPORTING);
 
   // Let update screen smooth time process (time = 0ms).
   content::RunAllPendingInMessageLoop();
@@ -1195,7 +1202,8 @@
   EXPECT_CALL(*mock_eula_screen_, Hide()).Times(1);
   EXPECT_CALL(*mock_update_screen_, StartNetworkCheck()).Times(1);
   EXPECT_CALL(*mock_update_screen_, Show()).Times(1);
-  OnExit(ScreenExitCode::EULA_ACCEPTED);
+  mock_eula_screen_->ExitScreen(
+      EulaScreen::Result::ACCEPTED_WITHOUT_USAGE_STATS_REPORTING);
 
   // Let update screen smooth time process (time = 0ms).
   content::RunAllPendingInMessageLoop();
@@ -1240,7 +1248,8 @@
   EXPECT_CALL(*mock_eula_screen_, Hide()).Times(1);
   EXPECT_CALL(*mock_update_screen_, StartNetworkCheck()).Times(1);
   EXPECT_CALL(*mock_update_screen_, Show()).Times(1);
-  OnExit(ScreenExitCode::EULA_ACCEPTED);
+  mock_eula_screen_->ExitScreen(
+      EulaScreen::Result::ACCEPTED_WITHOUT_USAGE_STATS_REPORTING);
 
   // Let update screen smooth time process (time = 0ms).
   content::RunAllPendingInMessageLoop();
@@ -1338,7 +1347,8 @@
   EXPECT_CALL(*mock_eula_screen_, Hide()).Times(1);
   EXPECT_CALL(*mock_update_screen_, StartNetworkCheck()).Times(1);
   EXPECT_CALL(*mock_update_screen_, Show()).Times(1);
-  OnExit(ScreenExitCode::EULA_ACCEPTED);
+  mock_eula_screen_->ExitScreen(
+      EulaScreen::Result::ACCEPTED_WITHOUT_USAGE_STATS_REPORTING);
 
   // Let update screen smooth time process (time = 0ms).
   base::RunLoop().RunUntilIdle();
@@ -1436,7 +1446,8 @@
   EXPECT_CALL(*mock_eula_screen_, Hide()).Times(1);
   EXPECT_CALL(*mock_update_screen_, StartNetworkCheck()).Times(1);
   EXPECT_CALL(*mock_update_screen_, Show()).Times(1);
-  OnExit(ScreenExitCode::EULA_ACCEPTED);
+  mock_eula_screen_->ExitScreen(
+      EulaScreen::Result::ACCEPTED_WITHOUT_USAGE_STATS_REPORTING);
 
   // Let update screen smooth time process (time = 0ms).
   base::RunLoop().RunUntilIdle();
@@ -1580,7 +1591,8 @@
   EXPECT_CALL(*mock_eula_screen_, Hide()).Times(1);
   EXPECT_CALL(*mock_update_screen_, StartNetworkCheck()).Times(1);
   EXPECT_CALL(*mock_update_screen_, Show()).Times(1);
-  OnExit(ScreenExitCode::EULA_ACCEPTED);
+  mock_eula_screen_->ExitScreen(
+      EulaScreen::Result::ACCEPTED_WITHOUT_USAGE_STATS_REPORTING);
 
   // Let update screen smooth time process (time = 0ms).
   base::RunLoop().RunUntilIdle();
@@ -1654,7 +1666,8 @@
   EXPECT_CALL(*mock_eula_screen_, Hide()).Times(1);
   EXPECT_CALL(*mock_update_screen_, StartNetworkCheck()).Times(1);
   EXPECT_CALL(*mock_update_screen_, Show()).Times(1);
-  OnExit(ScreenExitCode::EULA_ACCEPTED);
+  mock_eula_screen_->ExitScreen(
+      EulaScreen::Result::ACCEPTED_WITHOUT_USAGE_STATS_REPORTING);
 
   // Let update screen smooth time process (time = 0ms).
   base::RunLoop().RunUntilIdle();
@@ -1749,7 +1762,8 @@
   EXPECT_CALL(*mock_eula_screen_, Hide()).Times(1);
   EXPECT_CALL(*mock_update_screen_, StartNetworkCheck()).Times(1);
   EXPECT_CALL(*mock_update_screen_, Show()).Times(1);
-  OnExit(ScreenExitCode::EULA_ACCEPTED);
+  mock_eula_screen_->ExitScreen(
+      EulaScreen::Result::ACCEPTED_WITHOUT_USAGE_STATS_REPORTING);
 
   // Let update screen smooth time process (time = 0ms).
   base::RunLoop().RunUntilIdle();
@@ -1793,7 +1807,8 @@
   EXPECT_CALL(*mock_eula_screen_, Hide()).Times(1);
   EXPECT_CALL(*mock_update_screen_, StartNetworkCheck()).Times(1);
   EXPECT_CALL(*mock_update_screen_, Show()).Times(1);
-  OnExit(ScreenExitCode::EULA_ACCEPTED);
+  mock_eula_screen_->ExitScreen(
+      EulaScreen::Result::ACCEPTED_WITHOUT_USAGE_STATS_REPORTING);
 
   // Let update screen smooth time process (time = 0ms).
   base::RunLoop().RunUntilIdle();
@@ -1849,7 +1864,8 @@
   EXPECT_CALL(*mock_eula_screen_, Hide()).Times(1);
   EXPECT_CALL(*mock_update_screen_, StartNetworkCheck()).Times(1);
   EXPECT_CALL(*mock_update_screen_, Show()).Times(1);
-  OnExit(ScreenExitCode::EULA_ACCEPTED);
+  mock_eula_screen_->ExitScreen(
+      EulaScreen::Result::ACCEPTED_WITHOUT_USAGE_STATS_REPORTING);
 
   // Let update screen smooth time process (time = 0ms).
   task_runner->RunUntilIdle();
@@ -1905,7 +1921,8 @@
   EXPECT_CALL(*mock_eula_screen_, Hide()).Times(1);
   EXPECT_CALL(*mock_update_screen_, StartNetworkCheck()).Times(1);
   EXPECT_CALL(*mock_update_screen_, Show()).Times(1);
-  OnExit(ScreenExitCode::EULA_ACCEPTED);
+  mock_eula_screen_->ExitScreen(
+      EulaScreen::Result::ACCEPTED_WITHOUT_USAGE_STATS_REPORTING);
 
   // Let update screen smooth time process (time = 0ms).
   base::RunLoop().RunUntilIdle();
@@ -1987,7 +2004,8 @@
   EXPECT_CALL(*mock_eula_screen_, Hide()).Times(1);
   EXPECT_CALL(*mock_update_screen_, StartNetworkCheck()).Times(1);
   EXPECT_CALL(*mock_update_screen_, Show()).Times(1);
-  OnExit(ScreenExitCode::EULA_ACCEPTED);
+  mock_eula_screen_->ExitScreen(
+      EulaScreen::Result::ACCEPTED_WITHOUT_USAGE_STATS_REPORTING);
 
   // Let update screen smooth time process (time = 0ms).
   base::RunLoop().RunUntilIdle();
@@ -2152,7 +2170,8 @@
   EXPECT_CALL(*mock_eula_screen_, Hide()).Times(1);
   EXPECT_CALL(*mock_update_screen_, StartNetworkCheck()).Times(1);
   EXPECT_CALL(*mock_update_screen_, Show()).Times(1);
-  OnExit(ScreenExitCode::EULA_ACCEPTED);
+  mock_eula_screen_->ExitScreen(
+      EulaScreen::Result::ACCEPTED_WITHOUT_USAGE_STATS_REPORTING);
 
   // Let update screen smooth time process (time = 0ms).
   content::RunAllPendingInMessageLoop();
@@ -2200,7 +2219,8 @@
   EXPECT_CALL(*mock_eula_screen_, Hide()).Times(1);
   EXPECT_CALL(*mock_update_screen_, StartNetworkCheck()).Times(1);
   EXPECT_CALL(*mock_update_screen_, Show()).Times(1);
-  OnExit(ScreenExitCode::EULA_ACCEPTED);
+  mock_eula_screen_->ExitScreen(
+      EulaScreen::Result::ACCEPTED_WITHOUT_USAGE_STATS_REPORTING);
 
   // Let update screen smooth time process (time = 0ms).
   content::RunAllPendingInMessageLoop();
@@ -2327,7 +2347,8 @@
   EXPECT_CALL(*mock_eula_screen_, Hide()).Times(1);
   EXPECT_CALL(*mock_arc_terms_of_service_screen_, Show()).Times(1);
 
-  OnExit(ScreenExitCode::EULA_ACCEPTED);
+  mock_eula_screen_->ExitScreen(
+      EulaScreen::Result::ACCEPTED_WITHOUT_USAGE_STATS_REPORTING);
 
   CheckCurrentScreen(OobeScreen::SCREEN_ARC_TERMS_OF_SERVICE);
   EXPECT_TRUE(DemoSetupController::IsOobeDemoSetupFlowInProgress());
@@ -2399,7 +2420,8 @@
   EXPECT_CALL(*mock_eula_screen_, Hide()).Times(1);
   EXPECT_CALL(*mock_arc_terms_of_service_screen_, Show()).Times(1);
 
-  OnExit(ScreenExitCode::EULA_ACCEPTED);
+  mock_eula_screen_->ExitScreen(
+      EulaScreen::Result::ACCEPTED_WITHOUT_USAGE_STATS_REPORTING);
 
   CheckCurrentScreen(OobeScreen::SCREEN_ARC_TERMS_OF_SERVICE);
   EXPECT_TRUE(DemoSetupController::IsOobeDemoSetupFlowInProgress());
@@ -2454,7 +2476,8 @@
   EXPECT_CALL(*mock_eula_screen_, Hide()).Times(1);
   EXPECT_CALL(*mock_arc_terms_of_service_screen_, Show()).Times(1);
 
-  OnExit(ScreenExitCode::EULA_ACCEPTED);
+  mock_eula_screen_->ExitScreen(
+      EulaScreen::Result::ACCEPTED_WITHOUT_USAGE_STATS_REPORTING);
 
   CheckCurrentScreen(OobeScreen::SCREEN_ARC_TERMS_OF_SERVICE);
   EXPECT_TRUE(DemoSetupController::IsOobeDemoSetupFlowInProgress());
@@ -2545,7 +2568,7 @@
   EXPECT_CALL(*mock_eula_screen_, Hide()).Times(1);
   EXPECT_CALL(*mock_network_screen_, Show()).Times(1);
 
-  OnExit(ScreenExitCode::EULA_BACK);
+  mock_eula_screen_->ExitScreen(EulaScreen::Result::BACK);
 
   CheckCurrentScreen(OobeScreen::SCREEN_OOBE_NETWORK);
   EXPECT_TRUE(DemoSetupController::IsOobeDemoSetupFlowInProgress());
@@ -2624,7 +2647,8 @@
   EXPECT_CALL(*mock_eula_screen_, Hide()).Times(1);
   EXPECT_CALL(*mock_arc_terms_of_service_screen_, Show()).Times(1);
 
-  OnExit(ScreenExitCode::EULA_ACCEPTED);
+  mock_eula_screen_->ExitScreen(
+      EulaScreen::Result::ACCEPTED_WITHOUT_USAGE_STATS_REPORTING);
 
   CheckCurrentScreen(OobeScreen::SCREEN_ARC_TERMS_OF_SERVICE);
   EXPECT_TRUE(DemoSetupController::IsOobeDemoSetupFlowInProgress());
diff --git a/chrome/browser/chromeos/policy/device_native_printers_handler.cc b/chrome/browser/chromeos/policy/device_native_printers_handler.cc
index 423e1c80..1f614bd7 100644
--- a/chrome/browser/chromeos/policy/device_native_printers_handler.cc
+++ b/chrome/browser/chromeos/policy/device_native_printers_handler.cc
@@ -7,15 +7,16 @@
 #include <utility>
 
 #include "base/memory/weak_ptr.h"
-#include "chrome/browser/chromeos/printing/bulk_printers_calculator_factory.h"
+#include "chrome/browser/chromeos/printing/device_external_printers_factory.h"
+#include "chrome/browser/chromeos/printing/external_printers.h"
 #include "components/policy/policy_constants.h"
 
 namespace policy {
 
 namespace {
 
-base::WeakPtr<chromeos::BulkPrintersCalculator> GetBulkPrintersCalculator() {
-  return chromeos::BulkPrintersCalculatorFactory::Get()->GetForDevice();
+base::WeakPtr<chromeos::ExternalPrinters> GetExternalPrinters() {
+  return chromeos::DeviceExternalPrintersFactory::Get()->GetForDevice();
 }
 
 }  // namespace
@@ -32,25 +33,25 @@
 
 void DeviceNativePrintersHandler::OnDeviceExternalDataSet(
     const std::string& policy) {
-  GetBulkPrintersCalculator()->ClearData();
+  GetExternalPrinters()->ClearData();
 }
 
 void DeviceNativePrintersHandler::OnDeviceExternalDataCleared(
     const std::string& policy) {
-  GetBulkPrintersCalculator()->ClearData();
+  GetExternalPrinters()->ClearData();
 }
 
 void DeviceNativePrintersHandler::OnDeviceExternalDataFetched(
     const std::string& policy,
     std::unique_ptr<std::string> data,
     const base::FilePath& file_path) {
-  GetBulkPrintersCalculator()->SetData(std::move(data));
+  GetExternalPrinters()->SetData(std::move(data));
 }
 
 void DeviceNativePrintersHandler::Shutdown() {
   if (device_native_printers_observer_)
     device_native_printers_observer_.reset();
-  chromeos::BulkPrintersCalculatorFactory::Get()->Shutdown();
+  chromeos::DeviceExternalPrintersFactory::Get()->Shutdown();
 }
 
 }  // namespace policy
diff --git a/chrome/browser/chromeos/policy/device_native_printers_handler_unittest.cc b/chrome/browser/chromeos/policy/device_native_printers_handler_unittest.cc
index e9d1a24..6fb0a262 100644
--- a/chrome/browser/chromeos/policy/device_native_printers_handler_unittest.cc
+++ b/chrome/browser/chromeos/policy/device_native_printers_handler_unittest.cc
@@ -8,8 +8,8 @@
 #include <string>
 
 #include "base/test/scoped_task_environment.h"
-#include "chrome/browser/chromeos/printing/bulk_printers_calculator.h"
-#include "chrome/browser/chromeos/printing/bulk_printers_calculator_factory.h"
+#include "chrome/browser/chromeos/printing/device_external_printers_factory.h"
+#include "chrome/browser/chromeos/printing/external_printers.h"
 #include "chrome/browser/chromeos/settings/scoped_cros_settings_test_helper.h"
 #include "chromeos/printing/printer_configuration.h"
 #include "chromeos/settings/cros_settings_names.h"
@@ -73,9 +73,8 @@
     device_native_printers_handler_ =
         std::make_unique<DeviceNativePrintersHandler>(&policy_service_);
     external_printers_ =
-        chromeos::BulkPrintersCalculatorFactory::Get()->GetForDevice();
-    external_printers_->SetAccessMode(
-        chromeos::BulkPrintersCalculator::ALL_ACCESS);
+        chromeos::DeviceExternalPrintersFactory::Get()->GetForDevice();
+    external_printers_->SetAccessMode(chromeos::ExternalPrinters::ALL_ACCESS);
   }
 
   void TearDown() override { device_native_printers_handler_->Shutdown(); }
@@ -84,7 +83,7 @@
   base::test::ScopedTaskEnvironment scoped_task_environment_;
   MockPolicyService policy_service_;
   std::unique_ptr<DeviceNativePrintersHandler> device_native_printers_handler_;
-  base::WeakPtr<chromeos::BulkPrintersCalculator> external_printers_;
+  base::WeakPtr<chromeos::ExternalPrinters> external_printers_;
 };
 
 TEST_F(DeviceNativePrintersHandlerTest, OnDataFetched) {
diff --git a/chrome/browser/chromeos/power/auto_screen_brightness/controller.cc b/chrome/browser/chromeos/power/auto_screen_brightness/controller.cc
index 3f10abca..574964f5 100644
--- a/chrome/browser/chromeos/power/auto_screen_brightness/controller.cc
+++ b/chrome/browser/chromeos/power/auto_screen_brightness/controller.cc
@@ -12,6 +12,7 @@
 #include "chrome/browser/chromeos/power/auto_screen_brightness/brightness_monitor_impl.h"
 #include "chrome/browser/chromeos/power/auto_screen_brightness/gaussian_trainer.h"
 #include "chrome/browser/chromeos/power/auto_screen_brightness/metrics_reporter.h"
+#include "chrome/browser/chromeos/power/auto_screen_brightness/model_config_loader_impl.h"
 #include "chrome/browser/chromeos/power/auto_screen_brightness/modeller_impl.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/profiles/profile_manager.h"
@@ -35,6 +36,8 @@
   brightness_monitor_ =
       std::make_unique<BrightnessMonitorImpl>(power_manager_client);
 
+  model_config_loader_ = std::make_unique<ModelConfigLoaderImpl>();
+
   ui::UserActivityDetector* user_activity_detector =
       ui::UserActivityDetector::Get();
   DCHECK(user_activity_detector);
@@ -43,7 +46,8 @@
   DCHECK(profile);
   modeller_ = std::make_unique<ModellerImpl>(
       profile, als_reader_.get(), brightness_monitor_.get(),
-      user_activity_detector, std::make_unique<GaussianTrainer>());
+      model_config_loader_.get(), user_activity_detector,
+      std::make_unique<GaussianTrainer>());
 
   adapter_ = std::make_unique<Adapter>(
       profile, als_reader_.get(), brightness_monitor_.get(), modeller_.get(),
diff --git a/chrome/browser/chromeos/power/auto_screen_brightness/controller.h b/chrome/browser/chromeos/power/auto_screen_brightness/controller.h
index 99125a3..23e933f 100644
--- a/chrome/browser/chromeos/power/auto_screen_brightness/controller.h
+++ b/chrome/browser/chromeos/power/auto_screen_brightness/controller.h
@@ -19,6 +19,7 @@
 class AlsReaderImpl;
 class BrightnessMonitorImpl;
 class MetricsReporter;
+class ModelConfigLoaderImpl;
 class ModellerImpl;
 
 // This controller class sets up and destroys all components needed for the auto
@@ -32,6 +33,7 @@
   std::unique_ptr<MetricsReporter> metrics_reporter_;
   std::unique_ptr<AlsReaderImpl> als_reader_;
   std::unique_ptr<BrightnessMonitorImpl> brightness_monitor_;
+  std::unique_ptr<ModelConfigLoaderImpl> model_config_loader_;
   std::unique_ptr<ModellerImpl> modeller_;
   std::unique_ptr<Adapter> adapter_;
 
diff --git a/chrome/browser/chromeos/power/auto_screen_brightness/fake_model_config_loader.cc b/chrome/browser/chromeos/power/auto_screen_brightness/fake_model_config_loader.cc
new file mode 100644
index 0000000..2fbdf3fa
--- /dev/null
+++ b/chrome/browser/chromeos/power/auto_screen_brightness/fake_model_config_loader.cc
@@ -0,0 +1,44 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/chromeos/power/auto_screen_brightness/fake_model_config_loader.h"
+
+namespace chromeos {
+namespace power {
+namespace auto_screen_brightness {
+
+FakeModelConfigLoader::FakeModelConfigLoader() : weak_ptr_factory_(this) {}
+
+FakeModelConfigLoader::~FakeModelConfigLoader() = default;
+
+void FakeModelConfigLoader::ReportModelConfigLoaded() {
+  DCHECK(is_initialized_);
+  for (auto& observer : observers_) {
+    NotifyObserver(&observer);
+  }
+}
+
+void FakeModelConfigLoader::AddObserver(Observer* const observer) {
+  DCHECK(observer);
+  observers_.AddObserver(observer);
+  if (is_initialized_) {
+    NotifyObserver(observer);
+  }
+}
+
+void FakeModelConfigLoader::RemoveObserver(Observer* const observer) {
+  DCHECK(observer);
+  observers_.RemoveObserver(observer);
+}
+
+void FakeModelConfigLoader::NotifyObserver(Observer* const observer) {
+  DCHECK(observer);
+  observer->OnModelConfigLoaded(is_model_config_valid_
+                                    ? base::Optional<ModelConfig>(model_config_)
+                                    : base::nullopt);
+}
+
+}  // namespace auto_screen_brightness
+}  // namespace power
+}  // namespace chromeos
diff --git a/chrome/browser/chromeos/power/auto_screen_brightness/fake_model_config_loader.h b/chrome/browser/chromeos/power/auto_screen_brightness/fake_model_config_loader.h
new file mode 100644
index 0000000..05fa698
--- /dev/null
+++ b/chrome/browser/chromeos/power/auto_screen_brightness/fake_model_config_loader.h
@@ -0,0 +1,56 @@
+// 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_CHROMEOS_POWER_AUTO_SCREEN_BRIGHTNESS_FAKE_MODEL_CONFIG_LOADER_H_
+#define CHROME_BROWSER_CHROMEOS_POWER_AUTO_SCREEN_BRIGHTNESS_FAKE_MODEL_CONFIG_LOADER_H_
+
+#include "base/memory/weak_ptr.h"
+#include "base/observer_list.h"
+#include "chrome/browser/chromeos/power/auto_screen_brightness/model_config_loader.h"
+
+namespace chromeos {
+namespace power {
+namespace auto_screen_brightness {
+
+// This is a fake ModelConfigLoader used for testing only. To use it, we need to
+// call its |set_model_config| first to set a specific model config, and then we
+// can call |ReportModelConfigLoaded| so that it will notify its observers the
+// set model config.
+class FakeModelConfigLoader : public ModelConfigLoader {
+ public:
+  FakeModelConfigLoader();
+  ~FakeModelConfigLoader() override;
+
+  void set_model_config(const ModelConfig& model_config) {
+    model_config_ = model_config;
+    is_model_config_valid_ = IsValidModelConfig(model_config_);
+    is_initialized_ = true;
+  }
+
+  // Notifies its observers the pre-specified model config.
+  void ReportModelConfigLoaded();
+
+  // ModelConfigLoader overrides:
+  void AddObserver(Observer* observer) override;
+  void RemoveObserver(Observer* observer) override;
+
+ private:
+  // Notifies |observer| the specified model config if it's valid. Otherwise the
+  // |observer| will receive a nullopt.
+  void NotifyObserver(Observer* observer);
+
+  bool is_initialized_ = false;
+  bool is_model_config_valid_ = false;
+  ModelConfig model_config_;
+  base::ObserverList<Observer> observers_;
+  base::WeakPtrFactory<FakeModelConfigLoader> weak_ptr_factory_;
+
+  DISALLOW_COPY_AND_ASSIGN(FakeModelConfigLoader);
+};
+
+}  // namespace auto_screen_brightness
+}  // namespace power
+}  // namespace chromeos
+
+#endif  // CHROME_BROWSER_CHROMEOS_POWER_AUTO_SCREEN_BRIGHTNESS_FAKE_MODEL_CONFIG_LOADER_H_
diff --git a/chrome/browser/chromeos/power/auto_screen_brightness/model_config.cc b/chrome/browser/chromeos/power/auto_screen_brightness/model_config.cc
index f0aa7e13..188565a 100644
--- a/chrome/browser/chromeos/power/auto_screen_brightness/model_config.cc
+++ b/chrome/browser/chromeos/power/auto_screen_brightness/model_config.cc
@@ -2,6 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+#include <cmath>
+
 #include "chrome/browser/chromeos/power/auto_screen_brightness/model_config.h"
 
 namespace chromeos {
@@ -21,6 +23,38 @@
 
 ModelConfig::~ModelConfig() = default;
 
+bool ModelConfig::operator==(const ModelConfig& config) const {
+  const double kTol = 1e-10;
+  if (std::abs(auto_brightness_als_horizon_seconds -
+               config.auto_brightness_als_horizon_seconds) >= kTol)
+    return false;
+
+  if (log_lux.size() != config.log_lux.size())
+    return false;
+
+  for (size_t i = 0; i < log_lux.size(); ++i) {
+    if (std::abs(log_lux[i] - config.log_lux[i]) >= kTol)
+      return false;
+  }
+
+  if (brightness.size() != config.brightness.size())
+    return false;
+
+  for (size_t i = 0; i < brightness.size(); ++i) {
+    if (std::abs(brightness[i] - config.brightness[i]) >= kTol)
+      return false;
+  }
+
+  if (metrics_key != config.metrics_key)
+    return false;
+
+  if (std::abs(model_als_horizon_seconds - config.model_als_horizon_seconds) >=
+      kTol)
+    return false;
+
+  return true;
+}
+
 bool IsValidModelConfig(const ModelConfig& model_config) {
   if (model_config.auto_brightness_als_horizon_seconds <= 0)
     return false;
diff --git a/chrome/browser/chromeos/power/auto_screen_brightness/model_config.h b/chrome/browser/chromeos/power/auto_screen_brightness/model_config.h
index bde3dab..5b475420 100644
--- a/chrome/browser/chromeos/power/auto_screen_brightness/model_config.h
+++ b/chrome/browser/chromeos/power/auto_screen_brightness/model_config.h
@@ -20,9 +20,12 @@
   std::vector<double> brightness;
   std::string metrics_key;
   double model_als_horizon_seconds = -1.0;
+
   ModelConfig();
   ModelConfig(const ModelConfig& config);
   ~ModelConfig();
+
+  bool operator==(const ModelConfig& config) const;
 };
 
 bool IsValidModelConfig(const ModelConfig& model_config);
diff --git a/chrome/browser/chromeos/power/auto_screen_brightness/model_config_loader_impl_unittest.cc b/chrome/browser/chromeos/power/auto_screen_brightness/model_config_loader_impl_unittest.cc
index 4a79004c..6fbb466 100644
--- a/chrome/browser/chromeos/power/auto_screen_brightness/model_config_loader_impl_unittest.cc
+++ b/chrome/browser/chromeos/power/auto_screen_brightness/model_config_loader_impl_unittest.cc
@@ -27,23 +27,6 @@
 
 namespace {
 
-void CheckModelConfig(const ModelConfig& result, const ModelConfig& expected) {
-  EXPECT_DOUBLE_EQ(result.auto_brightness_als_horizon_seconds,
-                   expected.auto_brightness_als_horizon_seconds);
-  EXPECT_EQ(result.log_lux.size(), expected.log_lux.size());
-  for (size_t i = 0; i < result.log_lux.size(); ++i) {
-    EXPECT_DOUBLE_EQ(result.log_lux[i], expected.log_lux[i]);
-  }
-  EXPECT_EQ(result.brightness.size(), expected.brightness.size());
-  for (size_t i = 0; i < result.brightness.size(); ++i) {
-    EXPECT_DOUBLE_EQ(result.brightness[i], expected.brightness[i]);
-  }
-
-  EXPECT_EQ(result.metrics_key, expected.metrics_key);
-  EXPECT_DOUBLE_EQ(result.model_als_horizon_seconds,
-                   expected.model_als_horizon_seconds);
-}
-
 class TestObserver : public ModelConfigLoader::Observer {
  public:
   TestObserver() {}
@@ -158,7 +141,7 @@
   expected_model_config.metrics_key = "abc";
   expected_model_config.model_als_horizon_seconds = 5;
   EXPECT_TRUE(test_observer_->model_config());
-  CheckModelConfig(*test_observer_->model_config(), expected_model_config);
+  EXPECT_EQ(*test_observer_->model_config(), expected_model_config);
 }
 
 TEST_F(ModelConfigLoaderImplTest, ValidModelParamsLoadedThenOverriden) {
@@ -202,7 +185,7 @@
   expected_model_config.metrics_key = "abc";
   expected_model_config.model_als_horizon_seconds = 20.0;
   EXPECT_TRUE(test_observer_->model_config());
-  CheckModelConfig(*test_observer_->model_config(), expected_model_config);
+  EXPECT_EQ(*test_observer_->model_config(), expected_model_config);
 }
 
 TEST_F(ModelConfigLoaderImplTest, InvalidModelParamsLoaded) {
@@ -269,7 +252,7 @@
   expected_model_config.metrics_key = "abc";
   expected_model_config.model_als_horizon_seconds = 20.0;
   EXPECT_TRUE(test_observer_->model_config());
-  CheckModelConfig(*test_observer_->model_config(), expected_model_config);
+  EXPECT_EQ(*test_observer_->model_config(), expected_model_config);
 }
 
 TEST_F(ModelConfigLoaderImplTest, MissingModelParams) {
diff --git a/chrome/browser/chromeos/power/auto_screen_brightness/modeller_impl.cc b/chrome/browser/chromeos/power/auto_screen_brightness/modeller_impl.cc
index a7c2d37..2f20764 100644
--- a/chrome/browser/chromeos/power/auto_screen_brightness/modeller_impl.cc
+++ b/chrome/browser/chromeos/power/auto_screen_brightness/modeller_impl.cc
@@ -31,31 +31,6 @@
 
 namespace {
 
-// Creates a global/default brightness curve.
-// TODO(crbug.com/881215): default curve may be revised, if so, need to update
-// unit tests as well.
-MonotoneCubicSpline CreateGlobalCurve() {
-  const std::string global_curve = GetFieldTrialParamValueByFeature(
-      features::kAutoScreenBrightness, "global_curve");
-  if (!global_curve.empty()) {
-    const base::Optional<MonotoneCubicSpline> global_curve_spline =
-        MonotoneCubicSpline::FromString(global_curve);
-    if (global_curve_spline)
-      return *global_curve_spline;
-    // TODO(jiameng): log error to UMA.
-  }
-
-  const std::vector<double> default_log_lux = {
-      3.69, 4.83, 6.54, 7.68, 8.25, 8.82,
-  };
-
-  const std::vector<double> default_brightness = {
-      36.14, 47.62, 85.83, 93.27, 93.27, 100,
-  };
-
-  return MonotoneCubicSpline(default_log_lux, default_brightness);
-}
-
 // Loads curve from a specified location on disk. This should run in another
 // thread to be non-blocking to the main thread (if |is_testing| is false).
 // The ambient values read from disk should be in the log-domain already.
@@ -130,11 +105,13 @@
 ModellerImpl::ModellerImpl(const Profile* profile,
                            AlsReader* als_reader,
                            BrightnessMonitor* brightness_monitor,
+                           ModelConfigLoader* model_config_loader,
                            ui::UserActivityDetector* user_activity_detector,
                            std::unique_ptr<Trainer> trainer)
     : ModellerImpl(profile,
                    als_reader,
                    brightness_monitor,
+                   model_config_loader,
                    user_activity_detector,
                    std::move(trainer),
                    base::CreateSequencedTaskRunnerWithTraits(
@@ -166,8 +143,7 @@
   if (is_modeller_enabled_.has_value() && !*is_modeller_enabled_)
     return;
 
-  ambient_light_values_->SaveToBuffer(
-      {average_log_als_ ? ConvertToLog(lux) : lux, tick_clock_->NowTicks()});
+  ambient_light_values_->SaveToBuffer({lux, tick_clock_->NowTicks()});
 }
 
 void ModellerImpl::OnAlsReaderInitialized(AlsReader::AlsInitStatus status) {
@@ -202,15 +178,26 @@
 
   const double average_ambient_lux = average_ambient_lux_opt.value();
   data_cache_.push_back({old_brightness_percent, new_brightness_percent,
-                         average_log_als_ ? average_ambient_lux
-                                          : ConvertToLog(average_ambient_lux),
-                         now});
+                         ConvertToLog(average_ambient_lux), now});
 
   ScheduleTrainerStart();
 }
 
 void ModellerImpl::OnUserBrightnessChangeRequested() {}
 
+void ModellerImpl::OnModelConfigLoaded(
+    base::Optional<ModelConfig> model_config) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  DCHECK(!model_config_exists_.has_value());
+
+  model_config_exists_ = model_config.has_value();
+  if (model_config_exists_.value()) {
+    model_config_ = model_config.value();
+  }
+
+  HandleStatusUpdate();
+}
+
 void ModellerImpl::OnUserActivity(const ui::Event* event) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   if (!event)
@@ -222,14 +209,15 @@
     const Profile* profile,
     AlsReader* als_reader,
     BrightnessMonitor* brightness_monitor,
+    ModelConfigLoader* model_config_loader,
     ui::UserActivityDetector* user_activity_detector,
     std::unique_ptr<Trainer> trainer,
     scoped_refptr<base::SequencedTaskRunner> blocking_task_runner,
     const base::TickClock* tick_clock) {
   return base::WrapUnique(new ModellerImpl(
-      profile, als_reader, brightness_monitor, user_activity_detector,
-      std::move(trainer), blocking_task_runner, tick_clock,
-      true /* is_testing */));
+      profile, als_reader, brightness_monitor, model_config_loader,
+      user_activity_detector, std::move(trainer), blocking_task_runner,
+      tick_clock, true /* is_testing */));
 }
 
 base::Optional<double> ModellerImpl::AverageAmbientForTesting(
@@ -245,7 +233,8 @@
 
 MonotoneCubicSpline ModellerImpl::GetGlobalCurveForTesting() const {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  return global_curve_;
+  DCHECK(global_curve_);
+  return *global_curve_;
 }
 
 size_t ModellerImpl::GetMaxTrainingDataPointsForTesting() const {
@@ -256,6 +245,10 @@
   return training_delay_;
 }
 
+ModelConfig ModellerImpl::GetModelConfigForTesting() const {
+  return model_config_;
+}
+
 base::FilePath ModellerImpl::GetCurvePathFromProfile(const Profile* profile) {
   DCHECK(profile);
   const base::FilePath empty_path;
@@ -277,6 +270,7 @@
     const Profile* profile,
     AlsReader* als_reader,
     BrightnessMonitor* brightness_monitor,
+    ModelConfigLoader* model_config_loader,
     ui::UserActivityDetector* user_activity_detector,
     std::unique_ptr<Trainer> trainer,
     const scoped_refptr<base::SequencedTaskRunner> blocking_task_runner,
@@ -285,16 +279,18 @@
     : is_testing_(is_testing),
       als_reader_observer_(this),
       brightness_monitor_observer_(this),
+      model_config_loader_observer_(this),
       user_activity_observer_(this),
       blocking_task_runner_(blocking_task_runner),
       trainer_(trainer.release(),
                base::OnTaskRunnerDeleter(blocking_task_runner_)),
       tick_clock_(tick_clock),
       model_timer_(tick_clock_),
-      global_curve_(CreateGlobalCurve()),
       weak_ptr_factory_(this) {
   DCHECK(als_reader);
   DCHECK(brightness_monitor);
+  DCHECK(model_config_loader);
+
   DCHECK(trainer_);
   DCHECK(user_activity_detector);
 
@@ -314,36 +310,11 @@
     return;
   }
 
-  // Default is to average over past 10 seconds.
-  const int model_als_horizon_seconds = GetFieldTrialParamByFeatureAsInt(
-      features::kAutoScreenBrightness, "model_als_horizon_seconds", 10);
-
-  if (model_als_horizon_seconds <= 0) {
-    is_modeller_enabled_ = false;
-    return;
-  }
-  ambient_light_values_ = std::make_unique<AmbientLightSampleBuffer>(
-      base::TimeDelta::FromSeconds(model_als_horizon_seconds));
-
-  average_log_als_ = GetFieldTrialParamByFeatureAsBool(
-      features::kAutoScreenBrightness, "average_log_als", average_log_als_);
-
   als_reader_observer_.Add(als_reader);
   brightness_monitor_observer_.Add(brightness_monitor);
+  model_config_loader_observer_.Add(model_config_loader);
+
   user_activity_observer_.Add(user_activity_detector);
-
-  const int max_training_data_points = GetFieldTrialParamByFeatureAsInt(
-      features::kAutoScreenBrightness, "max_training_data_points", -1);
-  if (max_training_data_points > 0) {
-    max_training_data_points_ = max_training_data_points;
-  }
-
-  const int training_delay_in_seconds = GetFieldTrialParamByFeatureAsInt(
-      features::kAutoScreenBrightness, "training_delay_in_seconds",
-      training_delay_.InSeconds());
-  if (training_delay_in_seconds >= 0) {
-    training_delay_ = base::TimeDelta::FromSeconds(training_delay_in_seconds);
-  }
 }
 
 void ModellerImpl::HandleStatusUpdate() {
@@ -371,6 +342,17 @@
     return;
   }
 
+  if (!model_config_exists_.has_value())
+    return;
+
+  if (!model_config_exists_.value()) {
+    is_modeller_enabled_ = false;
+    OnInitializationComplete();
+    return;
+  }
+
+  RunCustomization();
+
   base::PostTaskAndReplyWithResult(
       blocking_task_runner_.get(), FROM_HERE,
       base::BindOnce(&LoadCurveFromDisk, curve_path_, is_testing_),
@@ -378,6 +360,30 @@
                      weak_ptr_factory_.GetWeakPtr()));
 }
 
+void ModellerImpl::RunCustomization() {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  global_curve_.emplace(
+      MonotoneCubicSpline(model_config_.log_lux, model_config_.brightness));
+
+  ambient_light_values_ = std::make_unique<AmbientLightSampleBuffer>(
+      base::TimeDelta::FromSeconds(model_config_.model_als_horizon_seconds));
+
+  // TODO(jiameng): the following params are probably not useful and can be
+  // removed.
+  const int max_training_data_points = GetFieldTrialParamByFeatureAsInt(
+      features::kAutoScreenBrightness, "max_training_data_points", -1);
+  if (max_training_data_points > 0) {
+    max_training_data_points_ = max_training_data_points;
+  }
+
+  const int training_delay_in_seconds = GetFieldTrialParamByFeatureAsInt(
+      features::kAutoScreenBrightness, "training_delay_in_seconds",
+      training_delay_.InSeconds());
+  if (training_delay_in_seconds >= 0) {
+    training_delay_ = base::TimeDelta::FromSeconds(training_delay_in_seconds);
+  }
+}
+
 void ModellerImpl::OnInitializationComplete() {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   // TODO(jiameng): log model status to UMA.
@@ -403,18 +409,19 @@
 void ModellerImpl::OnCurveLoadedFromDisk(
     const base::Optional<MonotoneCubicSpline>& curve) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  DCHECK(global_curve_);
 
   if (curve) {
     current_curve_.emplace(curve.value());
   } else {
-    current_curve_.emplace(global_curve_);
+    current_curve_.emplace(*global_curve_);
   }
 
   // Run SetInitialCurves calculations on background thread to avoid blocking UI
   // thread.
   base::PostTaskAndReplyWithResult(
       blocking_task_runner_.get(), FROM_HERE,
-      base::BindOnce(&SetInitialCurves, trainer_.get(), global_curve_,
+      base::BindOnce(&SetInitialCurves, trainer_.get(), *global_curve_,
                      *current_curve_, is_testing_),
       base::BindOnce(&ModellerImpl::OnSetInitialCurves,
                      weak_ptr_factory_.GetWeakPtr(), curve));
@@ -441,9 +448,10 @@
                         is_personal_curve_valid);
 
   has_initial_personal_curve_ = is_personal_curve_valid && loaded_curve;
-  DCHECK(trainer_->GetGlobalCurve() == global_curve_);
+  DCHECK(global_curve_);
+  DCHECK(trainer_->GetGlobalCurve() == *global_curve_);
   DCHECK(trainer_->GetCurrentCurve() ==
-         (has_initial_personal_curve_ ? *loaded_curve : global_curve_));
+         (has_initial_personal_curve_ ? *loaded_curve : *global_curve_));
 
   is_modeller_enabled_ = true;
   OnInitializationComplete();
diff --git a/chrome/browser/chromeos/power/auto_screen_brightness/modeller_impl.h b/chrome/browser/chromeos/power/auto_screen_brightness/modeller_impl.h
index 22ca1fe..060e1a3 100644
--- a/chrome/browser/chromeos/power/auto_screen_brightness/modeller_impl.h
+++ b/chrome/browser/chromeos/power/auto_screen_brightness/modeller_impl.h
@@ -20,6 +20,8 @@
 #include "chrome/browser/chromeos/power/auto_screen_brightness/als_reader.h"
 #include "chrome/browser/chromeos/power/auto_screen_brightness/als_samples.h"
 #include "chrome/browser/chromeos/power/auto_screen_brightness/brightness_monitor.h"
+#include "chrome/browser/chromeos/power/auto_screen_brightness/model_config.h"
+#include "chrome/browser/chromeos/power/auto_screen_brightness/model_config_loader.h"
 #include "chrome/browser/chromeos/power/auto_screen_brightness/modeller.h"
 #include "chrome/browser/chromeos/power/auto_screen_brightness/trainer.h"
 #include "chrome/browser/chromeos/power/auto_screen_brightness/utils.h"
@@ -40,6 +42,7 @@
 class ModellerImpl : public Modeller,
                      public AlsReader::Observer,
                      public BrightnessMonitor::Observer,
+                     public ModelConfigLoader::Observer,
                      public ui::UserActivityObserver {
  public:
   static constexpr char kModelDir[] = "autobrightness";
@@ -49,6 +52,7 @@
   ModellerImpl(const Profile* profile,
                AlsReader* als_reader,
                BrightnessMonitor* brightness_monitor,
+               ModelConfigLoader* model_config_loader,
                ui::UserActivityDetector* user_activity_detector,
                std::unique_ptr<Trainer> trainer);
   ~ModellerImpl() override;
@@ -67,6 +71,9 @@
                                double new_brightness_percent) override;
   void OnUserBrightnessChangeRequested() override;
 
+  // ModelConfigLoader::Observer overrides:
+  void OnModelConfigLoaded(base::Optional<ModelConfig> model_config) override;
+
   // ui::UserActivityObserver overrides:
   void OnUserActivity(const ui::Event* event) override;
 
@@ -75,6 +82,7 @@
       const Profile* profile,
       AlsReader* als_reader,
       BrightnessMonitor* brightness_monitor,
+      ModelConfigLoader* model_config_loader,
       ui::UserActivityDetector* user_activity_detector,
       std::unique_ptr<Trainer> trainer,
       scoped_refptr<base::SequencedTaskRunner> blocking_task_runner,
@@ -95,6 +103,8 @@
 
   base::TimeDelta GetTrainingDelayForTesting() const;
 
+  ModelConfig GetModelConfigForTesting() const;
+
   // Returns the path that will be used to store curves. It also creates
   // intermediate directories if they do not exist. Returns an empty path on
   // failures.
@@ -105,6 +115,7 @@
   ModellerImpl(const Profile* profile,
                AlsReader* als_reader,
                BrightnessMonitor* brightness_monitor,
+               ModelConfigLoader* model_config_loader,
                ui::UserActivityDetector* user_activity_detector,
                std::unique_ptr<Trainer> trainer,
                scoped_refptr<base::SequencedTaskRunner> task_runner,
@@ -123,6 +134,9 @@
   // notified about the status and the curve.
   void HandleStatusUpdate();
 
+  // Load customizations from model configs.
+  void RunCustomization();
+
   // Notifies its observers on the status of the model. It will be called either
   // when HandleStatusUpdate is called and |model_status_| is no longer
   // |kInitializing|, or when an observer is added to the modeller, and
@@ -184,6 +198,9 @@
   ScopedObserver<BrightnessMonitor, BrightnessMonitor::Observer>
       brightness_monitor_observer_;
 
+  ScopedObserver<ModelConfigLoader, ModelConfigLoader::Observer>
+      model_config_loader_observer_;
+
   ScopedObserver<ui::UserActivityDetector, ui::UserActivityObserver>
       user_activity_observer_;
 
@@ -200,6 +217,12 @@
   base::Optional<AlsReader::AlsInitStatus> als_init_status_;
   base::Optional<bool> brightness_monitor_success_;
 
+  // |model_config_exists_| will remain nullopt until |OnModelConfigLoaded| is
+  // called. Its value will then be set to true if the input model config exists
+  // (not nullopt), else its value will be false.
+  base::Optional<bool> model_config_exists_;
+  ModelConfig model_config_;
+
   // Whether this modeller has initialized successfully, including connecting
   // to AlsReader, BrightnessMonitor and loading a Trainer.
   // Initially has no value. Guaranteed to have a value after the completion of
@@ -212,8 +235,11 @@
   // Trainer and Trainer reported it was valid.
   bool has_initial_personal_curve_ = false;
 
-  // Global curve constructed from predefined params.
-  const MonotoneCubicSpline global_curve_;
+  // Global curve constructed from predefined params. It will remain nullopt
+  // until |OnModelConfigLoaded| is called. If input model config is nullopt
+  // then |global_curve_| will remain nullopt, else it will be created based on
+  // the model config.
+  base::Optional<MonotoneCubicSpline> global_curve_;
 
   // Current personal curve. Initially it could be either the global curve or
   // loaded curve. After training, it will be updated each time trainer
@@ -223,10 +249,6 @@
   // Recent ambient values.
   std::unique_ptr<AmbientLightSampleBuffer> ambient_light_values_;
 
-  // Whether we calculate average log ALS values. This should be the same as
-  // that used by the adapter.
-  bool average_log_als_ = false;
-
   std::vector<TrainingDataPoint> data_cache_;
 
   base::ObserverList<Modeller::Observer> observers_;
diff --git a/chrome/browser/chromeos/power/auto_screen_brightness/modeller_impl_unittest.cc b/chrome/browser/chromeos/power/auto_screen_brightness/modeller_impl_unittest.cc
index 9c7f171..56ba206 100644
--- a/chrome/browser/chromeos/power/auto_screen_brightness/modeller_impl_unittest.cc
+++ b/chrome/browser/chromeos/power/auto_screen_brightness/modeller_impl_unittest.cc
@@ -16,6 +16,7 @@
 #include "base/threading/sequenced_task_runner_handle.h"
 #include "chrome/browser/chromeos/power/auto_screen_brightness/fake_als_reader.h"
 #include "chrome/browser/chromeos/power/auto_screen_brightness/fake_brightness_monitor.h"
+#include "chrome/browser/chromeos/power/auto_screen_brightness/fake_model_config_loader.h"
 #include "chrome/browser/chromeos/power/auto_screen_brightness/monotone_cubic_spline.h"
 #include "chrome/browser/chromeos/power/auto_screen_brightness/trainer.h"
 #include "chrome/browser/chromeos/power/auto_screen_brightness/utils.h"
@@ -192,7 +193,7 @@
   void SetUpModeller(bool is_trainer_configured, bool is_personal_curve_valid) {
     modeller_ = ModellerImpl::CreateForTesting(
         profile_.get(), &fake_als_reader_, &fake_brightness_monitor_,
-        &user_activity_detector_,
+        &fake_model_config_loader_, &user_activity_detector_,
         std::make_unique<FakeTrainer>(is_trainer_configured,
                                       is_personal_curve_valid),
         base::SequencedTaskRunnerHandle::Get(),
@@ -204,17 +205,21 @@
 
   void Init(AlsReader::AlsInitStatus als_reader_status,
             BrightnessMonitor::Status brightness_monitor_status,
+            base::Optional<ModelConfig> model_config,
             bool is_trainer_configured = true,
             bool is_personal_curve_valid = true,
             const std::map<std::string, std::string>& params = {}) {
-    base::test::ScopedFeatureList scoped_feature_list;
     if (!params.empty()) {
-      scoped_feature_list.InitAndEnableFeatureWithParameters(
+      scoped_feature_list_.InitAndEnableFeatureWithParameters(
           features::kAutoScreenBrightness, params);
     }
 
     fake_als_reader_.set_als_init_status(als_reader_status);
     fake_brightness_monitor_.set_status(brightness_monitor_status);
+    if (model_config) {
+      fake_model_config_loader_.set_model_config(model_config.value());
+    }
+
     SetUpModeller(is_trainer_configured, is_personal_curve_valid);
     thread_bundle_.RunUntilIdle();
   }
@@ -233,6 +238,22 @@
         << " to " << curve_path;
   }
 
+  // Returns a valid ModelConfig.
+  ModelConfig GetTestModelConfig() {
+    ModelConfig model_config;
+    model_config.auto_brightness_als_horizon_seconds = 2.0;
+    model_config.log_lux = {
+        3.69, 4.83, 6.54, 7.68, 8.25, 8.82,
+    };
+    model_config.brightness = {
+        36.14, 47.62, 85.83, 93.27, 93.27, 100,
+    };
+
+    model_config.metrics_key = "abc";
+    model_config.model_als_horizon_seconds = 3.0;
+    return model_config;
+  }
+
   content::TestBrowserThreadBundle thread_bundle_;
   base::HistogramTester histogram_tester_;
 
@@ -243,9 +264,11 @@
 
   FakeAlsReader fake_als_reader_;
   FakeBrightnessMonitor fake_brightness_monitor_;
+  FakeModelConfigLoader fake_model_config_loader_;
 
   std::unique_ptr<ModellerImpl> modeller_;
   std::unique_ptr<TestObserver> test_observer_;
+  base::test::ScopedFeatureList scoped_feature_list_;
 
  private:
   DISALLOW_COPY_AND_ASSIGN(ModellerImplTest);
@@ -253,8 +276,8 @@
 
 // AlsReader is |kDisabled| when Modeller is created.
 TEST_F(ModellerImplTest, AlsReaderDisabledOnInit) {
-  Init(AlsReader::AlsInitStatus::kDisabled,
-       BrightnessMonitor::Status::kSuccess);
+  Init(AlsReader::AlsInitStatus::kDisabled, BrightnessMonitor::Status::kSuccess,
+       GetTestModelConfig());
 
   test_observer_->CheckStatus(true /* is_model_initialized */,
                               base::nullopt /* global_curve */,
@@ -263,8 +286,18 @@
 
 // BrightnessMonitor is |kDisabled| when Modeller is created.
 TEST_F(ModellerImplTest, BrightnessMonitorDisabledOnInit) {
-  Init(AlsReader::AlsInitStatus::kSuccess,
-       BrightnessMonitor::Status::kDisabled);
+  Init(AlsReader::AlsInitStatus::kSuccess, BrightnessMonitor::Status::kDisabled,
+       GetTestModelConfig());
+
+  test_observer_->CheckStatus(true /* is_model_initialized */,
+                              base::nullopt /* global_curve */,
+                              base::nullopt /* personal_curve */);
+}
+
+// ModelConfigLoader has an invalid config, hence Modeller is disabled.
+TEST_F(ModellerImplTest, ModelConfigLoaderDisabledOnInit) {
+  Init(AlsReader::AlsInitStatus::kSuccess, BrightnessMonitor::Status::kSuccess,
+       ModelConfig());
 
   test_observer_->CheckStatus(true /* is_model_initialized */,
                               base::nullopt /* global_curve */,
@@ -274,7 +307,7 @@
 // AlsReader is |kDisabled| on later notification.
 TEST_F(ModellerImplTest, AlsReaderDisabledOnNotification) {
   Init(AlsReader::AlsInitStatus::kInProgress,
-       BrightnessMonitor::Status::kSuccess);
+       BrightnessMonitor::Status::kSuccess, GetTestModelConfig());
 
   test_observer_->CheckStatus(false /* is_model_initialized */,
                               base::nullopt /* global_curve */,
@@ -292,7 +325,7 @@
 // AlsReader is |kSuccess| on later notification.
 TEST_F(ModellerImplTest, AlsReaderEnabledOnNotification) {
   Init(AlsReader::AlsInitStatus::kInProgress,
-       BrightnessMonitor::Status::kSuccess);
+       BrightnessMonitor::Status::kSuccess, GetTestModelConfig());
 
   test_observer_->CheckStatus(false /* is_model_initialized */,
                               base::nullopt /* global_curve */,
@@ -310,7 +343,7 @@
 // BrightnessMonitor is |kDisabled| on later notification.
 TEST_F(ModellerImplTest, BrightnessMonitorDisabledOnNotification) {
   Init(AlsReader::AlsInitStatus::kSuccess,
-       BrightnessMonitor::Status::kInitializing);
+       BrightnessMonitor::Status::kInitializing, GetTestModelConfig());
 
   test_observer_->CheckStatus(false /* is_model_initialized */,
                               base::nullopt /* global_curve */,
@@ -327,7 +360,7 @@
 // BrightnessMonitor is |kSuccess| on later notification.
 TEST_F(ModellerImplTest, BrightnessMonitorEnabledOnNotification) {
   Init(AlsReader::AlsInitStatus::kSuccess,
-       BrightnessMonitor::Status::kInitializing);
+       BrightnessMonitor::Status::kInitializing, GetTestModelConfig());
 
   test_observer_->CheckStatus(false /* is_model_initialized */,
                               base::nullopt /* global_curve */,
@@ -342,9 +375,46 @@
                               base::nullopt /* personal_curve */);
 }
 
+// ModelConfigLoader reports an invalid config on later notification.
+TEST_F(ModellerImplTest, InvalidModelConfigOnNotification) {
+  Init(AlsReader::AlsInitStatus::kSuccess, BrightnessMonitor::Status::kSuccess,
+       base::nullopt /* model_config */);
+
+  test_observer_->CheckStatus(false /* is_model_initialized */,
+                              base::nullopt /* global_curve */,
+                              base::nullopt /* personal_curve */);
+
+  // ModelConfig() creates an invalid config.
+  DCHECK(!IsValidModelConfig(ModelConfig()));
+  fake_model_config_loader_.set_model_config(ModelConfig());
+  fake_model_config_loader_.ReportModelConfigLoaded();
+  thread_bundle_.RunUntilIdle();
+  test_observer_->CheckStatus(true /* is_model_initialized */,
+                              base::nullopt /* global_curve */,
+                              base::nullopt /* personal_curve */);
+}
+
+// ModelConfigLoader reports a valid config on later notification.
+TEST_F(ModellerImplTest, ValidModelConfigOnNotification) {
+  Init(AlsReader::AlsInitStatus::kSuccess, BrightnessMonitor::Status::kSuccess,
+       base::nullopt /* model_config */);
+
+  test_observer_->CheckStatus(false /* is_model_initialized */,
+                              base::nullopt /* global_curve */,
+                              base::nullopt /* personal_curve */);
+
+  fake_model_config_loader_.set_model_config(GetTestModelConfig());
+  fake_model_config_loader_.ReportModelConfigLoaded();
+  thread_bundle_.RunUntilIdle();
+
+  test_observer_->CheckStatus(true /* is_model_initialized */,
+                              modeller_->GetGlobalCurveForTesting(),
+                              base::nullopt /* personal_curve */);
+}
 // There is no saved curve, hence a global curve is created.
 TEST_F(ModellerImplTest, NoSavedCurve) {
-  Init(AlsReader::AlsInitStatus::kSuccess, BrightnessMonitor::Status::kSuccess);
+  Init(AlsReader::AlsInitStatus::kSuccess, BrightnessMonitor::Status::kSuccess,
+       GetTestModelConfig());
 
   test_observer_->CheckStatus(true /* is_model_initialized */,
                               modeller_->GetGlobalCurveForTesting(),
@@ -361,7 +431,8 @@
 
   thread_bundle_.RunUntilIdle();
 
-  Init(AlsReader::AlsInitStatus::kSuccess, BrightnessMonitor::Status::kSuccess);
+  Init(AlsReader::AlsInitStatus::kSuccess, BrightnessMonitor::Status::kSuccess,
+       GetTestModelConfig());
 
   test_observer_->CheckStatus(true /* is_model_initialized */,
                               modeller_->GetGlobalCurveForTesting(), curve);
@@ -382,7 +453,8 @@
   thread_bundle_.RunUntilIdle();
 
   Init(AlsReader::AlsInitStatus::kSuccess, BrightnessMonitor::Status::kSuccess,
-       true /* is_trainer_configured */, false /* is_personal_curve_valid */);
+       GetTestModelConfig(), true /* is_trainer_configured */,
+       false /* is_personal_curve_valid */);
 
   test_observer_->CheckStatus(true /* is_model_initialized */,
                               modeller_->GetGlobalCurveForTesting(),
@@ -395,16 +467,18 @@
 // Ambient light values are received. We check average ambient light has been
 // calculated from the recent samples only.
 TEST_F(ModellerImplTest, OnAmbientLightUpdated) {
-  const int horizon_in_seconds = 5;
+  const ModelConfig model_config = GetTestModelConfig();
   Init(AlsReader::AlsInitStatus::kSuccess, BrightnessMonitor::Status::kSuccess,
-       true /* is_trainer_configured */, true /* is_personal_curve_valid */,
-       {{"model_als_horizon_seconds",
-         base::NumberToString(horizon_in_seconds)}});
+       model_config, true /* is_trainer_configured */,
+       true /* is_personal_curve_valid */);
 
   test_observer_->CheckStatus(true /* is_model_initialized */,
                               modeller_->GetGlobalCurveForTesting(),
                               base::nullopt /* personal_curve */);
 
+  EXPECT_EQ(modeller_->GetModelConfigForTesting(), model_config);
+  const int horizon_in_seconds = model_config.model_als_horizon_seconds;
+
   const int first_lux = 1000;
   double running_sum = 0.0;
   for (int i = 0; i < horizon_in_seconds; ++i) {
@@ -424,46 +498,12 @@
             running_sum / horizon_in_seconds);
 }
 
-// Same as OnAmbientLightUpdated except we calculate average of log ALS.
-TEST_F(ModellerImplTest, AverageLogAmbient) {
-  const int horizon_in_seconds = 5;
-  Init(AlsReader::AlsInitStatus::kSuccess, BrightnessMonitor::Status::kSuccess,
-       true /* is_trainer_configured */, true /* is_personal_curve_valid */,
-       {
-           {"model_als_horizon_seconds",
-            base::NumberToString(horizon_in_seconds)},
-           {"average_log_als", "true"},
-       });
-
-  test_observer_->CheckStatus(true /* is_model_initialized */,
-                              modeller_->GetGlobalCurveForTesting(),
-                              base::nullopt /* personal_curve */);
-
-  const int first_lux = 1000;
-  double running_sum = 0.0;
-  for (int i = 0; i < horizon_in_seconds; ++i) {
-    thread_bundle_.FastForwardBy(base::TimeDelta::FromSeconds(1));
-    const int lux = i == 0 ? first_lux : i;
-    fake_als_reader_.ReportAmbientLightUpdate(lux);
-    running_sum += ConvertToLog(lux);
-    EXPECT_EQ(modeller_->AverageAmbientForTesting(thread_bundle_.NowTicks()),
-              running_sum / (i + 1));
-  }
-
-  // Add another one should push the oldest |first_lux| out of the horizon.
-  thread_bundle_.FastForwardBy(base::TimeDelta::FromSeconds(1));
-  fake_als_reader_.ReportAmbientLightUpdate(100);
-  running_sum = running_sum + ConvertToLog(100) - ConvertToLog(first_lux);
-  EXPECT_DOUBLE_EQ(
-      modeller_->AverageAmbientForTesting(thread_bundle_.NowTicks()).value(),
-      running_sum / horizon_in_seconds);
-}
-
 // User brightness changes are received, training example cache reaches
 // |max_training_data_points_| to trigger early training. This all happens
 // within a small window shorter than |training_delay_|.
 TEST_F(ModellerImplTest, OnUserBrightnessChanged) {
-  Init(AlsReader::AlsInitStatus::kSuccess, BrightnessMonitor::Status::kSuccess);
+  Init(AlsReader::AlsInitStatus::kSuccess, BrightnessMonitor::Status::kSuccess,
+       GetTestModelConfig());
 
   test_observer_->CheckStatus(true /* is_model_initialized */,
                               modeller_->GetGlobalCurveForTesting(),
@@ -514,7 +554,8 @@
 
 // User activities resets timer used to start training.
 TEST_F(ModellerImplTest, MultipleUserActivities) {
-  Init(AlsReader::AlsInitStatus::kSuccess, BrightnessMonitor::Status::kSuccess);
+  Init(AlsReader::AlsInitStatus::kSuccess, BrightnessMonitor::Status::kSuccess,
+       GetTestModelConfig());
 
   test_observer_->CheckStatus(true /* is_model_initialized */,
                               modeller_->GetGlobalCurveForTesting(),
@@ -568,50 +609,11 @@
   EXPECT_EQ(expected_curve, *result_curve);
 }
 
-// Global curve specified by valid experiment parameter.
-TEST_F(ModellerImplTest, GlobaCurveFromValidExperimentParam) {
-  const std::string global_curve_spec("1,10\n2,20\n3,30");
-
-  const MonotoneCubicSpline expected_global_curve({1, 2, 3}, {10, 20, 30});
-
-  Init(AlsReader::AlsInitStatus::kSuccess, BrightnessMonitor::Status::kSuccess,
-       true /* is_trainer_configured */, true /* is_personal_curve_valid  */,
-       {{"global_curve", global_curve_spec}});
-  EXPECT_EQ(modeller_->GetGlobalCurveForTesting(), expected_global_curve);
-
-  test_observer_->CheckStatus(true /* is_model_initialized */,
-                              expected_global_curve,
-                              base::nullopt /* personal_curve */);
-}
-
-// Global curve specified by invalid experiment parameter leads to default curve
-// instead.
-TEST_F(ModellerImplTest, GlobaCurveFromInvalidExperimentParam) {
-  const std::string global_curve_spec("1,10");
-
-  // Defined by default values.
-  const MonotoneCubicSpline expected_global_curve(
-      {
-          3.69, 4.83, 6.54, 7.68, 8.25, 8.82,
-      },
-      {
-          36.14, 47.62, 85.83, 93.27, 93.27, 100,
-      });
-
-  Init(AlsReader::AlsInitStatus::kSuccess, BrightnessMonitor::Status::kSuccess,
-       true /* is_trainer_configured */, true /* is_personal_curve_valid  */,
-       {{"global_curve", global_curve_spec}});
-  EXPECT_EQ(modeller_->GetGlobalCurveForTesting(), expected_global_curve);
-
-  test_observer_->CheckStatus(true /* is_model_initialized */,
-                              expected_global_curve,
-                              base::nullopt /* personal_curve */);
-}
-
 // Training delay is 0, hence we train as soon as we have 1 data point.
 TEST_F(ModellerImplTest, ZeroTrainingDelay) {
   Init(AlsReader::AlsInitStatus::kSuccess, BrightnessMonitor::Status::kSuccess,
-       true /* is_trainer_configured */, true /* is_personal_curve_valid  */,
+       GetTestModelConfig(), true /* is_trainer_configured */,
+       true /* is_personal_curve_valid  */,
        {
            {"training_delay_in_seconds", "0"},
        });
diff --git a/chrome/browser/chromeos/power/ml/adaptive_screen_brightness_manager_unittest.cc b/chrome/browser/chromeos/power/ml/adaptive_screen_brightness_manager_unittest.cc
index 4e72070..cf08c923b 100644
--- a/chrome/browser/chromeos/power/ml/adaptive_screen_brightness_manager_unittest.cc
+++ b/chrome/browser/chromeos/power/ml/adaptive_screen_brightness_manager_unittest.cc
@@ -680,7 +680,8 @@
   tab_strip_model3->CloseAllTabs();
 }
 
-TEST_F(AdaptiveScreenBrightnessManagerTest, MultipleBrowsersNoneActive) {
+TEST_F(AdaptiveScreenBrightnessManagerTest,
+       DISABLED_MultipleBrowsersNoneActive) {
   // Simulates three browsers, none of which are active.
   //  - browser1 is the last active but minimized and so not visible.
   //  - browser2 and browser3 are both visible but not focused so not active.
diff --git a/chrome/browser/chromeos/printing/bulk_printers_calculator.cc b/chrome/browser/chromeos/printing/bulk_printers_calculator.cc
deleted file mode 100644
index 95b7b26..0000000
--- a/chrome/browser/chromeos/printing/bulk_printers_calculator.cc
+++ /dev/null
@@ -1,382 +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/chromeos/printing/bulk_printers_calculator.h"
-
-#include <set>
-
-#include "base/bind.h"
-#include "base/feature_list.h"
-#include "base/json/json_reader.h"
-#include "base/memory/weak_ptr.h"
-#include "base/observer_list.h"
-#include "base/sequence_checker.h"
-#include "base/sequenced_task_runner.h"
-#include "base/stl_util.h"
-#include "base/task/post_task.h"
-#include "base/task_runner_util.h"
-#include "base/threading/scoped_blocking_call.h"
-#include "base/values.h"
-#include "chrome/common/chrome_features.h"
-#include "chromeos/printing/printer_translator.h"
-
-namespace chromeos {
-
-namespace {
-
-constexpr int kMaxRecords = 20000;
-
-// Represents a task scheduled to process in the Restrictions class.
-struct TaskDataInternal {
-  const unsigned task_id;  // unique ID in increasing order
-  std::unordered_map<std::string, Printer> printers;  // resultant list (output)
-  explicit TaskDataInternal(unsigned id) : task_id(id) {}
-};
-
-using PrinterCache = std::vector<std::unique_ptr<Printer>>;
-using TaskData = std::unique_ptr<TaskDataInternal>;
-
-// Parses |data|, a JSON blob, into a vector of Printers.  If |data| cannot be
-// parsed, returns nullptr.  This is run off the UI thread as it could be very
-// slow.
-std::unique_ptr<PrinterCache> ParsePrinters(std::unique_ptr<std::string> data) {
-  if (!data) {
-    LOG(WARNING) << "Received null data";
-    return nullptr;
-  }
-  int error_code = 0;
-  int error_line = 0;
-
-  // This could be really slow.
-  base::ScopedBlockingCall scoped_blocking_call(FROM_HERE,
-                                                base::BlockingType::MAY_BLOCK);
-  std::unique_ptr<base::Value> json_blob =
-      base::JSONReader::ReadAndReturnErrorDeprecated(
-          *data, base::JSONParserOptions::JSON_PARSE_RFC, &error_code,
-          nullptr /* error_msg_out */, &error_line);
-  // It's not valid JSON.  Give up.
-  if (!json_blob || !json_blob->is_list()) {
-    LOG(WARNING) << "Failed to parse printers policy (" << error_code
-                 << ") on line " << error_line;
-    return nullptr;
-  }
-
-  const base::Value::ListStorage& printer_list = json_blob->GetList();
-  if (printer_list.size() > kMaxRecords) {
-    LOG(WARNING) << "Too many records in printers policy: "
-                 << printer_list.size();
-    return nullptr;
-  }
-
-  auto parsed_printers = std::make_unique<PrinterCache>();
-  parsed_printers->reserve(printer_list.size());
-  for (const base::Value& val : printer_list) {
-    // TODO(skau): Convert to the new Value APIs.
-    const base::DictionaryValue* printer_dict;
-    if (!val.GetAsDictionary(&printer_dict)) {
-      LOG(WARNING) << "Entry in printers policy skipped.  Not a dictionary.";
-      continue;
-    }
-
-    auto printer = RecommendedPrinterToPrinter(*printer_dict);
-    if (!printer) {
-      LOG(WARNING) << "Failed to parse printer configuration.  Skipped.";
-      continue;
-    }
-    parsed_printers->push_back(std::move(printer));
-  }
-
-  return parsed_printers;
-}
-
-// Computes the effective printer list using the access mode and
-// blacklist/whitelist.  Methods are required to be sequenced.  This object is
-// the owner of all the policy data. Methods updating the list of available
-// printers take TaskData (see above) as |task_data| parameter and returned it.
-class Restrictions {
- public:
-  Restrictions() : printers_cache_(nullptr) {
-    DETACH_FROM_SEQUENCE(sequence_checker_);
-  }
-  ~Restrictions() { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); }
-
-  // Sets the printer cache using the policy blob |data|.
-  TaskData SetData(TaskData task_data, std::unique_ptr<std::string> data) {
-    DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-    base::ScopedBlockingCall scoped_blocking_call(
-        FROM_HERE, base::BlockingType::MAY_BLOCK);
-    printers_cache_ = ParsePrinters(std::move(data));
-    return ComputePrinters(std::move(task_data));
-  }
-
-  // Clear the printer cache.
-  void ClearData() {
-    DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-    printers_cache_.reset();
-  }
-
-  // Sets the access mode to |mode|.
-  TaskData UpdateAccessMode(TaskData task_data,
-                            BulkPrintersCalculator::AccessMode mode) {
-    DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-    mode_ = mode;
-    return ComputePrinters(std::move(task_data));
-  }
-
-  // Sets the blacklist to |blacklist|.
-  TaskData UpdateBlacklist(TaskData task_data,
-                           const std::vector<std::string>& blacklist) {
-    DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-    has_blacklist_ = true;
-    blacklist_ = std::set<std::string>(blacklist.begin(), blacklist.end());
-    return ComputePrinters(std::move(task_data));
-  }
-
-  // Sets the whitelist to |whitelist|.
-  TaskData UpdateWhitelist(TaskData task_data,
-                           const std::vector<std::string>& whitelist) {
-    DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-    has_whitelist_ = true;
-    whitelist_ = std::set<std::string>(whitelist.begin(), whitelist.end());
-    return ComputePrinters(std::move(task_data));
-  }
-
- private:
-  // Returns true if we have enough data to compute the effective printer list.
-  bool IsReady() const {
-    DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-    if (!printers_cache_) {
-      return false;
-    }
-    switch (mode_) {
-      case BulkPrintersCalculator::AccessMode::ALL_ACCESS:
-        return true;
-      case BulkPrintersCalculator::AccessMode::BLACKLIST_ONLY:
-        return has_blacklist_;
-      case BulkPrintersCalculator::AccessMode::WHITELIST_ONLY:
-        return has_whitelist_;
-      case BulkPrintersCalculator::AccessMode::UNSET:
-        return false;
-    }
-    NOTREACHED();
-    return false;
-  }
-
-  // Calculates resultant list of available printers.
-  TaskData ComputePrinters(TaskData task_data) {
-    DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-
-    if (!IsReady()) {
-      return task_data;
-    }
-
-    switch (mode_) {
-      case BulkPrintersCalculator::UNSET:
-        NOTREACHED();
-        break;
-      case BulkPrintersCalculator::WHITELIST_ONLY:
-        for (const auto& printer : *printers_cache_) {
-          if (base::ContainsKey(whitelist_, printer->id())) {
-            task_data->printers.insert({printer->id(), *printer});
-          }
-        }
-        break;
-      case BulkPrintersCalculator::BLACKLIST_ONLY:
-        for (const auto& printer : *printers_cache_) {
-          if (!base::ContainsKey(blacklist_, printer->id())) {
-            task_data->printers.insert({printer->id(), *printer});
-          }
-        }
-        break;
-      case BulkPrintersCalculator::ALL_ACCESS:
-        for (const auto& printer : *printers_cache_) {
-          task_data->printers.insert({printer->id(), *printer});
-        }
-        break;
-    }
-
-    return task_data;
-  }
-
-  // Cache of the parsed printer configuration file.
-  std::unique_ptr<PrinterCache> printers_cache_;
-  // The type of restriction which is enforced.
-  BulkPrintersCalculator::AccessMode mode_ = BulkPrintersCalculator::UNSET;
-  // Blacklist: the list of ids which should not appear in the final list.
-  bool has_blacklist_ = false;
-  std::set<std::string> blacklist_;
-  // Whitelist: the list of the only ids which should appear in the final list.
-  bool has_whitelist_ = false;
-  std::set<std::string> whitelist_;
-
-  SEQUENCE_CHECKER(sequence_checker_);
-  DISALLOW_COPY_AND_ASSIGN(Restrictions);
-};
-
-class BulkPrintersCalculatorImpl : public BulkPrintersCalculator {
- public:
-  BulkPrintersCalculatorImpl()
-      : restrictions_(std::make_unique<Restrictions>()),
-        restrictions_runner_(base::CreateSequencedTaskRunnerWithTraits(
-            {base::TaskPriority::BEST_EFFORT, base::MayBlock(),
-             base::TaskShutdownBehavior::SKIP_ON_SHUTDOWN})),
-        weak_ptr_factory_(this) {}
-  ~BulkPrintersCalculatorImpl() override {
-    bool success =
-        restrictions_runner_->DeleteSoon(FROM_HERE, std::move(restrictions_));
-    if (!success) {
-      LOG(WARNING) << "Unable to schedule deletion of policy object.";
-    }
-  }
-
-  void AddObserver(Observer* observer) override {
-    DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-    observers_.AddObserver(observer);
-  }
-
-  void RemoveObserver(Observer* observer) override {
-    DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-    observers_.RemoveObserver(observer);
-  }
-
-  void ClearData() override {
-    DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-    if (!base::FeatureList::IsEnabled(features::kBulkPrinters)) {
-      return;
-    }
-    data_is_set_ = false;
-    last_processed_task_ = ++last_received_task_;
-    printers_.clear();
-    // Forward data to Restrictions to clear "Data".
-    restrictions_runner_->PostTask(
-        FROM_HERE, base::BindOnce(&Restrictions::ClearData,
-                                  base::Unretained(restrictions_.get())));
-    // Notify observers.
-    for (auto& observer : observers_) {
-      observer.OnPrintersChanged(this);
-    }
-  }
-
-  void SetData(std::unique_ptr<std::string> data) override {
-    DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-    if (!base::FeatureList::IsEnabled(features::kBulkPrinters)) {
-      return;
-    }
-    data_is_set_ = true;
-    TaskData task_data =
-        std::make_unique<TaskDataInternal>(++last_received_task_);
-    base::PostTaskAndReplyWithResult(
-        restrictions_runner_.get(), FROM_HERE,
-        base::BindOnce(&Restrictions::SetData,
-                       base::Unretained(restrictions_.get()),
-                       std::move(task_data), std::move(data)),
-        base::BindOnce(&BulkPrintersCalculatorImpl::OnComputationComplete,
-                       weak_ptr_factory_.GetWeakPtr()));
-  }
-
-  void SetAccessMode(AccessMode mode) override {
-    DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-    TaskData task_data =
-        std::make_unique<TaskDataInternal>(++last_received_task_);
-    base::PostTaskAndReplyWithResult(
-        restrictions_runner_.get(), FROM_HERE,
-        base::BindOnce(&Restrictions::UpdateAccessMode,
-                       base::Unretained(restrictions_.get()),
-                       std::move(task_data), mode),
-        base::BindOnce(&BulkPrintersCalculatorImpl::OnComputationComplete,
-                       weak_ptr_factory_.GetWeakPtr()));
-  }
-
-  void SetBlacklist(const std::vector<std::string>& blacklist) override {
-    DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-    TaskData task_data =
-        std::make_unique<TaskDataInternal>(++last_received_task_);
-    base::PostTaskAndReplyWithResult(
-        restrictions_runner_.get(), FROM_HERE,
-        base::BindOnce(&Restrictions::UpdateBlacklist,
-                       base::Unretained(restrictions_.get()),
-                       std::move(task_data), blacklist),
-        base::BindOnce(&BulkPrintersCalculatorImpl::OnComputationComplete,
-                       weak_ptr_factory_.GetWeakPtr()));
-  }
-
-  void SetWhitelist(const std::vector<std::string>& whitelist) override {
-    DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-    TaskData task_data =
-        std::make_unique<TaskDataInternal>(++last_received_task_);
-    base::PostTaskAndReplyWithResult(
-        restrictions_runner_.get(), FROM_HERE,
-        base::BindOnce(&Restrictions::UpdateWhitelist,
-                       base::Unretained(restrictions_.get()),
-                       std::move(task_data), whitelist),
-        base::BindOnce(&BulkPrintersCalculatorImpl::OnComputationComplete,
-                       weak_ptr_factory_.GetWeakPtr()));
-  }
-
-  bool IsDataPolicySet() const override {
-    DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-    return data_is_set_;
-  }
-
-  bool IsComplete() const override {
-    DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-    return (last_processed_task_ == last_received_task_);
-  }
-
-  const std::unordered_map<std::string, Printer>& GetPrinters() const override {
-    DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-    return printers_;
-  }
-
- private:
-  // Called on computation completion. |task_data| corresponds to finalized
-  // task.
-  void OnComputationComplete(TaskData task_data) {
-    DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-    if (!task_data || task_data->task_id <= last_processed_task_) {
-      // The task is outdated (ClearData() was called in the meantime).
-      return;
-    }
-    last_processed_task_ = task_data->task_id;
-    if (last_processed_task_ < last_received_task_ && printers_.empty() &&
-        task_data->printers.empty()) {
-      // No changes in the object's state.
-      return;
-    }
-    printers_.swap(task_data->printers);
-    task_data.reset();
-    // Notifies observers about changes.
-    for (auto& observer : observers_) {
-      observer.OnPrintersChanged(this);
-    }
-  }
-
-  // Holds the blacklist and whitelist.  Computes the effective printer list.
-  std::unique_ptr<Restrictions> restrictions_;
-  // Off UI sequence for computing the printer view.
-  scoped_refptr<base::SequencedTaskRunner> restrictions_runner_;
-
-  // True if printers_ is based on a current policy.
-  bool data_is_set_ = false;
-  // Id of the last scheduled task.
-  unsigned last_received_task_ = 0;
-  // Id of the last completed task.
-  unsigned last_processed_task_ = 0;
-  // The computed set of printers.
-  std::unordered_map<std::string, Printer> printers_;
-
-  base::ObserverList<BulkPrintersCalculator::Observer>::Unchecked observers_;
-  SEQUENCE_CHECKER(sequence_checker_);
-  DISALLOW_COPY_AND_ASSIGN(BulkPrintersCalculatorImpl);
-  base::WeakPtrFactory<BulkPrintersCalculatorImpl> weak_ptr_factory_;
-};
-
-}  // namespace
-
-// static
-std::unique_ptr<BulkPrintersCalculator> BulkPrintersCalculator::Create() {
-  return std::make_unique<BulkPrintersCalculatorImpl>();
-}
-
-}  // namespace chromeos
diff --git a/chrome/browser/chromeos/printing/bulk_printers_calculator.h b/chrome/browser/chromeos/printing/bulk_printers_calculator.h
deleted file mode 100644
index 3bf1bc9..0000000
--- a/chrome/browser/chromeos/printing/bulk_printers_calculator.h
+++ /dev/null
@@ -1,88 +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 CHROME_BROWSER_CHROMEOS_PRINTING_BULK_PRINTERS_CALCULATOR_H_
-#define CHROME_BROWSER_CHROMEOS_PRINTING_BULK_PRINTERS_CALCULATOR_H_
-
-#include <memory>
-#include <string>
-#include <unordered_map>
-#include <utility>
-#include <vector>
-
-#include "base/memory/weak_ptr.h"
-#include "chromeos/printing/printer_configuration.h"
-
-namespace chromeos {
-
-// Calculates a list of available printers from four policies: Data (json with
-// all printers), AccessMode (see below), Whitelist and Blacklist (lists with
-// ids). All methods must be called from the same sequence and all observers'
-// notifications will be called from this sequence. Resultant list of available
-// printers are calculated asynchronously on a dedicated internal sequence.
-class BulkPrintersCalculator
-    : public base::SupportsWeakPtr<BulkPrintersCalculator> {
- public:
-  // Algorithm used to calculate a list of available printers from the content
-  // of the "Data" policy.
-  enum AccessMode {
-    UNSET = -1,
-    // Printers in the blacklist are disallowed.  Others are allowed.
-    BLACKLIST_ONLY = 0,
-    // Only printers in the whitelist are allowed.
-    WHITELIST_ONLY = 1,
-    // All printers in the "Data" policy are allowed.
-    ALL_ACCESS = 2
-  };
-
-  class Observer {
-   public:
-    // Observer is notified by this call when the state of the object changes.
-    // See the section "Methods returning the state of the object" below to
-    // learn about parameters defining the state of the object. |sender| is
-    // a pointer to the object calling the notification.
-    virtual void OnPrintersChanged(const BulkPrintersCalculator* sender) = 0;
-  };
-
-  static std::unique_ptr<BulkPrintersCalculator> Create();
-  virtual ~BulkPrintersCalculator() = default;
-
-  virtual void AddObserver(Observer* observer) = 0;
-  virtual void RemoveObserver(Observer* observer) = 0;
-
-  // ========================= Methods setting values of the four policies
-
-  // Sets the "Data" policy. |data| is a list of all printers in JSON format.
-  virtual void SetData(std::unique_ptr<std::string> data) = 0;
-  // Clears the "Data" policy.
-  virtual void ClearData() = 0;
-
-  // Sets the "AccessMode" policy. See description of the AccessMode enum.
-  virtual void SetAccessMode(AccessMode mode) = 0;
-  // Sets the "Blacklist" policy. |blacklist| is a list of printers ids.
-  virtual void SetBlacklist(const std::vector<std::string>& blacklist) = 0;
-  // Sets the "Whitelist" policy. |whitelist| is a list of printers ids.
-  virtual void SetWhitelist(const std::vector<std::string>& whitelist) = 0;
-
-  // ========================= Methods returning the state of the object
-  // Methods returning the three parameters defining the state of the object.
-
-  // Returns true if the "Data" policy has been set with SetData(...) method
-  // (may be not processed yet). Returns false if the "Data" policy has been
-  // cleared with ClearData() method or SetData(...) has been never called.
-  virtual bool IsDataPolicySet() const = 0;
-  // Returns false if current policies were not processed yet. Returns true
-  // if there is no on-going calculations and the method below returns the
-  // list of available printers that is up-to-date with current policies.
-  virtual bool IsComplete() const = 0;
-  // Returns a reference to a resultant list of available printers. Keys are
-  // printers ids. If the list of available printers cannot be calculated
-  // (because of some error or missing policy), an empty map is returned.
-  virtual const std::unordered_map<std::string, Printer>& GetPrinters()
-      const = 0;
-};
-
-}  // namespace chromeos
-
-#endif  // CHROME_BROWSER_CHROMEOS_PRINTING_BULK_PRINTERS_CALCULATOR_H_
diff --git a/chrome/browser/chromeos/printing/bulk_printers_calculator_factory.cc b/chrome/browser/chromeos/printing/bulk_printers_calculator_factory.cc
deleted file mode 100644
index 110b4db..0000000
--- a/chrome/browser/chromeos/printing/bulk_printers_calculator_factory.cc
+++ /dev/null
@@ -1,80 +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/chromeos/printing/bulk_printers_calculator_factory.h"
-
-#include <memory>
-
-#include "base/lazy_instance.h"
-#include "chrome/browser/chromeos/printing/bulk_printers_calculator.h"
-#include "chrome/browser/chromeos/profiles/profile_helper.h"
-#include "chrome/browser/profiles/profile.h"
-#include "components/user_manager/user.h"
-
-namespace chromeos {
-
-namespace {
-
-base::LazyInstance<BulkPrintersCalculatorFactory>::DestructorAtExit
-    g_printers_factory = LAZY_INSTANCE_INITIALIZER;
-
-}  // namespace
-
-// static
-BulkPrintersCalculatorFactory* BulkPrintersCalculatorFactory::Get() {
-  return g_printers_factory.Pointer();
-}
-
-base::WeakPtr<BulkPrintersCalculator>
-BulkPrintersCalculatorFactory::GetForAccountId(const AccountId& account_id) {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  auto found = printers_by_user_.find(account_id);
-  if (found != printers_by_user_.end()) {
-    return found->second->AsWeakPtr();
-  }
-
-  printers_by_user_[account_id] = BulkPrintersCalculator::Create();
-  return printers_by_user_[account_id]->AsWeakPtr();
-}
-
-base::WeakPtr<BulkPrintersCalculator>
-BulkPrintersCalculatorFactory::GetForProfile(Profile* profile) {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  const user_manager::User* user =
-      ProfileHelper::Get()->GetUserByProfile(profile);
-  if (!user)
-    return nullptr;
-
-  return GetForAccountId(user->GetAccountId());
-}
-
-void BulkPrintersCalculatorFactory::RemoveForUserId(
-    const AccountId& account_id) {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  printers_by_user_.erase(account_id);
-}
-
-base::WeakPtr<BulkPrintersCalculator>
-BulkPrintersCalculatorFactory::GetForDevice() {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  if (!device_printers_)
-    device_printers_ = BulkPrintersCalculator::Create();
-  return device_printers_->AsWeakPtr();
-}
-
-void BulkPrintersCalculatorFactory::ShutdownProfiles() {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  printers_by_user_.clear();
-}
-
-void BulkPrintersCalculatorFactory::Shutdown() {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  printers_by_user_.clear();
-  device_printers_.reset();
-}
-
-BulkPrintersCalculatorFactory::BulkPrintersCalculatorFactory() = default;
-BulkPrintersCalculatorFactory::~BulkPrintersCalculatorFactory() = default;
-
-}  // namespace chromeos
diff --git a/chrome/browser/chromeos/printing/bulk_printers_calculator_factory.h b/chrome/browser/chromeos/printing/bulk_printers_calculator_factory.h
deleted file mode 100644
index 2755ac7c..0000000
--- a/chrome/browser/chromeos/printing/bulk_printers_calculator_factory.h
+++ /dev/null
@@ -1,68 +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_CHROMEOS_PRINTING_BULK_PRINTERS_CALCULATOR_FACTORY_H_
-#define CHROME_BROWSER_CHROMEOS_PRINTING_BULK_PRINTERS_CALCULATOR_FACTORY_H_
-
-#include <map>
-#include <memory>
-
-#include "base/lazy_instance.h"
-#include "base/macros.h"
-#include "base/memory/weak_ptr.h"
-#include "base/sequence_checker.h"
-#include "chrome/browser/chromeos/printing/bulk_printers_calculator.h"
-#include "components/account_id/account_id.h"
-
-class Profile;
-
-namespace chromeos {
-
-// Dispenses BulkPrintersCalculator objects based on account id.  Access to this
-// object should be sequenced.
-class BulkPrintersCalculatorFactory {
- public:
-  static BulkPrintersCalculatorFactory* Get();
-
-  // Returns a WeakPtr to the BulkPrintersCalculator registered for
-  // |account_id|. If an BulkPrintersCalculator does not exist, one will be
-  // created for |account_id|. The returned object remains valid until
-  // RemoveForUserId or Shutdown is called.
-  base::WeakPtr<BulkPrintersCalculator> GetForAccountId(
-      const AccountId& account_id);
-
-  // Returns a WeakPtr to the BulkPrintersCalculator registered for |profile|
-  // which could be null if |profile| does not map to a valid AccountId. The
-  // returned object remains valid until RemoveForUserId or Shutdown is called.
-  base::WeakPtr<BulkPrintersCalculator> GetForProfile(Profile* profile);
-
-  // Returns a WeakPtr to the BulkPrintersCalculator registered for the device.
-  base::WeakPtr<BulkPrintersCalculator> GetForDevice();
-
-  // Deletes the BulkPrintersCalculator registered for |account_id|.
-  void RemoveForUserId(const AccountId& account_id);
-
-  // Tear down all BulkPrintersCalculator created for users/profiles.
-  void ShutdownProfiles();
-
-  // Tear down all BulkPrintersCalculator.
-  void Shutdown();
-
- private:
-  friend struct base::LazyInstanceTraitsBase<BulkPrintersCalculatorFactory>;
-
-  BulkPrintersCalculatorFactory();
-  ~BulkPrintersCalculatorFactory();
-
-  std::map<AccountId, std::unique_ptr<BulkPrintersCalculator>>
-      printers_by_user_;
-  std::unique_ptr<BulkPrintersCalculator> device_printers_;
-  SEQUENCE_CHECKER(sequence_checker_);
-
-  DISALLOW_COPY_AND_ASSIGN(BulkPrintersCalculatorFactory);
-};
-
-}  // namespace chromeos
-
-#endif  // CHROME_BROWSER_CHROMEOS_PRINTING_BULK_PRINTERS_CALCULATOR_FACTORY_H_
diff --git a/chrome/browser/chromeos/printing/calculators_policies_binder.cc b/chrome/browser/chromeos/printing/calculators_policies_binder.cc
deleted file mode 100644
index 1f422df..0000000
--- a/chrome/browser/chromeos/printing/calculators_policies_binder.cc
+++ /dev/null
@@ -1,195 +0,0 @@
-// Copyright 2019 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "chrome/browser/chromeos/printing/calculators_policies_binder.h"
-
-#include <list>
-#include <map>
-#include <string>
-#include <vector>
-
-#include "chrome/browser/chromeos/printing/bulk_printers_calculator.h"
-#include "chrome/browser/chromeos/printing/bulk_printers_calculator_factory.h"
-#include "chrome/browser/policy/profile_policy_connector.h"
-#include "chrome/browser/policy/profile_policy_connector_factory.h"
-#include "chrome/common/pref_names.h"
-#include "chromeos/settings/cros_settings_names.h"
-#include "components/prefs/pref_change_registrar.h"
-#include "components/prefs/pref_service.h"
-
-namespace chromeos {
-
-namespace {
-
-// It stores the number of bindings (instances of this class) connected to each
-// BulkPrintersCalculator object. It allows us to make sure, that every
-// BulkPrintersCalculator object is not binded more that once.
-std::map<BulkPrintersCalculator*, unsigned>& BindingsCount() {
-  static base::NoDestructor<std::map<BulkPrintersCalculator*, unsigned>>
-      bindings_count;
-  return *bindings_count;
-}
-
-BulkPrintersCalculator::AccessMode ConvertToAccessMode(int mode_val) {
-  if (mode_val >= BulkPrintersCalculator::BLACKLIST_ONLY &&
-      mode_val <= BulkPrintersCalculator::ALL_ACCESS) {
-    return static_cast<BulkPrintersCalculator::AccessMode>(mode_val);
-  }
-  // Error occurred, let's return the default value.
-  LOG(ERROR) << "Unrecognized access mode";
-  return BulkPrintersCalculator::ALL_ACCESS;
-}
-
-std::vector<std::string> ConvertToVector(const base::ListValue* list) {
-  std::vector<std::string> string_list;
-  if (list) {
-    for (const base::Value& value : *list) {
-      if (value.is_string()) {
-        string_list.push_back(value.GetString());
-      }
-    }
-  }
-  return string_list;
-}
-
-class CalculatorsPoliciesBinderImpl : public CalculatorsPoliciesBinder {
- public:
-  CalculatorsPoliciesBinderImpl(CrosSettings* settings, Profile* profile)
-      : settings_(settings), profile_(profile) {
-    pref_change_registrar_.Init(profile->GetPrefs());
-    // Bind device policies to corresponding instance of BulkPrintersCalculator.
-    device_printers_ = BulkPrintersCalculatorFactory::Get()->GetForDevice();
-    if (device_printers_ && ++(BindingsCount()[device_printers_.get()]) == 1) {
-      BindSettings(kDeviceNativePrintersAccessMode,
-                   &CalculatorsPoliciesBinderImpl::UpdateDeviceAccessMode);
-      BindSettings(kDeviceNativePrintersBlacklist,
-                   &CalculatorsPoliciesBinderImpl::UpdateDeviceBlacklist);
-      BindSettings(kDeviceNativePrintersWhitelist,
-                   &CalculatorsPoliciesBinderImpl::UpdateDeviceWhitelist);
-    }
-    // Bind user policies to corresponding instance of BulkPrintersCalculator.
-    user_printers_ =
-        BulkPrintersCalculatorFactory::Get()->GetForProfile(profile);
-    if (user_printers_ && ++(BindingsCount()[user_printers_.get()]) == 1) {
-      BindPref(prefs::kRecommendedNativePrintersAccessMode,
-               &CalculatorsPoliciesBinderImpl::UpdateUserAccessMode);
-      BindPref(prefs::kRecommendedNativePrintersBlacklist,
-               &CalculatorsPoliciesBinderImpl::UpdateUserBlacklist);
-      BindPref(prefs::kRecommendedNativePrintersWhitelist,
-               &CalculatorsPoliciesBinderImpl::UpdateUserWhitelist);
-    }
-  }
-
-  ~CalculatorsPoliciesBinderImpl() override {
-    // We have to decrease counters in bindings_count.
-    if (device_printers_ && --(BindingsCount()[device_printers_.get()]) == 0) {
-      BindingsCount().erase(device_printers_.get());
-    }
-    if (user_printers_ && --(BindingsCount()[user_printers_.get()]) == 0) {
-      BindingsCount().erase(user_printers_.get());
-    }
-  }
-
- private:
-  // Methods propagating values from policies to BulkPrintersCalculator.
-  void UpdateDeviceAccessMode() {
-    int mode_val;
-    if (!settings_->GetInteger(kDeviceNativePrintersAccessMode, &mode_val)) {
-      mode_val = BulkPrintersCalculator::AccessMode::UNSET;
-    }
-    device_printers_->SetAccessMode(ConvertToAccessMode(mode_val));
-  }
-
-  void UpdateDeviceBlacklist() {
-    device_printers_->SetBlacklist(
-        FromSettings(kDeviceNativePrintersBlacklist));
-  }
-
-  void UpdateDeviceWhitelist() {
-    device_printers_->SetWhitelist(
-        FromSettings(kDeviceNativePrintersWhitelist));
-  }
-
-  void UpdateUserAccessMode() {
-    user_printers_->SetAccessMode(
-        ConvertToAccessMode(profile_->GetPrefs()->GetInteger(
-            prefs::kRecommendedNativePrintersAccessMode)));
-  }
-
-  void UpdateUserBlacklist() {
-    user_printers_->SetBlacklist(
-        FromPrefs(prefs::kRecommendedNativePrintersBlacklist));
-  }
-
-  void UpdateUserWhitelist() {
-    user_printers_->SetWhitelist(
-        FromPrefs(prefs::kRecommendedNativePrintersWhitelist));
-  }
-
-  typedef void (CalculatorsPoliciesBinderImpl::*SimpleMethod)();
-
-  // Binds given device policy to given method and calls this method once.
-  void BindPref(const char* policy_name, SimpleMethod method_to_call) {
-    pref_change_registrar_.Add(
-        policy_name,
-        base::BindRepeating(method_to_call, base::Unretained(this)));
-    (this->*method_to_call)();
-  }
-
-  // Binds given user policy to given method and calls this method once.
-  void BindSettings(const char* policy_name, SimpleMethod method_to_call) {
-    subscriptions_.push_back(settings_->AddSettingsObserver(
-        policy_name,
-        base::BindRepeating(method_to_call, base::Unretained(this))));
-    (this->*method_to_call)();
-  }
-
-  // Extracts the list of strings named |policy_name| from device policies.
-  std::vector<std::string> FromSettings(const std::string& policy_name) {
-    const base::ListValue* list;
-    if (!settings_->GetList(policy_name, &list)) {
-      list = nullptr;
-    }
-    return ConvertToVector(list);
-  }
-
-  // Extracts the list of strings named |policy_name| from user policies.
-  std::vector<std::string> FromPrefs(const std::string& policy_name) {
-    return ConvertToVector(profile_->GetPrefs()->GetList(policy_name));
-  }
-
-  // Device and user bulk printers. Unowned.
-  base::WeakPtr<BulkPrintersCalculator> device_printers_;
-  base::WeakPtr<BulkPrintersCalculator> user_printers_;
-
-  // Device and profile (user) settings.
-  CrosSettings* settings_;
-  std::list<std::unique_ptr<CrosSettings::ObserverSubscription>> subscriptions_;
-  Profile* profile_;
-  PrefChangeRegistrar pref_change_registrar_;
-
-  SEQUENCE_CHECKER(sequence_checker_);
-  DISALLOW_COPY_AND_ASSIGN(CalculatorsPoliciesBinderImpl);
-};
-
-}  // namespace
-
-// static
-void CalculatorsPoliciesBinder::RegisterProfilePrefs(
-    user_prefs::PrefRegistrySyncable* registry) {
-  // Default value for access mode is AllAccess.
-  registry->RegisterIntegerPref(prefs::kRecommendedNativePrintersAccessMode,
-                                BulkPrintersCalculator::ALL_ACCESS);
-  registry->RegisterListPref(prefs::kRecommendedNativePrintersBlacklist);
-  registry->RegisterListPref(prefs::kRecommendedNativePrintersWhitelist);
-}
-
-// static
-std::unique_ptr<CalculatorsPoliciesBinder> CalculatorsPoliciesBinder::Create(
-    CrosSettings* settings,
-    Profile* profile) {
-  return std::make_unique<CalculatorsPoliciesBinderImpl>(settings, profile);
-}
-
-}  // namespace chromeos
diff --git a/chrome/browser/chromeos/printing/calculators_policies_binder.h b/chrome/browser/chromeos/printing/calculators_policies_binder.h
deleted file mode 100644
index 926e840a0..0000000
--- a/chrome/browser/chromeos/printing/calculators_policies_binder.h
+++ /dev/null
@@ -1,39 +0,0 @@
-// Copyright 2018 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CHROME_BROWSER_CHROMEOS_PRINTING_CALCULATORS_POLICIES_BINDER_H_
-#define CHROME_BROWSER_CHROMEOS_PRINTING_CALCULATORS_POLICIES_BINDER_H_
-
-#include <memory>
-
-#include "base/macros.h"
-#include "chrome/browser/chromeos/settings/cros_settings.h"
-#include "chrome/browser/profiles/profile.h"
-#include "components/pref_registry/pref_registry_syncable.h"
-
-namespace chromeos {
-
-// Observes device settings & user profile modifications and propagates them to
-// BulkPrintersCalculator objects associated with given device context and user
-// profile. All methods must be called from the same sequence (UI).
-class CalculatorsPoliciesBinder {
- public:
-  static void RegisterProfilePrefs(user_prefs::PrefRegistrySyncable* registry);
-
-  // |settings| is the source of device policies. |profile| is a user profile.
-  static std::unique_ptr<CalculatorsPoliciesBinder> Create(
-      CrosSettings* settings,
-      Profile* profile);
-  virtual ~CalculatorsPoliciesBinder() = default;
-
- protected:
-  CalculatorsPoliciesBinder() = default;
-
- private:
-  DISALLOW_COPY_AND_ASSIGN(CalculatorsPoliciesBinder);
-};
-
-}  // namespace chromeos
-
-#endif  // CHROME_BROWSER_CHROMEOS_PRINTING_CALCULATORS_POLICIES_BINDER_H_
diff --git a/chrome/browser/chromeos/printing/cups_print_job_manager_impl.cc b/chrome/browser/chromeos/printing/cups_print_job_manager_impl.cc
index 45e2a4c1..e6dc236fe 100644
--- a/chrome/browser/chromeos/printing/cups_print_job_manager_impl.cc
+++ b/chrome/browser/chromeos/printing/cups_print_job_manager_impl.cc
@@ -487,7 +487,7 @@
     jobs_.clear();
   }
 
-  // Notify observers that a state update has occurred for |job|.
+  // Notify observers that a state update has occured for |job|.
   void NotifyJobStateUpdate(base::WeakPtr<CupsPrintJob> job) {
     DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
 
diff --git a/chrome/browser/chromeos/printing/cups_printers_manager.cc b/chrome/browser/chromeos/printing/cups_printers_manager.cc
index c389831c..1faee11 100644
--- a/chrome/browser/chromeos/printing/cups_printers_manager.cc
+++ b/chrome/browser/chromeos/printing/cups_printers_manager.cc
@@ -111,9 +111,8 @@
     // Prime the printer cache with the configured and enterprise printers.
     printers_[kConfigured] = synced_printers_manager_->GetConfiguredPrinters();
     RebuildConfiguredPrintersIndex();
+    printers_[kEnterprise] = synced_printers_manager_->GetEnterprisePrinters();
     synced_printers_manager_observer_.Add(synced_printers_manager_);
-    enterprise_printers_are_ready_ =
-        synced_printers_manager_->GetEnterprisePrinters(printers_[kEnterprise]);
 
     // Callbacks may ensue immediately when the observer proxies are set up, so
     // these instantiations must come after everything else is initialized.
@@ -198,9 +197,6 @@
   void AddObserver(CupsPrintersManager::Observer* observer) override {
     DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_);
     observer_list_.AddObserver(observer);
-    if (enterprise_printers_are_ready_) {
-      observer->OnEnterprisePrintersInitialized();
-    }
   }
 
   // Public API function.
@@ -262,16 +258,9 @@
 
   // SyncedPrintersManager::Observer implementation
   void OnEnterprisePrintersChanged(
-      const std::vector<Printer>& printers,
-      bool enterprise_printers_are_ready) override {
+      const std::vector<Printer>& printers) override {
     DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_);
     printers_[kEnterprise] = printers;
-    if (enterprise_printers_are_ready && !enterprise_printers_are_ready_) {
-      enterprise_printers_are_ready_ = true;
-      for (auto& observer : observer_list_) {
-        observer.OnEnterprisePrintersInitialized();
-      }
-    }
     NotifyObservers({kEnterprise});
   }
 
@@ -559,13 +548,6 @@
   // Categorized printers.  This is indexed by PrinterClass.
   std::vector<std::vector<Printer>> printers_;
 
-  // Equals true if the list of enterprise printers and related policies
-  // is initialized and configured correctly.
-  bool enterprise_printers_are_ready_ = false;
-
-  // Printer ids that occur in one of our categories or printers.
-  std::unordered_set<std::string> known_printer_ids_;
-
   // This is a dual-purpose structure.  The keys in the map are printer ids.
   // If an entry exists in this map it means we have received a response from
   // PpdProvider about a PpdReference for the given printer.  A null value
diff --git a/chrome/browser/chromeos/printing/cups_printers_manager.h b/chrome/browser/chromeos/printing/cups_printers_manager.h
index 441a71db..b03ab169 100644
--- a/chrome/browser/chromeos/printing/cups_printers_manager.h
+++ b/chrome/browser/chromeos/printing/cups_printers_manager.h
@@ -45,13 +45,9 @@
    public:
     // The list of printers in this class has changed to the given printers.
     virtual void OnPrintersChanged(PrinterClass printer_class,
-                                   const std::vector<Printer>& printers) {}
-    // It is called exactly once for each observer. It means that the
-    // subsystem for enterprise printers is initialized. When an observer is
-    // being registered after the subsystem's initialization, this call is
-    // scheduled immediately in AddObserver method.
-    virtual void OnEnterprisePrintersInitialized() {}
+                                   const std::vector<Printer>& printers) = 0;
 
+   protected:
     virtual ~Observer() = default;
   };
 
@@ -94,9 +90,9 @@
   // the printer_id is not that of a configured printer.
   virtual void RemoveConfiguredPrinter(const std::string& printer_id) = 0;
 
-  // Add or remove observers.  Observers must be on the same
+  // Add or remove observers.  Observers do not need to be on the same
   // sequence as the CupsPrintersManager.  Callbacks for a given observer
-  // will be on the same sequence as the CupsPrintersManager.
+  // will be on the same sequence as was used to call AddObserver().
   virtual void AddObserver(Observer* observer) = 0;
   virtual void RemoveObserver(Observer* observer) = 0;
 
diff --git a/chrome/browser/chromeos/printing/cups_printers_manager_unittest.cc b/chrome/browser/chromeos/printing/cups_printers_manager_unittest.cc
index 9861b24..76f234c 100644
--- a/chrome/browser/chromeos/printing/cups_printers_manager_unittest.cc
+++ b/chrome/browser/chromeos/printing/cups_printers_manager_unittest.cc
@@ -36,9 +36,8 @@
   }
 
   // Returns printers from enterprise policy.
-  bool GetEnterprisePrinters(std::vector<Printer>& printers) const override {
-    printers = enterprise_printers_;
-    return true;
+  std::vector<Printer> GetEnterprisePrinters() const override {
+    return enterprise_printers_;
   }
 
   // Attach |observer| for notification of events.  |observer| is expected to
@@ -129,7 +128,7 @@
     enterprise_printers_.insert(enterprise_printers_.end(), printers.begin(),
                                 printers.end());
     for (Observer& observer : observers_) {
-      observer.OnEnterprisePrintersChanged(enterprise_printers_, true);
+      observer.OnEnterprisePrintersChanged(enterprise_printers_);
     }
   }
 
@@ -138,7 +137,7 @@
   void RemoveEnterprisePrinters(const std::unordered_set<std::string>& ids) {
     RemovePrinters(ids, &enterprise_printers_);
     for (Observer& observer : observers_) {
-      observer.OnEnterprisePrintersChanged(enterprise_printers_, true);
+      observer.OnEnterprisePrintersChanged(enterprise_printers_);
     }
   }
 
diff --git a/chrome/browser/chromeos/printing/device_external_printers_factory.cc b/chrome/browser/chromeos/printing/device_external_printers_factory.cc
new file mode 100644
index 0000000..f85bb38
--- /dev/null
+++ b/chrome/browser/chromeos/printing/device_external_printers_factory.cc
@@ -0,0 +1,41 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/chromeos/printing/device_external_printers_factory.h"
+
+#include <memory>
+
+#include "base/lazy_instance.h"
+#include "chrome/browser/chromeos/printing/external_printers.h"
+
+namespace chromeos {
+
+namespace {
+
+base::LazyInstance<DeviceExternalPrintersFactory>::DestructorAtExit
+    g_printers_factory = LAZY_INSTANCE_INITIALIZER;
+
+}  // namespace
+
+// static
+DeviceExternalPrintersFactory* DeviceExternalPrintersFactory::Get() {
+  return g_printers_factory.Pointer();
+}
+
+base::WeakPtr<ExternalPrinters> DeviceExternalPrintersFactory::GetForDevice() {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  if (!device_printers_)
+    device_printers_ = ExternalPrinters::Create();
+  return device_printers_->AsWeakPtr();
+}
+
+void DeviceExternalPrintersFactory::Shutdown() {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  device_printers_.reset();
+}
+
+DeviceExternalPrintersFactory::DeviceExternalPrintersFactory() = default;
+DeviceExternalPrintersFactory::~DeviceExternalPrintersFactory() = default;
+
+}  // namespace chromeos
diff --git a/chrome/browser/chromeos/printing/device_external_printers_factory.h b/chrome/browser/chromeos/printing/device_external_printers_factory.h
new file mode 100644
index 0000000..e73b311d
--- /dev/null
+++ b/chrome/browser/chromeos/printing/device_external_printers_factory.h
@@ -0,0 +1,45 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_CHROMEOS_PRINTING_DEVICE_EXTERNAL_PRINTERS_FACTORY_H_
+#define CHROME_BROWSER_CHROMEOS_PRINTING_DEVICE_EXTERNAL_PRINTERS_FACTORY_H_
+
+#include <memory>
+
+#include "base/lazy_instance.h"
+#include "base/macros.h"
+#include "base/memory/weak_ptr.h"
+#include "base/sequence_checker.h"
+#include "chrome/browser/chromeos/printing/external_printers.h"
+
+namespace chromeos {
+
+// Dispenses ExternalPrinters object for device. Access to this object should be
+// sequenced.
+class DeviceExternalPrintersFactory {
+ public:
+  static DeviceExternalPrintersFactory* Get();
+
+  // Returns a WeakPtr to the ExternalPrinters registered for the device.
+  base::WeakPtr<ExternalPrinters> GetForDevice();
+
+  // Tear down device ExternalPrinters object.
+  void Shutdown();
+
+ private:
+  friend struct base::LazyInstanceTraitsBase<DeviceExternalPrintersFactory>;
+
+  DeviceExternalPrintersFactory();
+  ~DeviceExternalPrintersFactory();
+
+  std::unique_ptr<ExternalPrinters> device_printers_;
+
+  SEQUENCE_CHECKER(sequence_checker_);
+
+  DISALLOW_COPY_AND_ASSIGN(DeviceExternalPrintersFactory);
+};
+
+}  // namespace chromeos
+
+#endif  // CHROME_BROWSER_CHROMEOS_PRINTING_DEVICE_EXTERNAL_PRINTERS_FACTORY_H_
diff --git a/chrome/browser/chromeos/printing/device_external_printers_settings_bridge.cc b/chrome/browser/chromeos/printing/device_external_printers_settings_bridge.cc
new file mode 100644
index 0000000..dfdabd88
--- /dev/null
+++ b/chrome/browser/chromeos/printing/device_external_printers_settings_bridge.cc
@@ -0,0 +1,101 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/chromeos/printing/device_external_printers_settings_bridge.h"
+
+#include <string>
+#include <vector>
+
+#include "base/bind.h"
+#include "base/values.h"
+#include "chrome/browser/chromeos/printing/device_external_printers_factory.h"
+#include "chrome/browser/chromeos/printing/external_printers.h"
+#include "chrome/browser/chromeos/settings/cros_settings.h"
+
+namespace chromeos {
+
+namespace {
+
+// Extracts the list of strings named |policy_name| from |settings| and returns
+// it.
+std::vector<std::string> FromSettings(const CrosSettings* settings,
+                                      const std::string& policy_name) {
+  std::vector<std::string> string_list;
+  const base::ListValue* list = nullptr;
+  if (!settings->GetList(policy_name, &list) || !list) {
+    return string_list;
+  }
+
+  for (const base::Value& value : *list) {
+    if (value.is_string()) {
+      string_list.push_back(value.GetString());
+    }
+  }
+
+  return string_list;
+}
+
+}  // namespace
+
+DeviceExternalPrintersSettingsBridge::DeviceExternalPrintersSettingsBridge(
+    const ExternalPrinterPolicies& policies,
+    CrosSettings* settings)
+    : settings_(settings), policies_(policies) {
+  access_mode_subscription_ = settings->AddSettingsObserver(
+      policies_.access_mode,
+      base::BindRepeating(
+          &DeviceExternalPrintersSettingsBridge::AccessModeUpdated,
+          base::Unretained(this)));
+  blacklist_subscription_ = settings->AddSettingsObserver(
+      policies_.blacklist,
+      base::BindRepeating(
+          &DeviceExternalPrintersSettingsBridge::BlacklistUpdated,
+          base::Unretained(this)));
+  whitelist_subscription_ = settings->AddSettingsObserver(
+      policies_.whitelist,
+      base::BindRepeating(
+          &DeviceExternalPrintersSettingsBridge::WhitelistUpdated,
+          base::Unretained(this)));
+  Initialize();
+}
+
+DeviceExternalPrintersSettingsBridge::~DeviceExternalPrintersSettingsBridge() =
+    default;
+
+void DeviceExternalPrintersSettingsBridge::Initialize() {
+  BlacklistUpdated();
+  WhitelistUpdated();
+  AccessModeUpdated();
+}
+
+void DeviceExternalPrintersSettingsBridge::AccessModeUpdated() {
+  int mode_val;
+  // Settings should contain value for access mode device setting.
+  // Even if it's not pushed with device policy, the default value should be
+  // set.
+  CHECK(settings_->GetInteger(policies_.access_mode, &mode_val));
+  ExternalPrinters::AccessMode mode =
+      static_cast<ExternalPrinters::AccessMode>(mode_val);
+
+  base::WeakPtr<ExternalPrinters> printers =
+      DeviceExternalPrintersFactory::Get()->GetForDevice();
+  if (printers)
+    printers->SetAccessMode(mode);
+}
+
+void DeviceExternalPrintersSettingsBridge::BlacklistUpdated() {
+  base::WeakPtr<ExternalPrinters> printers =
+      DeviceExternalPrintersFactory::Get()->GetForDevice();
+  if (printers)
+    printers->SetBlacklist(FromSettings(settings_, policies_.blacklist));
+}
+
+void DeviceExternalPrintersSettingsBridge::WhitelistUpdated() {
+  base::WeakPtr<ExternalPrinters> printers =
+      DeviceExternalPrintersFactory::Get()->GetForDevice();
+  if (printers)
+    printers->SetWhitelist(FromSettings(settings_, policies_.whitelist));
+}
+
+}  // namespace chromeos
diff --git a/chrome/browser/chromeos/printing/device_external_printers_settings_bridge.h b/chrome/browser/chromeos/printing/device_external_printers_settings_bridge.h
new file mode 100644
index 0000000..49e908d
--- /dev/null
+++ b/chrome/browser/chromeos/printing/device_external_printers_settings_bridge.h
@@ -0,0 +1,47 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_CHROMEOS_PRINTING_DEVICE_EXTERNAL_PRINTERS_SETTINGS_BRIDGE_H_
+#define CHROME_BROWSER_CHROMEOS_PRINTING_DEVICE_EXTERNAL_PRINTERS_SETTINGS_BRIDGE_H_
+
+#include "base/macros.h"
+#include "chrome/browser/chromeos/printing/external_printers_policies.h"
+#include "chrome/browser/chromeos/settings/cros_settings.h"
+
+namespace chromeos {
+
+class ExternalPrinters;
+
+// Observe device settings changes and propagate changes to ExternalPrinters.
+class DeviceExternalPrintersSettingsBridge {
+ public:
+  DeviceExternalPrintersSettingsBridge(const ExternalPrinterPolicies& policies,
+                                       CrosSettings* settings);
+  ~DeviceExternalPrintersSettingsBridge();
+
+ private:
+  // Retrieve initial values for device settings.
+  void Initialize();
+
+  // Handle update for the access mode policy.
+  void AccessModeUpdated();
+
+  // Handle updates for the blacklist policy.
+  void BlacklistUpdated();
+
+  // Handle updates for the whitelist policy.
+  void WhitelistUpdated();
+
+  CrosSettings* settings_;
+  const ExternalPrinterPolicies policies_;
+  std::unique_ptr<CrosSettings::ObserverSubscription> access_mode_subscription_;
+  std::unique_ptr<CrosSettings::ObserverSubscription> blacklist_subscription_;
+  std::unique_ptr<CrosSettings::ObserverSubscription> whitelist_subscription_;
+
+  DISALLOW_COPY_AND_ASSIGN(DeviceExternalPrintersSettingsBridge);
+};
+
+}  // namespace chromeos
+
+#endif  // CHROME_BROWSER_CHROMEOS_PRINTING_DEVICE_EXTERNAL_PRINTERS_SETTINGS_BRIDGE_H_
diff --git a/chrome/browser/chromeos/printing/enterprise_printers_provider.cc b/chrome/browser/chromeos/printing/enterprise_printers_provider.cc
deleted file mode 100644
index 7f6015d..0000000
--- a/chrome/browser/chromeos/printing/enterprise_printers_provider.cc
+++ /dev/null
@@ -1,255 +0,0 @@
-// Copyright 2018 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "chrome/browser/chromeos/printing/enterprise_printers_provider.h"
-
-#include <list>
-#include <vector>
-
-#include "base/feature_list.h"
-#include "base/json/json_reader.h"
-#include "base/md5.h"
-#include "chrome/browser/chromeos/printing/bulk_printers_calculator.h"
-#include "chrome/browser/chromeos/printing/bulk_printers_calculator_factory.h"
-#include "chrome/browser/chromeos/printing/calculators_policies_binder.h"
-#include "chrome/browser/policy/profile_policy_connector.h"
-#include "chrome/browser/policy/profile_policy_connector_factory.h"
-#include "chrome/common/chrome_features.h"
-#include "chrome/common/pref_names.h"
-#include "chromeos/printing/printer_translator.h"
-#include "components/policy/core/common/policy_service.h"
-#include "components/policy/policy_constants.h"
-#include "components/prefs/pref_change_registrar.h"
-#include "components/prefs/pref_service.h"
-
-namespace chromeos {
-
-namespace {
-
-std::vector<std::string> ConvertToVector(const base::ListValue* list) {
-  std::vector<std::string> string_list;
-  if (list) {
-    for (const base::Value& value : *list) {
-      if (value.is_string()) {
-        string_list.push_back(value.GetString());
-      }
-    }
-  }
-  return string_list;
-}
-
-class EnterprisePrintersProviderImpl : public EnterprisePrintersProvider,
-                                       public BulkPrintersCalculator::Observer {
- public:
-  EnterprisePrintersProviderImpl(CrosSettings* settings, Profile* profile)
-      : profile_(profile) {
-    // initialization of pref_change_registrar
-    pref_change_registrar_.Init(profile->GetPrefs());
-
-    if (base::FeatureList::IsEnabled(features::kBulkPrinters)) {
-      // Binds instances of BulkPrintersCalculator to policies.
-      policies_binder_ = CalculatorsPoliciesBinder::Create(settings, profile);
-      // Get instance of BulkPrintersCalculator for device policies.
-      device_printers_ = BulkPrintersCalculatorFactory::Get()->GetForDevice();
-      if (device_printers_) {
-        device_printers_->AddObserver(this);
-        RecalculateCompleteFlagForDevicePrinters();
-      }
-      // Get instance of BulkPrintersCalculator for user policies.
-      user_printers_ =
-          BulkPrintersCalculatorFactory::Get()->GetForProfile(profile);
-      if (user_printers_) {
-        user_printers_->AddObserver(this);
-        RecalculateCompleteFlagForUserPrinters();
-      }
-    } else {
-      // If a "Bulk Printers" feature is inactive, we do not bind anything.
-      // The list of printers is always empty and is reported as complete.
-      complete_ = true;
-    }
-    // Binds policy with recommended printers (deprecated). This method calls
-    // indirectly RecalculateCurrentPrintersList() that prepares the first
-    // version of final list of printers.
-    BindPref(prefs::kRecommendedNativePrinters,
-             &EnterprisePrintersProviderImpl::UpdateUserRecommendedPrinters);
-  }
-
-  ~EnterprisePrintersProviderImpl() override {
-    if (device_printers_)
-      device_printers_->RemoveObserver(this);
-    if (user_printers_)
-      user_printers_->RemoveObserver(this);
-  }
-
-  void AddObserver(EnterprisePrintersProvider::Observer* observer) override {
-    DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-    observers_.AddObserver(observer);
-    observer->OnPrintersChanged(complete_, printers_);
-  }
-
-  void RemoveObserver(EnterprisePrintersProvider::Observer* observer) override {
-    DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-    observers_.RemoveObserver(observer);
-  }
-
-  // BulkPrintersCalculator::Observer implementation
-  void OnPrintersChanged(const BulkPrintersCalculator* sender) override {
-    if (sender == device_printers_.get()) {
-      RecalculateCompleteFlagForDevicePrinters();
-    } else {
-      RecalculateCompleteFlagForUserPrinters();
-    }
-    RecalculateCurrentPrintersList();
-  }
-
- private:
-  // This method process value from the deprecated policy with recommended
-  // printers. It is called when value of the policy changes.
-  void UpdateUserRecommendedPrinters() {
-    recommended_printers_.clear();
-    std::vector<std::string> data =
-        FromPrefs(prefs::kRecommendedNativePrinters);
-    for (const auto& printer_json : data) {
-      std::unique_ptr<base::DictionaryValue> printer_dictionary =
-          base::DictionaryValue::From(base::JSONReader::ReadDeprecated(
-              printer_json, base::JSON_ALLOW_TRAILING_COMMAS));
-      if (!printer_dictionary) {
-        LOG(WARNING) << "Ignoring invalid printer.  Invalid JSON object: "
-                     << printer_json;
-        continue;
-      }
-
-      // Policy printers don't have id's but the ids only need to be locally
-      // unique so we'll hash the record.  This will not collide with the
-      // UUIDs generated for user entries.
-      std::string id = base::MD5String(printer_json);
-      printer_dictionary->SetString(kPrinterId, id);
-
-      auto new_printer = RecommendedPrinterToPrinter(*printer_dictionary);
-      if (!new_printer) {
-        LOG(WARNING) << "Recommended printer is malformed.";
-        continue;
-      }
-
-      if (!recommended_printers_.insert({id, *new_printer}).second) {
-        // Printer is already in the list.
-        LOG(WARNING) << "Duplicate printer ignored: " << id;
-        continue;
-      }
-    }
-    RecalculateCurrentPrintersList();
-  }
-
-  // These three methods calculate resultant list of printers and complete flag.
-
-  void RecalculateCompleteFlagForUserPrinters() {
-    user_printers_is_complete_ =
-        user_printers_->IsComplete() &&
-        (user_printers_->IsDataPolicySet() ||
-         !PolicyWithDataIsSet(policy::key::kNativePrintersBulkConfiguration));
-  }
-
-  void RecalculateCompleteFlagForDevicePrinters() {
-    device_printers_is_complete_ =
-        device_printers_->IsComplete() &&
-        (device_printers_->IsDataPolicySet() ||
-         !PolicyWithDataIsSet(policy::key::kDeviceNativePrinters));
-  }
-
-  void RecalculateCurrentPrintersList() {
-    complete_ = true;
-    printers_ = recommended_printers_;
-    if (device_printers_) {
-      complete_ = complete_ && device_printers_is_complete_;
-      const auto& printers = device_printers_->GetPrinters();
-      printers_.insert(printers.begin(), printers.end());
-    }
-    if (user_printers_) {
-      complete_ = complete_ && user_printers_is_complete_;
-      const auto& printers = user_printers_->GetPrinters();
-      printers_.insert(printers.begin(), printers.end());
-    }
-    for (auto& observer : observers_) {
-      observer.OnPrintersChanged(complete_, printers_);
-    }
-  }
-
-  typedef void (EnterprisePrintersProviderImpl::*SimpleMethod)();
-
-  // Binds given user policy to given method and calls this method once.
-  void BindPref(const char* policy_name, SimpleMethod method_to_call) {
-    pref_change_registrar_.Add(
-        policy_name,
-        base::BindRepeating(method_to_call, base::Unretained(this)));
-    (this->*method_to_call)();
-  }
-
-  // Extracts the list of strings named |policy_name| from user policies.
-  std::vector<std::string> FromPrefs(const std::string& policy_name) {
-    return ConvertToVector(profile_->GetPrefs()->GetList(policy_name));
-  }
-
-  // Checks if given policy is set and if it is a dictionary
-  bool PolicyWithDataIsSet(const char* policy_name) {
-    policy::ProfilePolicyConnector* policy_connector =
-        policy::ProfilePolicyConnectorFactory::GetForBrowserContext(profile_);
-    if (!policy_connector) {
-      // something is wrong
-      return false;
-    }
-    const policy::PolicyNamespace policy_namespace =
-        policy::PolicyNamespace(policy::PolicyDomain::POLICY_DOMAIN_CHROME, "");
-    const policy::PolicyMap& policy_map =
-        policy_connector->policy_service()->GetPolicies(policy_namespace);
-    const base::Value* value = policy_map.GetValue(policy_name);
-    if (value && value->is_dict()) {
-      // policy is set and its value is a dictionary
-      return true;
-    }
-    return false;
-  }
-
-  // current partial results
-  std::unordered_map<std::string, Printer> recommended_printers_;
-  bool device_printers_is_complete_ = true;
-  bool user_printers_is_complete_ = true;
-
-  // current final results
-  bool complete_ = false;
-  std::unordered_map<std::string, Printer> printers_;
-
-  // Calculators for bulk printers from device and user policies. Unowned.
-  base::WeakPtr<BulkPrintersCalculator> device_printers_;
-  base::WeakPtr<BulkPrintersCalculator> user_printers_;
-
-  // Policies binder (bridge between policies and calculators). Owned.
-  std::unique_ptr<CalculatorsPoliciesBinder> policies_binder_;
-
-  // Profile (user) settings.
-  Profile* profile_;
-  PrefChangeRegistrar pref_change_registrar_;
-
-  base::ObserverList<EnterprisePrintersProvider::Observer>::Unchecked
-      observers_;
-  SEQUENCE_CHECKER(sequence_checker_);
-  DISALLOW_COPY_AND_ASSIGN(EnterprisePrintersProviderImpl);
-};
-
-}  // namespace
-
-// static
-void EnterprisePrintersProvider::RegisterProfilePrefs(
-    user_prefs::PrefRegistrySyncable* registry) {
-  registry->RegisterListPref(prefs::kRecommendedNativePrinters);
-  CalculatorsPoliciesBinder::RegisterProfilePrefs(registry);
-}
-
-// static
-std::unique_ptr<EnterprisePrintersProvider> EnterprisePrintersProvider::Create(
-    CrosSettings* settings,
-    Profile* profile) {
-  return std::make_unique<EnterprisePrintersProviderImpl>(settings, profile);
-}
-
-}  // namespace chromeos
diff --git a/chrome/browser/chromeos/printing/enterprise_printers_provider.h b/chrome/browser/chromeos/printing/enterprise_printers_provider.h
deleted file mode 100644
index cdd4210f..0000000
--- a/chrome/browser/chromeos/printing/enterprise_printers_provider.h
+++ /dev/null
@@ -1,61 +0,0 @@
-// Copyright 2018 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CHROME_BROWSER_CHROMEOS_PRINTING_ENTERPRISE_PRINTERS_PROVIDER_H_
-#define CHROME_BROWSER_CHROMEOS_PRINTING_ENTERPRISE_PRINTERS_PROVIDER_H_
-
-#include <memory>
-#include <string>
-#include <unordered_map>
-
-#include "base/macros.h"
-#include "chrome/browser/chromeos/settings/cros_settings.h"
-#include "chrome/browser/profiles/profile.h"
-#include "chromeos/printing/printer_configuration.h"
-#include "components/pref_registry/pref_registry_syncable.h"
-
-namespace chromeos {
-
-// Uses classes BulkPrintersCalculator and CalculatorsPoliciesBinder to track
-// device settings & user profile modifications and to calculates resultant
-// list of available enterprise printers. The final list of available enterprise
-// printers is propagated to Observers.
-// All methods must be called from the same sequence (UI) and all observers'
-// notifications will be called from this sequence.
-class EnterprisePrintersProvider {
- public:
-  class Observer {
-   public:
-    // |complete| is true if all policies have been parsed and applied (even
-    // when parsing errors occurred), false means that a new list of available
-    // printers is being calculated. |printers| contains the current list of
-    // available printers: the map is indexed by printers ids. This
-    // notification is called when value of any of these two parameters changes.
-    virtual void OnPrintersChanged(
-        bool complete,
-        const std::unordered_map<std::string, Printer>& printers) = 0;
-  };
-
-  static void RegisterProfilePrefs(user_prefs::PrefRegistrySyncable* registry);
-
-  // |settings| is the source of device policies. |profile| is a user profile.
-  static std::unique_ptr<EnterprisePrintersProvider> Create(
-      CrosSettings* settings,
-      Profile* profile);
-  virtual ~EnterprisePrintersProvider() = default;
-
-  // This method also calls directly OnPrintersChanged(...) from |observer|.
-  virtual void AddObserver(Observer* observer) = 0;
-  virtual void RemoveObserver(Observer* observer) = 0;
-
- protected:
-  EnterprisePrintersProvider() = default;
-
- private:
-  DISALLOW_COPY_AND_ASSIGN(EnterprisePrintersProvider);
-};
-
-}  // namespace chromeos
-
-#endif  // CHROME_BROWSER_CHROMEOS_PRINTING_ENTERPRISE_PRINTERS_PROVIDER_H_
diff --git a/chrome/browser/chromeos/printing/external_printers.cc b/chrome/browser/chromeos/printing/external_printers.cc
new file mode 100644
index 0000000..eee26f31
--- /dev/null
+++ b/chrome/browser/chromeos/printing/external_printers.cc
@@ -0,0 +1,374 @@
+// 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/chromeos/printing/external_printers.h"
+
+#include <set>
+
+#include "base/bind.h"
+#include "base/feature_list.h"
+#include "base/json/json_reader.h"
+#include "base/memory/weak_ptr.h"
+#include "base/observer_list.h"
+#include "base/sequence_checker.h"
+#include "base/sequenced_task_runner.h"
+#include "base/stl_util.h"
+#include "base/task/post_task.h"
+#include "base/task_runner_util.h"
+#include "base/threading/scoped_blocking_call.h"
+#include "base/values.h"
+#include "chrome/common/chrome_features.h"
+#include "chromeos/printing/printer_translator.h"
+
+namespace chromeos {
+
+namespace {
+
+constexpr int kMaxRecords = 20000;
+
+using PrinterCache = std::vector<std::unique_ptr<Printer>>;
+using PrinterView = std::map<const std::string, const Printer>;
+
+// Parses |data|, a JSON blob, into a vector of Printers.  If |data| cannot be
+// parsed, returns nullptr.  This is run off the UI thread as it could be very
+// slow.
+std::unique_ptr<PrinterCache> ParsePrinters(std::unique_ptr<std::string> data) {
+  int error_code = 0;
+  int error_line = 0;
+
+  // This could be really slow.
+  base::ScopedBlockingCall scoped_blocking_call(FROM_HERE,
+                                                base::BlockingType::MAY_BLOCK);
+  std::unique_ptr<base::Value> json_blob =
+      base::JSONReader::ReadAndReturnErrorDeprecated(
+          *data, base::JSONParserOptions::JSON_PARSE_RFC, &error_code,
+          nullptr /* error_msg_out */, &error_line);
+  // It's not valid JSON.  Give up.
+  if (!json_blob || !json_blob->is_list()) {
+    LOG(WARNING) << "Failed to parse printers policy (" << error_code
+                 << ") on line " << error_line;
+    return nullptr;
+  }
+
+  const base::Value::ListStorage& printer_list = json_blob->GetList();
+  if (printer_list.size() > kMaxRecords) {
+    LOG(WARNING) << "Too many records in printers policy: "
+                 << printer_list.size();
+    return nullptr;
+  }
+
+  auto parsed_printers = std::make_unique<PrinterCache>();
+  parsed_printers->reserve(printer_list.size());
+  for (const base::Value& val : printer_list) {
+    // TODO(skau): Convert to the new Value APIs.
+    const base::DictionaryValue* printer_dict;
+    if (!val.GetAsDictionary(&printer_dict)) {
+      LOG(WARNING) << "Entry in printers policy skipped.  Not a dictionary.";
+      continue;
+    }
+
+    auto printer = RecommendedPrinterToPrinter(*printer_dict);
+    if (!printer) {
+      LOG(WARNING) << "Failed to parse printer configuration.  Skipped.";
+      continue;
+    }
+    parsed_printers->push_back(std::move(printer));
+  }
+
+  return parsed_printers;
+}
+
+// Computes the effective printer list using the access mode and
+// blacklist/whitelist.  Methods are required to be sequenced.  This object is
+// the owner of all the policy data.
+class Restrictions {
+ public:
+  Restrictions() : printers_cache_(nullptr) {
+    DETACH_FROM_SEQUENCE(sequence_checker_);
+  }
+  ~Restrictions() { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); }
+
+  // Sets the printer cache using the policy blob |data|.  If the policy can be
+  // computed, returns the computed list.  Otherwise, nullptr.
+  std::unique_ptr<PrinterView> SetData(std::unique_ptr<std::string> data) {
+    DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+    base::ScopedBlockingCall scoped_blocking_call(
+        FROM_HERE, base::BlockingType::MAY_BLOCK);
+    printers_cache_ = ParsePrinters(std::move(data));
+    return ComputePrinters();
+  }
+
+  // Clear the printer cache.  Computed lists will be invalid until we receive
+  // new data.
+  void ClearData() {
+    DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+    printers_cache_.reset();
+  }
+
+  // Sets the access mode to |mode|.  If the policy can be computed, returns the
+  // computed list.  Otherwise, nullptr.
+  std::unique_ptr<PrinterView> UpdateAccessMode(
+      ExternalPrinters::AccessMode mode) {
+    DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+    mode_ = mode;
+    return ComputePrinters();
+  }
+
+  // Sets the blacklist to |blacklist|.  If the policy can be computed, returns
+  // the computed list. Otherwise, nullptr.
+  std::unique_ptr<PrinterView> UpdateBlacklist(
+      const std::vector<std::string>& blacklist) {
+    DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+    has_blacklist_ = true;
+    blacklist_ = std::set<std::string>(blacklist.begin(), blacklist.end());
+    return ComputePrinters();
+  }
+
+  // Sets the whitelist to |whitelist|.  If the policy can be computed, returns
+  // the computed list.  Otherwise, nullptr.
+  std::unique_ptr<PrinterView> UpdateWhitelist(
+      const std::vector<std::string>& whitelist) {
+    DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+    has_whitelist_ = true;
+    whitelist_ = std::set<std::string>(whitelist.begin(), whitelist.end());
+    return ComputePrinters();
+  }
+
+ private:
+  // Returns true if we have enough data to compute the effective printer list.
+  bool IsReady() const {
+    DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+    if (!printers_cache_) {
+      return false;
+    }
+
+    switch (mode_) {
+      case ExternalPrinters::AccessMode::ALL_ACCESS:
+        return true;
+      case ExternalPrinters::AccessMode::BLACKLIST_ONLY:
+        return has_blacklist_;
+      case ExternalPrinters::AccessMode::WHITELIST_ONLY:
+        return has_whitelist_;
+      case ExternalPrinters::AccessMode::UNSET:
+        return false;
+    }
+    NOTREACHED();
+    return false;
+  }
+
+  // Returns the effective printer list based on |mode_| from the entries in
+  // |printers_cache_|.
+  std::unique_ptr<PrinterView> ComputePrinters() {
+    DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+
+    if (!IsReady()) {
+      return nullptr;
+    }
+
+    auto view = std::make_unique<PrinterView>();
+    switch (mode_) {
+      case ExternalPrinters::UNSET:
+        NOTREACHED();
+        break;
+      case ExternalPrinters::WHITELIST_ONLY:
+        for (const auto& printer : *printers_cache_) {
+          if (base::ContainsKey(whitelist_, printer->id())) {
+            view->insert({printer->id(), *printer});
+          }
+        }
+        break;
+      case ExternalPrinters::BLACKLIST_ONLY:
+        for (const auto& printer : *printers_cache_) {
+          if (!base::ContainsKey(blacklist_, printer->id())) {
+            view->insert({printer->id(), *printer});
+          }
+        }
+        break;
+      case ExternalPrinters::ALL_ACCESS:
+        for (const auto& printer : *printers_cache_) {
+          view->insert({printer->id(), *printer});
+        }
+        break;
+    }
+
+    return view;
+  }
+
+  // Cache of the parsed printer configuration file.
+  std::unique_ptr<PrinterCache> printers_cache_;
+
+  // The type of restriction which is enforced.
+  ExternalPrinters::AccessMode mode_ = ExternalPrinters::UNSET;
+  // The list of ids which should not appear in the final list.
+  bool has_blacklist_ = false;
+  std::set<std::string> blacklist_;
+  // The list of the only ids which should appear in the final list.
+  bool has_whitelist_ = false;
+  std::set<std::string> whitelist_;
+
+  SEQUENCE_CHECKER(sequence_checker_);
+  DISALLOW_COPY_AND_ASSIGN(Restrictions);
+};
+
+class ExternalPrintersImpl : public ExternalPrinters {
+ public:
+  ExternalPrintersImpl()
+      : restrictions_(std::make_unique<Restrictions>()),
+        restrictions_runner_(base::CreateSequencedTaskRunnerWithTraits(
+            {base::TaskPriority::BEST_EFFORT, base::MayBlock(),
+             base::TaskShutdownBehavior::SKIP_ON_SHUTDOWN})),
+        weak_ptr_factory_(this) {}
+  ~ExternalPrintersImpl() override {
+    bool success =
+        restrictions_runner_->DeleteSoon(FROM_HERE, std::move(restrictions_));
+    if (!success) {
+      LOG(WARNING) << "Unable to schedule deletion of policy object.";
+    }
+  }
+
+  void AddObserver(Observer* observer) override {
+    DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+    observers_.AddObserver(observer);
+  }
+
+  void RemoveObserver(Observer* observer) override {
+    DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+    observers_.RemoveObserver(observer);
+  }
+
+  // Resets the printer state fields.
+  void ClearData() override {
+    DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+    if (!base::FeatureList::IsEnabled(features::kBulkPrinters)) {
+      return;
+    }
+
+    // Update restrictions then clear our local cache on return so we don't get
+    // out of sequence.
+    restrictions_runner_->PostTaskAndReply(
+        FROM_HERE,
+        base::BindOnce(&Restrictions::ClearData,
+                       base::Unretained(restrictions_.get())),
+        base::BindOnce(&ExternalPrintersImpl::OnComputationComplete,
+                       weak_ptr_factory_.GetWeakPtr(), nullptr));
+  }
+
+  void SetData(std::unique_ptr<std::string> data) override {
+    DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+    if (!base::FeatureList::IsEnabled(features::kBulkPrinters)) {
+      return;
+    }
+
+    if (!data) {
+      LOG(WARNING) << "Received null data";
+      return;
+    }
+
+    // Forward data to Restrictions for computation.
+    base::PostTaskAndReplyWithResult(
+        restrictions_runner_.get(), FROM_HERE,
+        base::BindOnce(&Restrictions::SetData,
+                       base::Unretained(restrictions_.get()), std::move(data)),
+        base::BindOnce(&ExternalPrintersImpl::OnComputationComplete,
+                       weak_ptr_factory_.GetWeakPtr()));
+  }
+
+  void SetAccessMode(AccessMode mode) override {
+    DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+    base::PostTaskAndReplyWithResult(
+        restrictions_runner_.get(), FROM_HERE,
+        base::BindOnce(&Restrictions::UpdateAccessMode,
+                       base::Unretained(restrictions_.get()), mode),
+        base::BindOnce(&ExternalPrintersImpl::OnComputationComplete,
+                       weak_ptr_factory_.GetWeakPtr()));
+  }
+
+  void SetBlacklist(const std::vector<std::string>& blacklist) override {
+    DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+    base::PostTaskAndReplyWithResult(
+        restrictions_runner_.get(), FROM_HERE,
+        base::BindOnce(&Restrictions::UpdateBlacklist,
+                       base::Unretained(restrictions_.get()), blacklist),
+        base::BindOnce(&ExternalPrintersImpl::OnComputationComplete,
+                       weak_ptr_factory_.GetWeakPtr()));
+  }
+
+  void SetWhitelist(const std::vector<std::string>& whitelist) override {
+    DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+    base::PostTaskAndReplyWithResult(
+        restrictions_runner_.get(), FROM_HERE,
+        base::BindOnce(&Restrictions::UpdateWhitelist,
+                       base::Unretained(restrictions_.get()), whitelist),
+        base::BindOnce(&ExternalPrintersImpl::OnComputationComplete,
+                       weak_ptr_factory_.GetWeakPtr()));
+  }
+
+  bool IsPolicySet() const override {
+    DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+    return received_data_;
+  }
+
+  const std::map<const std::string, const Printer>& GetPrinters()
+      const override {
+    DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+    return printers_;
+  }
+
+ private:
+  // Called on computation completion.  |view| is the computed printers which a
+  // user should be able to see.  If |view| is nullptr, it's taken to mean that
+  // the list is now invalid and will be cleared.
+  void OnComputationComplete(std::unique_ptr<PrinterView> view) {
+    DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+    bool valid;
+    if (!view) {
+      // Printers are dropped if parsing failed.  We can no longer determine
+      // what the domain owner wanted.
+      printers_.clear();
+      valid = false;
+    } else {
+      printers_.swap(*view);
+      valid = true;
+    }
+
+    // Maybe notify that the computed list has changed.
+    // Do not notify for invalid->invalid transitions
+    if (!valid && !received_data_) {
+      return;
+    }
+
+    received_data_ = valid;
+    for (auto& observer : observers_) {
+      // We rely on the assumption that this is sequenced with the rest of our
+      // code to guarantee that printers_ remains valid.
+      observer.OnPrintersChanged(received_data_, printers_);
+    }
+  }
+
+  // Holds the blacklist and whitelist.  Computes the effective printer list.
+  std::unique_ptr<Restrictions> restrictions_;
+  // Off UI sequence for computing the printer view.
+  scoped_refptr<base::SequencedTaskRunner> restrictions_runner_;
+
+  // True if printers_ is based on a current policy.
+  bool received_data_ = false;
+  // The computed set of printers.
+  PrinterView printers_;
+
+  base::ObserverList<ExternalPrinters::Observer>::Unchecked observers_;
+
+  SEQUENCE_CHECKER(sequence_checker_);
+  base::WeakPtrFactory<ExternalPrintersImpl> weak_ptr_factory_;
+
+  DISALLOW_COPY_AND_ASSIGN(ExternalPrintersImpl);
+};
+
+}  // namespace
+
+// static
+std::unique_ptr<ExternalPrinters> ExternalPrinters::Create() {
+  return std::make_unique<ExternalPrintersImpl>();
+}
+
+}  // namespace chromeos
diff --git a/chrome/browser/chromeos/printing/external_printers.h b/chrome/browser/chromeos/printing/external_printers.h
new file mode 100644
index 0000000..81f0bb5
--- /dev/null
+++ b/chrome/browser/chromeos/printing/external_printers.h
@@ -0,0 +1,85 @@
+// 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_CHROMEOS_PRINTING_EXTERNAL_PRINTERS_H_
+#define CHROME_BROWSER_CHROMEOS_PRINTING_EXTERNAL_PRINTERS_H_
+
+#include <map>
+#include <memory>
+#include <string>
+#include <utility>
+#include <vector>
+
+#include "base/memory/weak_ptr.h"
+#include "chromeos/printing/printer_configuration.h"
+
+namespace chromeos {
+
+// Manages download and parsing of the external policy printer configuration and
+// enforces restrictions.
+class ExternalPrinters : public base::SupportsWeakPtr<ExternalPrinters> {
+ public:
+  // Choose the policy for printer access.
+  enum AccessMode {
+    UNSET = -1,
+    // Printers in the blacklist are disallowed.  Others are allowed.
+    BLACKLIST_ONLY = 0,
+    // Only printers in the whitelist are allowed.
+    WHITELIST_ONLY = 1,
+    // All printers in the policy are allowed.
+    ALL_ACCESS = 2
+  };
+
+  // Observer is notified when the computed set of printers change.  It is
+  // assumed that the observer is on the same sequence as the object it is
+  // observing.
+  class Observer {
+   public:
+    // Called when the printers have changed and should be queried.  |valid| is
+    // true if |printers| is based on a valid policy.  |printers| are the
+    // printers that should be available to the user.
+    virtual void OnPrintersChanged(
+        bool valid,
+        const std::map<const std::string, const Printer>& printers) = 0;
+  };
+
+  // Creates a handler for the external printer policies.
+  static std::unique_ptr<ExternalPrinters> Create();
+
+  virtual ~ExternalPrinters() = default;
+
+  virtual void AddObserver(Observer* observer) = 0;
+  virtual void RemoveObserver(Observer* observer) = 0;
+
+  // Parses |data| which is the contents of the bulk printes file and extracts
+  // printer information.  The file format is assumed to be JSON.
+  virtual void SetData(std::unique_ptr<std::string> data) = 0;
+  // Removes all printer data and invalidates the configuration.
+  virtual void ClearData() = 0;
+
+  // Set the access mode which chooses the type of filtering that is performed.
+  virtual void SetAccessMode(AccessMode mode) = 0;
+  // Set the |blacklist| which excludes printers with the given id if access
+  // mode is BLACKLIST_ONLY.
+  virtual void SetBlacklist(const std::vector<std::string>& blacklist) = 0;
+  // Set the |whitelist| which is the complete list of printers that are show to
+  // the user.  This is in effect if access mode is WHITELIST_ONLY.
+  virtual void SetWhitelist(const std::vector<std::string>& whitelist) = 0;
+
+  // Returns true if the printer map has been computed from a valid policy.
+  // Returns false otherwise.  This is computed asynchronously.  Observe
+  // OnPrintersChanged() to be notified when it is updated.  This may never
+  // become true if a user does not have the appropriate printer policies.
+  virtual bool IsPolicySet() const = 0;
+
+  // Returns a refernce to a map of the computed set of printers.  The map is
+  // empty if either the policy was empty or we are yet to receive the full
+  // policy. Use IsPolicySet() to differentiate betweeen the two.
+  virtual const std::map<const std::string, const Printer>& GetPrinters()
+      const = 0;
+};
+
+}  // namespace chromeos
+
+#endif  // CHROME_BROWSER_CHROMEOS_PRINTING_EXTERNAL_PRINTERS_H_
diff --git a/chrome/browser/chromeos/printing/external_printers_factory.cc b/chrome/browser/chromeos/printing/external_printers_factory.cc
new file mode 100644
index 0000000..b4f311d
--- /dev/null
+++ b/chrome/browser/chromeos/printing/external_printers_factory.cc
@@ -0,0 +1,65 @@
+// 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/chromeos/printing/external_printers_factory.h"
+
+#include <memory>
+
+#include "base/lazy_instance.h"
+#include "chrome/browser/chromeos/printing/external_printers.h"
+#include "chrome/browser/chromeos/profiles/profile_helper.h"
+#include "chrome/browser/profiles/profile.h"
+#include "components/user_manager/user.h"
+
+namespace chromeos {
+
+namespace {
+
+base::LazyInstance<ExternalPrintersFactory>::DestructorAtExit
+    g_printers_factory = LAZY_INSTANCE_INITIALIZER;
+
+}  // namespace
+
+// static
+ExternalPrintersFactory* ExternalPrintersFactory::Get() {
+  return g_printers_factory.Pointer();
+}
+
+base::WeakPtr<ExternalPrinters> ExternalPrintersFactory::GetForAccountId(
+    const AccountId& account_id) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  auto found = printers_by_user_.find(account_id);
+  if (found != printers_by_user_.end()) {
+    return found->second->AsWeakPtr();
+  }
+
+  printers_by_user_[account_id] = ExternalPrinters::Create();
+  return printers_by_user_[account_id]->AsWeakPtr();
+}
+
+base::WeakPtr<ExternalPrinters> ExternalPrintersFactory::GetForProfile(
+    Profile* profile) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  const user_manager::User* user =
+      ProfileHelper::Get()->GetUserByProfile(profile);
+  if (!user)
+    return nullptr;
+
+  return GetForAccountId(user->GetAccountId());
+}
+
+void ExternalPrintersFactory::RemoveForUserId(const AccountId& account_id) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  printers_by_user_.erase(account_id);
+}
+
+void ExternalPrintersFactory::Shutdown() {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  printers_by_user_.clear();
+}
+
+ExternalPrintersFactory::ExternalPrintersFactory() = default;
+ExternalPrintersFactory::~ExternalPrintersFactory() = default;
+
+}  // namespace chromeos
diff --git a/chrome/browser/chromeos/printing/external_printers_factory.h b/chrome/browser/chromeos/printing/external_printers_factory.h
new file mode 100644
index 0000000..c86d990
--- /dev/null
+++ b/chrome/browser/chromeos/printing/external_printers_factory.h
@@ -0,0 +1,59 @@
+// 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_CHROMEOS_PRINTING_EXTERNAL_PRINTERS_FACTORY_H_
+#define CHROME_BROWSER_CHROMEOS_PRINTING_EXTERNAL_PRINTERS_FACTORY_H_
+
+#include <map>
+#include <memory>
+
+#include "base/lazy_instance.h"
+#include "base/macros.h"
+#include "base/memory/weak_ptr.h"
+#include "base/sequence_checker.h"
+#include "chrome/browser/chromeos/printing/external_printers.h"
+#include "components/account_id/account_id.h"
+
+class Profile;
+
+namespace chromeos {
+
+// Dispenses ExternalPrinters objects based on account id.  Access to this
+// object should be sequenced.
+class ExternalPrintersFactory {
+ public:
+  static ExternalPrintersFactory* Get();
+
+  // Returns a WeakPtr to the ExternalPrinters registered for |account_id|. If
+  // an ExternalPrinters does not exist, one will be created for |account_id|.
+  // The returned object remains valid until RemoveForUserId or Shutdown is
+  // called.
+  base::WeakPtr<ExternalPrinters> GetForAccountId(const AccountId& account_id);
+
+  // Returns a WeakPtr to the ExternalPrinters registered for |profile| which
+  // could be null if |profile| does not map to a valid AccountId. The returned
+  // object remains valid until RemoveForUserId or Shutdown is called.
+  base::WeakPtr<ExternalPrinters> GetForProfile(Profile* profile);
+
+  // Deletes the ExternalPrinters registered for |account_id|.
+  void RemoveForUserId(const AccountId& account_id);
+
+  // Tear down all ExternalPrinters.
+  void Shutdown();
+
+ private:
+  friend struct base::LazyInstanceTraitsBase<ExternalPrintersFactory>;
+
+  ExternalPrintersFactory();
+  ~ExternalPrintersFactory();
+
+  std::map<AccountId, std::unique_ptr<ExternalPrinters>> printers_by_user_;
+  SEQUENCE_CHECKER(sequence_checker_);
+
+  DISALLOW_COPY_AND_ASSIGN(ExternalPrintersFactory);
+};
+
+}  // namespace chromeos
+
+#endif  // CHROME_BROWSER_CHROMEOS_PRINTING_EXTERNAL_PRINTERS_FACTORY_H_
diff --git a/chrome/browser/chromeos/printing/external_printers_policies.h b/chrome/browser/chromeos/printing/external_printers_policies.h
new file mode 100644
index 0000000..2b895ab4
--- /dev/null
+++ b/chrome/browser/chromeos/printing/external_printers_policies.h
@@ -0,0 +1,17 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_CHROMEOS_PRINTING_EXTERNAL_PRINTERS_POLICIES_H_
+#define CHROME_BROWSER_CHROMEOS_PRINTING_EXTERNAL_PRINTERS_POLICIES_H_
+
+#include <string>
+
+// A collection of preference names representing the external printer fields.
+struct ExternalPrinterPolicies {
+  std::string access_mode;
+  std::string blacklist;
+  std::string whitelist;
+};
+
+#endif  // CHROME_BROWSER_CHROMEOS_PRINTING_EXTERNAL_PRINTERS_POLICIES_H_
diff --git a/chrome/browser/chromeos/printing/external_printers_pref_bridge.cc b/chrome/browser/chromeos/printing/external_printers_pref_bridge.cc
new file mode 100644
index 0000000..237ac8d
--- /dev/null
+++ b/chrome/browser/chromeos/printing/external_printers_pref_bridge.cc
@@ -0,0 +1,112 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/chromeos/printing/external_printers_pref_bridge.h"
+
+#include <string>
+#include <vector>
+
+#include "base/bind.h"
+#include "base/values.h"
+#include "chrome/browser/chromeos/printing/external_printers.h"
+#include "chrome/browser/chromeos/printing/external_printers_factory.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/common/pref_names.h"
+#include "components/policy/policy_constants.h"
+#include "components/pref_registry/pref_registry_syncable.h"
+#include "components/prefs/pref_service.h"
+
+namespace chromeos {
+
+namespace {
+
+// Extracts the list of strings named |policy_name| from |prefs| and returns it.
+std::vector<std::string> FromPrefs(const PrefService* prefs,
+                                   const std::string& policy_name) {
+  std::vector<std::string> string_list;
+  const base::ListValue* list = prefs->GetList(policy_name);
+  for (const base::Value& value : *list) {
+    if (value.is_string()) {
+      string_list.push_back(value.GetString());
+    }
+  }
+
+  return string_list;
+}
+
+}  // namespace
+
+// static
+void ExternalPrintersPrefBridge::RegisterProfilePrefs(
+    user_prefs::PrefRegistrySyncable* registry,
+    const ExternalPrinterPolicies& policies) {
+  // Default value for access mode is AllAccess.
+  registry->RegisterIntegerPref(policies.access_mode,
+                                ExternalPrinters::ALL_ACCESS);
+  registry->RegisterListPref(policies.blacklist);
+  registry->RegisterListPref(policies.whitelist);
+}
+
+ExternalPrintersPrefBridge::ExternalPrintersPrefBridge(
+    const ExternalPrinterPolicies& policies,
+    Profile* profile)
+    : profile_(profile), policies_(policies) {
+  pref_change_registrar_.Init(profile_->GetPrefs());
+
+  pref_change_registrar_.Add(
+      policies_.access_mode,
+      base::BindRepeating(&ExternalPrintersPrefBridge::AccessModeUpdated,
+                          base::Unretained(this)));
+  pref_change_registrar_.Add(
+      policies_.blacklist,
+      base::BindRepeating(&ExternalPrintersPrefBridge::BlacklistUpdated,
+                          base::Unretained(this)));
+  pref_change_registrar_.Add(
+      policies_.whitelist,
+      base::BindRepeating(&ExternalPrintersPrefBridge::WhitelistUpdated,
+                          base::Unretained(this)));
+  Initialize();
+}
+
+void ExternalPrintersPrefBridge::Initialize() {
+  BlacklistUpdated();
+  WhitelistUpdated();
+  AccessModeUpdated();
+}
+
+void ExternalPrintersPrefBridge::AccessModeUpdated() {
+  const PrefService* prefs = profile_->GetPrefs();
+  ExternalPrinters::AccessMode mode = ExternalPrinters::UNSET;
+  int mode_val = prefs->GetInteger(policies_.access_mode);
+  if (mode_val >= ExternalPrinters::BLACKLIST_ONLY &&
+      mode_val <= ExternalPrinters::ALL_ACCESS) {
+    mode = static_cast<ExternalPrinters::AccessMode>(mode_val);
+  } else {
+    LOG(ERROR) << "Unrecognized access mode";
+    return;
+  }
+
+  base::WeakPtr<ExternalPrinters> printers =
+      ExternalPrintersFactory::Get()->GetForProfile(profile_);
+  if (printers)
+    printers->SetAccessMode(mode);
+}
+
+void ExternalPrintersPrefBridge::BlacklistUpdated() {
+  base::WeakPtr<ExternalPrinters> printers =
+      ExternalPrintersFactory::Get()->GetForProfile(profile_);
+  if (printers)
+    printers->SetBlacklist(
+        FromPrefs(profile_->GetPrefs(), policies_.blacklist));
+}
+
+void ExternalPrintersPrefBridge::WhitelistUpdated() {
+  base::WeakPtr<ExternalPrinters> printers =
+      ExternalPrintersFactory::Get()->GetForProfile(profile_);
+  if (printers)
+    printers->SetWhitelist(
+        FromPrefs(profile_->GetPrefs(), policies_.whitelist));
+}
+
+}  // namespace chromeos
diff --git a/chrome/browser/chromeos/printing/external_printers_pref_bridge.h b/chrome/browser/chromeos/printing/external_printers_pref_bridge.h
new file mode 100644
index 0000000..b7041109
--- /dev/null
+++ b/chrome/browser/chromeos/printing/external_printers_pref_bridge.h
@@ -0,0 +1,55 @@
+// 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_CHROMEOS_PRINTING_EXTERNAL_PRINTERS_PREF_BRIDGE_H_
+#define CHROME_BROWSER_CHROMEOS_PRINTING_EXTERNAL_PRINTERS_PREF_BRIDGE_H_
+
+#include <string>
+
+#include "base/macros.h"
+#include "chrome/browser/chromeos/printing/external_printers_policies.h"
+#include "components/prefs/pref_change_registrar.h"
+
+class Profile;
+
+namespace user_prefs {
+class PrefRegistrySyncable;
+}
+
+namespace chromeos {
+
+class ExternalPrinters;
+
+// Observe preference changes and propogate changes to ExternalPrinters.
+class ExternalPrintersPrefBridge {
+ public:
+  static void RegisterProfilePrefs(user_prefs::PrefRegistrySyncable* registry,
+                                   const ExternalPrinterPolicies& policies);
+
+  ExternalPrintersPrefBridge(const ExternalPrinterPolicies& policies,
+                             Profile* profile);
+
+ private:
+  // Retrieve initial values for preferences.
+  void Initialize();
+
+  // Handle update for the access mode policy.
+  void AccessModeUpdated();
+
+  // Handle updates for the blacklist policy.
+  void BlacklistUpdated();
+
+  // Handle updates for the whitelist policy.
+  void WhitelistUpdated();
+
+  Profile* profile_;
+  const ExternalPrinterPolicies policies_;
+  PrefChangeRegistrar pref_change_registrar_;
+
+  DISALLOW_COPY_AND_ASSIGN(ExternalPrintersPrefBridge);
+};
+
+}  // namespace chromeos
+
+#endif  // CHROME_BROWSER_CHROMEOS_PRINTING_EXTERNAL_PRINTERS_PREF_BRIDGE_H_
diff --git a/chrome/browser/chromeos/printing/bulk_printers_calculator_unittest.cc b/chrome/browser/chromeos/printing/external_printers_unittest.cc
similarity index 67%
rename from chrome/browser/chromeos/printing/bulk_printers_calculator_unittest.cc
rename to chrome/browser/chromeos/printing/external_printers_unittest.cc
index 7b3e7904..35bea3e 100644
--- a/chrome/browser/chromeos/printing/bulk_printers_calculator_unittest.cc
+++ b/chrome/browser/chromeos/printing/external_printers_unittest.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chrome/browser/chromeos/printing/bulk_printers_calculator.h"
+#include "chrome/browser/chromeos/printing/external_printers.h"
 
 #include <string>
 #include <vector>
@@ -77,10 +77,13 @@
 ])json";
 
 // Observer that counts the number of times it has been called.
-class TestObserver : public BulkPrintersCalculator::Observer {
+class TestObserver : public ExternalPrinters::Observer {
  public:
-  void OnPrintersChanged(const BulkPrintersCalculator* sender) override {
-    last_valid = sender->IsComplete();
+  void OnPrintersChanged(
+      bool valid,
+      const std::map<const std::string, const Printer>& /* printers */)
+      override {
+    last_valid = valid;
     called++;
   }
 
@@ -90,20 +93,20 @@
   bool last_valid = false;
 };
 
-class BulkPrintersCalculatorTest : public testing::Test {
+class ExternalPrintersTest : public testing::Test {
  public:
-  BulkPrintersCalculatorTest() : scoped_task_environment_() {
+  ExternalPrintersTest() : scoped_task_environment_() {
     scoped_feature_list_.InitAndEnableFeature(
         base::Feature(features::kBulkPrinters));
-    external_printers_ = BulkPrintersCalculator::Create();
+    external_printers_ = ExternalPrinters::Create();
   }
-  ~BulkPrintersCalculatorTest() override {
+  ~ExternalPrintersTest() override {
     // Delete the printer before the task environment.
     external_printers_.reset();
   }
 
  protected:
-  std::unique_ptr<BulkPrintersCalculator> external_printers_;
+  std::unique_ptr<ExternalPrinters> external_printers_;
   base::test::ScopedTaskEnvironment scoped_task_environment_;
 
  private:
@@ -111,17 +114,16 @@
 };
 
 // Verify that we're initiall unset and empty.
-TEST_F(BulkPrintersCalculatorTest, InitialConditions) {
-  EXPECT_FALSE(external_printers_->IsDataPolicySet());
+TEST_F(ExternalPrintersTest, InitialConditions) {
+  EXPECT_FALSE(external_printers_->IsPolicySet());
   EXPECT_TRUE(external_printers_->GetPrinters().empty());
 }
 
 // Verify that the object can be destroyed while parsing is in progress.
-TEST_F(BulkPrintersCalculatorTest, DestructionIsSafe) {
+TEST_F(ExternalPrintersTest, DestructionIsSafe) {
   {
-    std::unique_ptr<BulkPrintersCalculator> printers =
-        BulkPrintersCalculator::Create();
-    printers->SetAccessMode(BulkPrintersCalculator::BLACKLIST_ONLY);
+    std::unique_ptr<ExternalPrinters> printers = ExternalPrinters::Create();
+    printers->SetAccessMode(ExternalPrinters::BLACKLIST_ONLY);
     printers->SetBlacklist({"Third"});
     printers->SetData(std::make_unique<std::string>(kBulkPolicyContentsJson));
     // Data is valid.  Computation is proceeding.
@@ -131,28 +133,51 @@
   scoped_task_environment_.RunUntilIdle();
 }
 
-// Verifies that IsDataPolicySet returns false until data is set.
-TEST_F(BulkPrintersCalculatorTest, PolicyUnsetWithMissingData) {
+// Verifies that all IsPolicySet returns false until all necessary data is set.
+TEST_F(ExternalPrintersTest, PolicyUnsetWithMissingData) {
   auto data = std::make_unique<std::string>(kBulkPolicyContentsJson);
   external_printers_->ClearData();
-  EXPECT_FALSE(external_printers_->IsDataPolicySet());
   external_printers_->SetData(std::move(data));
-  EXPECT_TRUE(external_printers_->IsDataPolicySet());
+
+  // Waiting for AccessMode.
   scoped_task_environment_.RunUntilIdle();
-  EXPECT_TRUE(external_printers_->IsComplete());
+  EXPECT_FALSE(external_printers_->IsPolicySet());
+
+  external_printers_->SetAccessMode(ExternalPrinters::AccessMode::ALL_ACCESS);
+  scoped_task_environment_.RunUntilIdle();
+  EXPECT_TRUE(external_printers_->IsPolicySet());
+
+  external_printers_->SetAccessMode(
+      ExternalPrinters::AccessMode::WHITELIST_ONLY);
+  scoped_task_environment_.RunUntilIdle();
+  EXPECT_FALSE(external_printers_->IsPolicySet());  // Waiting for Whitelist.
+
+  std::vector<std::string> whitelist = {"First", "Third"};
+  external_printers_->SetWhitelist(whitelist);
+  scoped_task_environment_.RunUntilIdle();
+  EXPECT_TRUE(external_printers_->IsPolicySet());  // Everything is set.
+
+  external_printers_->SetAccessMode(
+      ExternalPrinters::AccessMode::BLACKLIST_ONLY);
+  scoped_task_environment_.RunUntilIdle();
+  EXPECT_FALSE(external_printers_->IsPolicySet());  // Blacklist needed now.
+
+  std::vector<std::string> blacklist = {"Second"};
+  external_printers_->SetBlacklist(blacklist);
+  scoped_task_environment_.RunUntilIdle();
+  EXPECT_TRUE(
+      external_printers_->IsPolicySet());  // Blacklist was set.  Ready again.
 }
 
 // Verify printer list after all attributes have been set.
-TEST_F(BulkPrintersCalculatorTest, AllPoliciesResultInPrinters) {
+TEST_F(ExternalPrintersTest, AllPoliciesResultInPrinters) {
   auto data = std::make_unique<std::string>(kBulkPolicyContentsJson);
-  external_printers_->SetAccessMode(
-      BulkPrintersCalculator::AccessMode::ALL_ACCESS);
+  external_printers_->SetAccessMode(ExternalPrinters::AccessMode::ALL_ACCESS);
   external_printers_->SetData(std::move(data));
-  EXPECT_TRUE(external_printers_->IsDataPolicySet());
 
   scoped_task_environment_.RunUntilIdle();
-  EXPECT_TRUE(external_printers_->IsComplete());
   const auto& printers = external_printers_->GetPrinters();
+  EXPECT_TRUE(external_printers_->IsPolicySet());
   EXPECT_EQ(kNumPrinters, printers.size());
   EXPECT_EQ("LexaPrint", printers.at("First").display_name());
   EXPECT_EQ("Color Laser", printers.at("Second").display_name());
@@ -160,34 +185,34 @@
 }
 
 // The external policy was cleared, results should be invalidated.
-TEST_F(BulkPrintersCalculatorTest, PolicyClearedNowUnset) {
+TEST_F(ExternalPrintersTest, PolicyClearedNowUnset) {
   auto data = std::make_unique<std::string>(kBulkPolicyContentsJson);
-  external_printers_->SetAccessMode(
-      BulkPrintersCalculator::AccessMode::ALL_ACCESS);
+  external_printers_->SetAccessMode(ExternalPrinters::AccessMode::ALL_ACCESS);
   external_printers_->ClearData();
   external_printers_->SetData(std::move(data));
 
   scoped_task_environment_.RunUntilIdle();
-  ASSERT_TRUE(external_printers_->IsDataPolicySet());
+  ASSERT_TRUE(external_printers_->IsPolicySet());
 
   external_printers_->ClearData();
   scoped_task_environment_.RunUntilIdle();
-  EXPECT_FALSE(external_printers_->IsDataPolicySet());
+  EXPECT_FALSE(external_printers_->IsPolicySet());
   EXPECT_TRUE(external_printers_->GetPrinters().empty());
 }
 
 // Verify that the blacklist policy is applied correctly.  Printers in the
 // blacklist policy should not be available.  Printers not in the blackslist
 // should be available.
-TEST_F(BulkPrintersCalculatorTest, BlacklistPolicySet) {
+TEST_F(ExternalPrintersTest, BlacklistPolicySet) {
   auto data = std::make_unique<std::string>(kBulkPolicyContentsJson);
   external_printers_->ClearData();
   external_printers_->SetData(std::move(data));
-  external_printers_->SetAccessMode(BulkPrintersCalculator::BLACKLIST_ONLY);
+  external_printers_->SetAccessMode(ExternalPrinters::BLACKLIST_ONLY);
   scoped_task_environment_.RunUntilIdle();
+  EXPECT_FALSE(external_printers_->IsPolicySet());
   external_printers_->SetBlacklist({"Second", "Third"});
   scoped_task_environment_.RunUntilIdle();
-  EXPECT_TRUE(external_printers_->IsComplete());
+  EXPECT_TRUE(external_printers_->IsPolicySet());
 
   scoped_task_environment_.RunUntilIdle();
   const auto& printers = external_printers_->GetPrinters();
@@ -197,76 +222,79 @@
 
 // Verify that the whitelist policy is correctly applied.  Only printers
 // available in the whitelist are available.
-TEST_F(BulkPrintersCalculatorTest, WhitelistPolicySet) {
+TEST_F(ExternalPrintersTest, WhitelistPolicySet) {
   auto data = std::make_unique<std::string>(kBulkPolicyContentsJson);
   external_printers_->ClearData();
   external_printers_->SetData(std::move(data));
-  external_printers_->SetAccessMode(BulkPrintersCalculator::WHITELIST_ONLY);
+  external_printers_->SetAccessMode(ExternalPrinters::WHITELIST_ONLY);
   scoped_task_environment_.RunUntilIdle();
+  EXPECT_FALSE(external_printers_->IsPolicySet());
   external_printers_->SetWhitelist({"First"});
 
   scoped_task_environment_.RunUntilIdle();
-  EXPECT_TRUE(external_printers_->IsComplete());
+  EXPECT_TRUE(external_printers_->IsPolicySet());
   const auto& printers = external_printers_->GetPrinters();
   EXPECT_EQ(1U, printers.size());
   EXPECT_EQ("LexaPrint", printers.at("First").display_name());
 }
 
 // Verify that an empty blacklist results in no printer limits.
-TEST_F(BulkPrintersCalculatorTest, EmptyBlacklistAllPrinters) {
+TEST_F(ExternalPrintersTest, EmptyBlacklistAllPrinters) {
   auto data = std::make_unique<std::string>(kBulkPolicyContentsJson);
   external_printers_->ClearData();
   external_printers_->SetData(std::move(data));
-  external_printers_->SetAccessMode(BulkPrintersCalculator::BLACKLIST_ONLY);
+  external_printers_->SetAccessMode(ExternalPrinters::BLACKLIST_ONLY);
   scoped_task_environment_.RunUntilIdle();
+  EXPECT_FALSE(external_printers_->IsPolicySet());
   external_printers_->SetBlacklist({});
 
   scoped_task_environment_.RunUntilIdle();
-  EXPECT_TRUE(external_printers_->IsComplete());
+  EXPECT_TRUE(external_printers_->IsPolicySet());
   const auto& printers = external_printers_->GetPrinters();
   EXPECT_EQ(kNumPrinters, printers.size());
 }
 
 // Verify that an empty whitelist results in no printers.
-TEST_F(BulkPrintersCalculatorTest, EmptyWhitelistNoPrinters) {
+TEST_F(ExternalPrintersTest, EmptyWhitelistNoPrinters) {
   auto data = std::make_unique<std::string>(kBulkPolicyContentsJson);
   external_printers_->ClearData();
   external_printers_->SetData(std::move(data));
-  external_printers_->SetAccessMode(BulkPrintersCalculator::WHITELIST_ONLY);
+  external_printers_->SetAccessMode(ExternalPrinters::WHITELIST_ONLY);
   scoped_task_environment_.RunUntilIdle();
+  EXPECT_FALSE(external_printers_->IsPolicySet());
   external_printers_->SetWhitelist({});
 
   scoped_task_environment_.RunUntilIdle();
-  EXPECT_TRUE(external_printers_->IsComplete());
+  EXPECT_TRUE(external_printers_->IsPolicySet());
   const auto& printers = external_printers_->GetPrinters();
   EXPECT_EQ(0U, printers.size());
 }
 
 // Verify that switching from whitelist to blacklist behaves correctly.
-TEST_F(BulkPrintersCalculatorTest, BlacklistToWhitelistSwap) {
+TEST_F(ExternalPrintersTest, BlacklistToWhitelistSwap) {
   auto data = std::make_unique<std::string>(kBulkPolicyContentsJson);
   external_printers_->ClearData();
   external_printers_->SetData(std::move(data));
-  external_printers_->SetAccessMode(BulkPrintersCalculator::BLACKLIST_ONLY);
+  external_printers_->SetAccessMode(ExternalPrinters::BLACKLIST_ONLY);
   external_printers_->SetWhitelist({"First"});
   external_printers_->SetBlacklist({"First"});
 
   // This should result in 2 printers.  But we're switching the mode anyway.
 
-  external_printers_->SetAccessMode(BulkPrintersCalculator::WHITELIST_ONLY);
+  external_printers_->SetAccessMode(ExternalPrinters::WHITELIST_ONLY);
   scoped_task_environment_.RunUntilIdle();
-  EXPECT_TRUE(external_printers_->IsComplete());
+  EXPECT_TRUE(external_printers_->IsPolicySet());
   const auto& printers = external_printers_->GetPrinters();
   EXPECT_EQ(1U, printers.size());
   EXPECT_EQ("LexaPrint", printers.at("First").display_name());
 }
 
 // Verify that updated configurations are handled properly.
-TEST_F(BulkPrintersCalculatorTest, MultipleUpdates) {
+TEST_F(ExternalPrintersTest, MultipleUpdates) {
   auto data = std::make_unique<std::string>(kBulkPolicyContentsJson);
   external_printers_->ClearData();
   external_printers_->SetData(std::move(data));
-  external_printers_->SetAccessMode(BulkPrintersCalculator::ALL_ACCESS);
+  external_printers_->SetAccessMode(ExternalPrinters::ALL_ACCESS);
   // There will be 3 printers here.  But we don't want to wait for compuation to
   // complete to verify the final value gets used.
 
@@ -279,41 +307,41 @@
 }
 
 // Verifies that the observer is called at the expected times.
-TEST_F(BulkPrintersCalculatorTest, ObserverTest) {
+TEST_F(ExternalPrintersTest, ObserverTest) {
   TestObserver obs;
   external_printers_->AddObserver(&obs);
 
-  external_printers_->SetAccessMode(BulkPrintersCalculator::ALL_ACCESS);
+  external_printers_->SetAccessMode(ExternalPrinters::ALL_ACCESS);
   external_printers_->SetWhitelist(std::vector<std::string>());
   external_printers_->SetBlacklist(std::vector<std::string>());
   external_printers_->ClearData();
   scoped_task_environment_.RunUntilIdle();
-  EXPECT_EQ(1, obs.called);
+  EXPECT_EQ(0, obs.called);
 
   external_printers_->SetData(
       std::make_unique<std::string>(kBulkPolicyContentsJson));
 
   scoped_task_environment_.RunUntilIdle();
-  EXPECT_TRUE(external_printers_->IsDataPolicySet());
-  EXPECT_EQ(2, obs.called);
+  EXPECT_TRUE(external_printers_->IsPolicySet());
+  EXPECT_EQ(1, obs.called);
   EXPECT_TRUE(obs.last_valid);  // ready now
   // Printer list is correct after notification.
   EXPECT_EQ(kNumPrinters, external_printers_->GetPrinters().size());
 
-  external_printers_->SetAccessMode(BulkPrintersCalculator::WHITELIST_ONLY);
+  external_printers_->SetAccessMode(ExternalPrinters::WHITELIST_ONLY);
+  scoped_task_environment_.RunUntilIdle();
+  EXPECT_EQ(2, obs.called);  // effective list changed.  Notified.
+  EXPECT_TRUE(obs.last_valid);
+
+  external_printers_->SetAccessMode(ExternalPrinters::BLACKLIST_ONLY);
   scoped_task_environment_.RunUntilIdle();
   EXPECT_EQ(3, obs.called);  // effective list changed.  Notified.
   EXPECT_TRUE(obs.last_valid);
 
-  external_printers_->SetAccessMode(BulkPrintersCalculator::BLACKLIST_ONLY);
-  scoped_task_environment_.RunUntilIdle();
-  EXPECT_EQ(4, obs.called);  // effective list changed.  Notified.
-  EXPECT_TRUE(obs.last_valid);
-
   external_printers_->ClearData();
   scoped_task_environment_.RunUntilIdle();
-  EXPECT_EQ(5, obs.called);  // Called for transition to invalid policy.
-  EXPECT_TRUE(obs.last_valid);
+  EXPECT_EQ(4, obs.called);  // Called for transition to invalid policy.
+  EXPECT_FALSE(obs.last_valid);
   EXPECT_TRUE(external_printers_->GetPrinters().empty());
 
   // cleanup
diff --git a/chrome/browser/chromeos/printing/synced_printers_manager.cc b/chrome/browser/chromeos/printing/synced_printers_manager.cc
index 5a3886b..ee03322 100644
--- a/chrome/browser/chromeos/printing/synced_printers_manager.cc
+++ b/chrome/browser/chromeos/printing/synced_printers_manager.cc
@@ -10,14 +10,22 @@
 #include <utility>
 #include <vector>
 
+#include "base/bind.h"
+#include "base/feature_list.h"
 #include "base/guid.h"
+#include "base/json/json_reader.h"
+#include "base/md5.h"
 #include "base/observer_list_threadsafe.h"
 #include "base/optional.h"
 #include "base/synchronization/lock.h"
 #include "base/values.h"
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/chromeos/policy/browser_policy_connector_chromeos.h"
-#include "chrome/browser/chromeos/printing/enterprise_printers_provider.h"
+#include "chrome/browser/chromeos/printing/device_external_printers_factory.h"
+#include "chrome/browser/chromeos/printing/device_external_printers_settings_bridge.h"
+#include "chrome/browser/chromeos/printing/external_printers.h"
+#include "chrome/browser/chromeos/printing/external_printers_factory.h"
+#include "chrome/browser/chromeos/printing/external_printers_pref_bridge.h"
 #include "chrome/browser/chromeos/printing/printer_configurer.h"
 #include "chrome/browser/chromeos/printing/printers_sync_bridge.h"
 #include "chrome/browser/chromeos/printing/specifics_translation.h"
@@ -35,9 +43,36 @@
 
 namespace {
 
+// Returns the collection policies for user printers.
+ExternalPrinterPolicies UserPolicyNames() {
+  ExternalPrinterPolicies user_policy_names;
+  user_policy_names.access_mode = prefs::kRecommendedNativePrintersAccessMode;
+  user_policy_names.blacklist = prefs::kRecommendedNativePrintersBlacklist;
+  user_policy_names.whitelist = prefs::kRecommendedNativePrintersWhitelist;
+  return user_policy_names;
+}
+
+// Returns the collection policies for device printers.
+ExternalPrinterPolicies DevicePolicyNames() {
+  ExternalPrinterPolicies device_policy_names;
+  device_policy_names.access_mode = kDeviceNativePrintersAccessMode;
+  device_policy_names.blacklist = kDeviceNativePrintersBlacklist;
+  device_policy_names.whitelist = kDeviceNativePrintersWhitelist;
+  return device_policy_names;
+}
+
+// Inserts |printer| into |new_printers| if the id does not already exist.
+// Returns true if the insert was successful, false if there was a conflict.
+bool InsertIfNotPresent(std::unordered_map<std::string, Printer>* new_printers,
+                        const Printer& printer) {
+  std::pair<std::unordered_map<std::string, Printer>::iterator, bool> ret =
+      new_printers->insert({printer.id(), printer});
+  return ret.second;
+}
+
 class SyncedPrintersManagerImpl : public SyncedPrintersManager,
                                   public PrintersSyncBridge::Observer,
-                                  public EnterprisePrintersProvider::Observer {
+                                  public ExternalPrinters::Observer {
  public:
   SyncedPrintersManagerImpl(Profile* profile,
                             std::unique_ptr<PrintersSyncBridge> sync_bridge)
@@ -46,14 +81,42 @@
         observers_(new base::ObserverListThreadSafe<
                    SyncedPrintersManager::Observer>()),
         weak_factory_(this) {
-    printers_provider_ =
-        EnterprisePrintersProvider::Create(CrosSettings::Get(), profile_);
-    printers_provider_->AddObserver(this);
+    pref_change_registrar_.Init(profile->GetPrefs());
+    pref_change_registrar_.Add(
+        prefs::kRecommendedNativePrinters,
+        base::Bind(&SyncedPrintersManagerImpl::UpdateRecommendedPrinters,
+                   base::Unretained(this)));
+    if (base::FeatureList::IsEnabled(features::kBulkPrinters)) {
+      user_external_printers_observer_ =
+          std::make_unique<ExternalPrintersPrefBridge>(UserPolicyNames(),
+                                                       profile_);
+      user_external_printers_ =
+          ExternalPrintersFactory::Get()->GetForProfile(profile_);
+      if (user_external_printers_) {
+        user_external_printers_->AddObserver(this);
+      }
+    }
+
+    device_external_printers_observer_ =
+        std::make_unique<DeviceExternalPrintersSettingsBridge>(
+            DevicePolicyNames(), CrosSettings::Get());
+    device_external_printers_ =
+        DeviceExternalPrintersFactory::Get()->GetForDevice();
+    if (device_external_printers_) {
+      device_external_printers_->AddObserver(this);
+    }
+
+    UpdateRecommendedPrinters();
     sync_bridge_->AddObserver(this);
   }
 
   ~SyncedPrintersManagerImpl() override {
-    printers_provider_->RemoveObserver(this);
+    if (user_external_printers_) {
+      user_external_printers_->RemoveObserver(this);
+    }
+    if (device_external_printers_) {
+      device_external_printers_->RemoveObserver(this);
+    }
     sync_bridge_->RemoveObserver(this);
   }
 
@@ -69,10 +132,9 @@
     return printers;
   }
 
-  bool GetEnterprisePrinters(std::vector<Printer>& printers) const override {
+  std::vector<Printer> GetEnterprisePrinters() const override {
     base::AutoLock l(lock_);
-    printers = GetEnterprisePrintersLocked();
-    return enterprise_printers_are_ready_;
+    return GetEnterprisePrintersLocked();
   }
 
   std::unique_ptr<Printer> GetPrinter(
@@ -128,18 +190,14 @@
         GetConfiguredPrinters());
   }
 
-  // EnterprisePrintersProvider::Observer override
+  // ExternalPrinters::Observer override
   void OnPrintersChanged(
-      bool complete,
-      const std::unordered_map<std::string, Printer>& printers) override {
-    // Enterprise printers policy changed.  Update the lists.
-    base::AutoLock l(lock_);
-    enterprise_printers_ = printers;
-    enterprise_printers_are_ready_ = complete;
-    observers_->Notify(
-        FROM_HERE,
-        &SyncedPrintersManager::Observer::OnEnterprisePrintersChanged,
-        GetEnterprisePrintersLocked(), enterprise_printers_are_ready_);
+      bool valid,
+      const std::map<const ::std::string, const Printer>& printers) override {
+    // User or device policy printers changed.  Update the lists.
+    // |valid| is safe to ignore here since we're recomputing and the cached
+    // printers are always cleared.
+    UpdateRecommendedPrinters();
   }
 
  private:
@@ -179,12 +237,128 @@
     sync_bridge_->UpdatePrinter(PrinterToSpecifics(printer));
   }
 
+  // Reads printers provided by NativePrinters policy.  Appends ids to |new_ids|
+  // in the order they were received. Appends printers to |new_printers| indexed
+  // by id.  Discards printers with duplicate ids.
+  void PolicyNativePrinters(
+      std::vector<std::string>* new_ids,
+      std::unordered_map<std::string, Printer>* new_printers) {
+    const PrefService* prefs = profile_->GetPrefs();
+    const base::ListValue* values =
+        prefs->GetList(prefs::kRecommendedNativePrinters);
+    for (const auto& value : *values) {
+      std::string printer_json;
+      if (!value.GetAsString(&printer_json)) {
+        NOTREACHED();
+        continue;
+      }
+
+      std::unique_ptr<base::DictionaryValue> printer_dictionary =
+          base::DictionaryValue::From(base::JSONReader::ReadDeprecated(
+              printer_json, base::JSON_ALLOW_TRAILING_COMMAS));
+
+      if (!printer_dictionary) {
+        LOG(WARNING) << "Ignoring invalid printer.  Invalid JSON object: "
+                     << printer_json;
+        continue;
+      }
+
+      // Policy printers don't have id's but the ids only need to be locally
+      // unique so we'll hash the record.  This will not collide with the
+      // UUIDs generated for user entries.
+      std::string id = base::MD5String(printer_json);
+      printer_dictionary->SetString(kPrinterId, id);
+
+      auto new_printer = RecommendedPrinterToPrinter(*printer_dictionary);
+      if (!new_printer) {
+        LOG(WARNING) << "Recommended printer is malformed.";
+        continue;
+      }
+
+      if (!InsertIfNotPresent(new_printers, *new_printer)) {
+        // Printer is already in the list.
+        LOG(WARNING) << "Duplicate printer ignored: " << id;
+        continue;
+      }
+
+      new_ids->push_back(id);
+    }
+  }
+
+  // Reads printers provided by NativePrintersBulkConfigurations and
+  // DeviceNativePrinters policies. Appends ids to |new_ids| in the order they
+  // were received. Appends printers to |new_printers| indexed by id. Discards
+  // printers with duplicate ids.
+  void ReadPolicyPrinters(
+      base::WeakPtr<ExternalPrinters> external_printers,
+      std::vector<std::string>* new_ids,
+      std::unordered_map<std::string, Printer>* new_printers) {
+    DCHECK(new_ids);
+    DCHECK(new_printers);
+
+    if (!external_printers || !external_printers->IsPolicySet())
+      return;
+
+    const std::map<const std::string, const Printer>& printers =
+        external_printers->GetPrinters();
+    for (const auto& entry : printers) {
+      Printer printer(entry.second);
+      printer.set_source(Printer::SRC_POLICY);
+
+      if (!InsertIfNotPresent(new_printers, printer)) {
+        // Printer is already in the list.
+        LOG(WARNING) << "Duplicate printer ignored: " << printer.id();
+        continue;
+      }
+
+      new_ids->push_back(printer.id());
+    }
+  }
+
+  // Reads printers provided by NativePrintersBulkConfigurations policy.
+  void BulkPolicyPrinters(
+      std::vector<std::string>* new_ids,
+      std::unordered_map<std::string, Printer>* new_printers) {
+    ReadPolicyPrinters(ExternalPrintersFactory::Get()->GetForProfile(profile_),
+                       new_ids, new_printers);
+  }
+
+  // Reads printers provided by DeviceNativePrinters policy.
+  void DevicePolicyPrinters(
+      std::vector<std::string>* new_ids,
+      std::unordered_map<std::string, Printer>* new_printers) {
+    ReadPolicyPrinters(DeviceExternalPrintersFactory::Get()->GetForDevice(),
+                       new_ids, new_printers);
+  }
+
+  void UpdateRecommendedPrinters() {
+    // Parse the policy JSON into new structures outside the lock.
+    std::vector<std::string> new_ids;
+    std::unordered_map<std::string, Printer> new_printers;
+
+    PolicyNativePrinters(&new_ids, &new_printers);
+    if (base::FeatureList::IsEnabled(features::kBulkPrinters)) {
+      BulkPolicyPrinters(&new_ids, &new_printers);
+    }
+    DevicePolicyPrinters(&new_ids, &new_printers);
+
+    // Objects not in the most recent update get deallocated after method
+    // exit.
+    base::AutoLock l(lock_);
+    enterprise_printer_ids_.swap(new_ids);
+    enterprise_printers_.swap(new_printers);
+    observers_->Notify(
+        FROM_HERE,
+        &SyncedPrintersManager::Observer::OnEnterprisePrintersChanged,
+        GetEnterprisePrintersLocked());
+  }
+
   std::vector<Printer> GetEnterprisePrintersLocked() const {
     lock_.AssertAcquired();
     std::vector<Printer> ret;
     ret.reserve(enterprise_printers_.size());
-    for (auto kv : enterprise_printers_) {
-      ret.push_back(kv.second);
+    for (const std::string& id : enterprise_printer_ids_) {
+      ret.push_back(enterprise_printers_.find(id)->second);
     }
     return ret;
   }
@@ -192,17 +366,29 @@
   mutable base::Lock lock_;
 
   Profile* profile_;
+  PrefChangeRegistrar pref_change_registrar_;
+
+  // Bulk user printers. Unowned.
+  base::WeakPtr<ExternalPrinters> user_external_printers_;
+
+  // Device printers. Unowned.
+  base::WeakPtr<ExternalPrinters> device_external_printers_;
 
   // The backend for profile printers.
   std::unique_ptr<PrintersSyncBridge> sync_bridge_;
 
-  // The Object that provides updates about enterprise printers.
-  std::unique_ptr<EnterprisePrintersProvider> printers_provider_;
+  // Connects external printers preferences with the tracking object.
+  std::unique_ptr<ExternalPrintersPrefBridge> user_external_printers_observer_;
 
-  // Enterprise printers as of the last time we got a policy update.
+  // Connects external printers device settings with the tracking object.
+  std::unique_ptr<DeviceExternalPrintersSettingsBridge>
+      device_external_printers_observer_;
+
+  // Enterprise printers as of the last time we got a policy update.  The ids
+  // vector is used to preserve the received ordering.
+  std::vector<std::string> enterprise_printer_ids_;
+  // Map is from id to printer.
   std::unordered_map<std::string, Printer> enterprise_printers_;
-  // This flag is set to true if all enterprise policies were loaded.
-  bool enterprise_printers_are_ready_ = false;
 
   // Map of printer ids to PrinterConfigurer setup fingerprints at the time
   // the printers was last installed with CUPS.
@@ -218,7 +404,9 @@
 // static
 void SyncedPrintersManager::RegisterProfilePrefs(
     user_prefs::PrefRegistrySyncable* registry) {
-  EnterprisePrintersProvider::RegisterProfilePrefs(registry);
+  registry->RegisterListPref(prefs::kRecommendedNativePrinters);
+
+  ExternalPrintersPrefBridge::RegisterProfilePrefs(registry, UserPolicyNames());
 }
 
 // static
diff --git a/chrome/browser/chromeos/printing/synced_printers_manager.h b/chrome/browser/chromeos/printing/synced_printers_manager.h
index dd73d19..2ec83c2 100644
--- a/chrome/browser/chromeos/printing/synced_printers_manager.h
+++ b/chrome/browser/chromeos/printing/synced_printers_manager.h
@@ -39,8 +39,7 @@
     virtual void OnConfiguredPrintersChanged(
         const std::vector<Printer>& printers) = 0;
     virtual void OnEnterprisePrintersChanged(
-        const std::vector<Printer>& printers,
-        bool enterprise_printers_are_ready) {}
+        const std::vector<Printer>& printers) = 0;
   };
 
   static std::unique_ptr<SyncedPrintersManager> Create(
@@ -54,9 +53,8 @@
   // Returns the printers that are saved in preferences.
   virtual std::vector<Printer> GetConfiguredPrinters() const = 0;
 
-  // Replaces given vector with vector of printers from enterprise policy.
-  // Returns true if the enterprise policy was loaded and is valid.
-  virtual bool GetEnterprisePrinters(std::vector<Printer>& printers) const = 0;
+  // Returns printers from enterprise policy.
+  virtual std::vector<Printer> GetEnterprisePrinters() const = 0;
 
   // Returns the printer with id |printer_id|, or nullptr if no such printer
   // exists.  Searches both Configured and Enterprise printers.
diff --git a/chrome/browser/chromeos/printing/synced_printers_manager_unittest.cc b/chrome/browser/chromeos/printing/synced_printers_manager_unittest.cc
index 5191943d..ff90a8c 100644
--- a/chrome/browser/chromeos/printing/synced_printers_manager_unittest.cc
+++ b/chrome/browser/chromeos/printing/synced_printers_manager_unittest.cc
@@ -72,8 +72,7 @@
   }
 
   void OnEnterprisePrintersChanged(
-      const std::vector<Printer>& printer,
-      bool enterprise_printers_are_ready) override {
+      const std::vector<Printer>& printer) override {
     enterprise_printers_ = printer;
   }
 
@@ -201,12 +200,10 @@
   // TestingPrefSyncableService assumes ownership of |value|.
   prefs->SetManagedPref(prefs::kRecommendedNativePrinters, std::move(value));
 
-  std::vector<Printer> printers;
-  manager_->GetEnterprisePrinters(printers);
+  auto printers = manager_->GetEnterprisePrinters();
   ASSERT_EQ(2U, printers.size());
-  // order not specified
-  // EXPECT_EQ("Color Laser", printers[0].display_name());
-  // EXPECT_EQ("ipp://192.168.1.5", printers[1].uri());
+  EXPECT_EQ("Color Laser", printers[0].display_name());
+  EXPECT_EQ("ipp://192.168.1.5", printers[1].uri());
   EXPECT_EQ(Printer::Source::SRC_POLICY, printers[1].source());
 }
 
@@ -220,8 +217,7 @@
   // TestingPrefSyncableService assumes ownership of |value|.
   prefs->SetManagedPref(prefs::kRecommendedNativePrinters, std::move(value));
 
-  std::vector<Printer> printers;
-  manager_->GetEnterprisePrinters(printers);
+  auto printers = manager_->GetEnterprisePrinters();
 
   const Printer& from_list = printers.front();
   std::unique_ptr<Printer> retrieved = manager_->GetPrinter(from_list.id());
@@ -254,9 +250,7 @@
   prefs->SetManagedPref(prefs::kRecommendedNativePrinters, std::move(value));
 
   // Figure out the id of the enterprise printer that was just installed.
-  std::vector<Printer> printers;
-  manager_->GetEnterprisePrinters(printers);
-  std::string enterprise_id = printers.at(0).id();
+  std::string enterprise_id = manager_->GetEnterprisePrinters().at(0).id();
 
   Printer configured(kTestPrinterId);
 
diff --git a/chrome/browser/chromeos/system_logs/command_line_log_source.cc b/chrome/browser/chromeos/system_logs/command_line_log_source.cc
index a3672b9a..76e026e9 100644
--- a/chrome/browser/chromeos/system_logs/command_line_log_source.cc
+++ b/chrome/browser/chromeos/system_logs/command_line_log_source.cc
@@ -72,7 +72,7 @@
     command.AppendArg("-c");
     command.AppendArg(
         "/usr/bin/du -h --max-depth=5 /home/ /mnt/stateful_partition/ | "
-        "grep -v -e Downloads");
+        "grep -v -e Downloads -e IndexedDB -e databases");
     commands.emplace_back("system_files", command);
   }
 
diff --git a/chrome/browser/component_updater/recovery_component_installer.cc b/chrome/browser/component_updater/recovery_component_installer.cc
index ecd7e95..22980638 100644
--- a/chrome/browser/component_updater/recovery_component_installer.cc
+++ b/chrome/browser/component_updater/recovery_component_installer.cc
@@ -6,6 +6,7 @@
 
 #include <stdint.h>
 
+#include <functional>
 #include <memory>
 #include <string>
 #include <utility>
@@ -408,7 +409,7 @@
     update_client::CrxInstaller::Callback callback) {
   auto result = update_client::InstallFunctionWrapper(
       base::BindOnce(&RecoveryComponentInstaller::DoInstall,
-                     base::Unretained(this), base::ConstRef(unpack_path)));
+                     base::Unretained(this), std::cref(unpack_path)));
   base::PostTask(FROM_HERE, base::BindOnce(std::move(callback), result));
 }
 
diff --git a/chrome/browser/conflicts/incompatible_applications_browsertest.cc b/chrome/browser/conflicts/incompatible_applications_browsertest.cc
index 3e89cb0f..11e6db08 100644
--- a/chrome/browser/conflicts/incompatible_applications_browsertest.cc
+++ b/chrome/browser/conflicts/incompatible_applications_browsertest.cc
@@ -87,7 +87,7 @@
     // TODO(crbug.com/850517): Don't do test-specific setup if the test isn't
     // going to do anything. It seems to conflict with the VizDisplayCompositor
     // feature.
-    if (!base::FeatureList::IsEnabled(features::kVizDisplayCompositor)) {
+    if (!features::IsVizDisplayCompositorEnabled()) {
       ASSERT_TRUE(scoped_temp_dir_.CreateUniqueTempDir());
 
       ASSERT_NO_FATAL_FAILURE(
@@ -200,7 +200,7 @@
     return;
 
   // TODO(crbug.com/850517) This fails in viz_browser_tests in official builds.
-  if (base::FeatureList::IsEnabled(features::kVizDisplayCompositor))
+  if (features::IsVizDisplayCompositorEnabled())
     return;
 
   ModuleDatabase* module_database = ModuleDatabase::GetInstance();
diff --git a/chrome/browser/download/download_browsertest.cc b/chrome/browser/download/download_browsertest.cc
index ff6ebe1..5875f02 100644
--- a/chrome/browser/download/download_browsertest.cc
+++ b/chrome/browser/download/download_browsertest.cc
@@ -3168,8 +3168,7 @@
 
 // Test that the entire download pipeline handles unicode correctly.
 // Disabled on Windows due to flaky timeouts: crbug.com/446695
-// Disabled on chromeos due to flaky crash: crbug.com/577332
-#if defined(OS_WIN) || defined(OS_CHROMEOS)
+#if defined(OS_WIN)
 #define MAYBE_DownloadTest_CrazyFilenames DISABLED_DownloadTest_CrazyFilenames
 #else
 #define MAYBE_DownloadTest_CrazyFilenames DownloadTest_CrazyFilenames
diff --git a/chrome/browser/download/download_core_service_impl.cc b/chrome/browser/download/download_core_service_impl.cc
index b1b3130..582326f 100644
--- a/chrome/browser/download/download_core_service_impl.cc
+++ b/chrome/browser/download/download_core_service_impl.cc
@@ -12,9 +12,12 @@
 #include "chrome/browser/download/download_offline_content_provider.h"
 #include "chrome/browser/download/download_status_updater.h"
 #include "chrome/browser/download/download_ui_controller.h"
+#include "chrome/browser/download/offline_item_utils.h"
 #include "chrome/browser/history/history_service_factory.h"
+#include "chrome/browser/offline_items_collection/offline_content_aggregator_factory.h"
 #include "chrome/browser/profiles/profile.h"
 #include "components/history/core/browser/history_service.h"
+#include "components/offline_items_collection/core/offline_content_aggregator.h"
 #include "content/public/browser/download_manager.h"
 #include "extensions/buildflags/buildflags.h"
 
@@ -61,12 +64,19 @@
                      new DownloadHistory::HistoryAdapter(history))));
   }
 
+  download_provider_.reset(new DownloadOfflineContentProvider(
+      OfflineContentAggregatorFactory::GetForBrowserContext(
+          profile_->GetOriginalProfile()),
+      offline_items_collection::OfflineContentAggregator::CreateUniqueNameSpace(
+          OfflineItemUtils::GetDownloadNamespacePrefix(
+              profile_->IsOffTheRecord()),
+          profile_->IsOffTheRecord())));
+
   // Pass an empty delegate when constructing the DownloadUIController. The
   // default delegate does all the notifications we need.
   download_ui_.reset(new DownloadUIController(
-      manager, std::unique_ptr<DownloadUIController::Delegate>()));
-
-  download_provider_.reset(new DownloadOfflineContentProvider(manager));
+      manager, std::unique_ptr<DownloadUIController::Delegate>(),
+      download_provider_.get()));
 
 #if !defined(OS_ANDROID)
   download_shelf_controller_.reset(new DownloadShelfController(profile_));
diff --git a/chrome/browser/download/download_core_service_impl.h b/chrome/browser/download/download_core_service_impl.h
index f153ca8..23ee925 100644
--- a/chrome/browser/download/download_core_service_impl.h
+++ b/chrome/browser/download/download_core_service_impl.h
@@ -65,6 +65,10 @@
 
   std::unique_ptr<DownloadHistory> download_history_;
 
+  // The download provider is the responsible for supplying offline items to the
+  // UI.
+  std::unique_ptr<DownloadOfflineContentProvider> download_provider_;
+
   // The UI controller is responsible for observing the download manager and
   // notifying the UI of any new downloads. Its lifetime matches that of the
   // associated download manager.
@@ -76,10 +80,6 @@
   std::unique_ptr<DownloadShelfController> download_shelf_controller_;
 #endif
 
-  // The download provider is the responsible for supplying offline items to the
-  // UI.
-  std::unique_ptr<DownloadOfflineContentProvider> download_provider_;
-
 // On Android, GET downloads are not handled by the DownloadManager.
 // Once we have extensions on android, we probably need the EventRouter
 // in ContentViewDownloadDelegate which knows about both GET and POST
diff --git a/chrome/browser/download/download_offline_content_provider.cc b/chrome/browser/download/download_offline_content_provider.cc
index 84658a5..679721e 100644
--- a/chrome/browser/download/download_offline_content_provider.cc
+++ b/chrome/browser/download/download_offline_content_provider.cc
@@ -9,6 +9,7 @@
 #include "base/bind.h"
 #include "base/callback.h"
 #include "base/time/time.h"
+#include "build/build_config.h"
 #include "chrome/browser/download/image_thumbnail_request.h"
 #include "chrome/browser/download/offline_item_utils.h"
 #include "chrome/browser/offline_items_collection/offline_content_aggregator_factory.h"
@@ -21,6 +22,11 @@
 #include "ui/display/display.h"
 #include "ui/display/screen.h"
 
+#if defined(OS_ANDROID)
+#include "chrome/browser/android/download/download_manager_bridge.h"
+#include "chrome/browser/android/download/download_manager_service.h"
+#endif
+
 using OfflineItemFilter = offline_items_collection::OfflineItemFilter;
 using OfflineItemState = offline_items_collection::OfflineItemState;
 using OfflineItemProgressUnit =
@@ -32,7 +38,7 @@
 // Thumbnail size used for generating thumbnails for image files.
 const int kThumbnailSizeInDP = 64;
 
-bool ShouldShowDownloadItem(const download::DownloadItem* item) {
+bool ShouldShowDownloadItem(const DownloadItem* item) {
   return !item->IsTemporary() && !item->IsTransient() && !item->IsDangerous() &&
          !item->GetTargetFilePath().empty();
 }
@@ -40,16 +46,12 @@
 }  // namespace
 
 DownloadOfflineContentProvider::DownloadOfflineContentProvider(
-    DownloadManager* manager)
-    : manager_(manager),
-      download_notifier_(manager, this),
+    OfflineContentAggregator* aggregator,
+    const std::string& name_space)
+    : aggregator_(aggregator),
+      name_space_(name_space),
+      manager_(nullptr),
       weak_ptr_factory_(this) {
-  Profile* profile = Profile::FromBrowserContext(manager_->GetBrowserContext());
-  profile = profile->GetOriginalProfile();
-  aggregator_ = OfflineContentAggregatorFactory::GetForBrowserContext(profile);
-  bool incognito = manager_->GetBrowserContext()->IsOffTheRecord();
-  name_space_ = OfflineContentAggregator::CreateUniqueNameSpace(
-      OfflineItemUtils::GetDownloadNamespacePrefix(incognito), incognito);
   aggregator_->RegisterProvider(name_space_, this);
 }
 
@@ -57,35 +59,40 @@
   aggregator_->UnregisterProvider(name_space_);
 }
 
+void DownloadOfflineContentProvider::SetDownloadManager(
+    DownloadManager* manager) {
+  manager_ = manager;
+}
+
 // TODO(shaktisahu) : Pass DownloadOpenSource.
 void DownloadOfflineContentProvider::OpenItem(LaunchLocation location,
                                               const ContentId& id) {
-  download::DownloadItem* item = manager_->GetDownloadByGuid(id.id);
+  DownloadItem* item = GetDownload(id.id);
   if (item)
     item->OpenDownload();
 }
 
 void DownloadOfflineContentProvider::RemoveItem(const ContentId& id) {
-  download::DownloadItem* item = manager_->GetDownloadByGuid(id.id);
+  DownloadItem* item = GetDownload(id.id);
   if (item)
     item->Remove();
 }
 
 void DownloadOfflineContentProvider::CancelDownload(const ContentId& id) {
-  download::DownloadItem* item = manager_->GetDownloadByGuid(id.id);
+  DownloadItem* item = GetDownload(id.id);
   if (item)
     item->Cancel(true);
 }
 
 void DownloadOfflineContentProvider::PauseDownload(const ContentId& id) {
-  download::DownloadItem* item = manager_->GetDownloadByGuid(id.id);
+  DownloadItem* item = GetDownload(id.id);
   if (item)
     item->Pause();
 }
 
 void DownloadOfflineContentProvider::ResumeDownload(const ContentId& id,
                                                     bool has_user_gesture) {
-  download::DownloadItem* item = manager_->GetDownloadByGuid(id.id);
+  DownloadItem* item = GetDownload(id.id);
   if (item)
     item->Resume(has_user_gesture);
 }
@@ -93,7 +100,7 @@
 void DownloadOfflineContentProvider::GetItemById(
     const ContentId& id,
     OfflineContentProvider::SingleItemCallback callback) {
-  DownloadItem* item = manager_->GetDownloadByGuid(id.id);
+  DownloadItem* item = GetDownload(id.id);
   auto offline_item =
       item && ShouldShowDownloadItem(item)
           ? base::make_optional(
@@ -107,7 +114,7 @@
 void DownloadOfflineContentProvider::GetAllItems(
     OfflineContentProvider::MultipleItemCallback callback) {
   DownloadManager::DownloadVector all_items;
-  manager_->GetAllDownloads(&all_items);
+  GetAllDownloads(&all_items);
 
   std::vector<OfflineItem> items;
   for (auto* item : all_items) {
@@ -125,7 +132,7 @@
     const ContentId& id,
     VisualsCallback callback) {
   // TODO(crbug.com/855330) Supply thumbnail if item is visible.
-  DownloadItem* item = manager_->GetDownloadByGuid(id.id);
+  DownloadItem* item = GetDownload(id.id);
   if (!item)
     return;
 
@@ -172,8 +179,14 @@
   observers_.RemoveObserver(observer);
 }
 
-void DownloadOfflineContentProvider::OnDownloadUpdated(DownloadManager* manager,
-                                                       DownloadItem* item) {
+void DownloadOfflineContentProvider::OnDownloadStarted(DownloadItem* item) {
+  item->RemoveObserver(this);
+  item->AddObserver(this);
+
+  OnDownloadUpdated(item);
+}
+
+void DownloadOfflineContentProvider::OnDownloadUpdated(DownloadItem* item) {
   // Wait until the target path is determined or the download is canceled.
   if (item->GetTargetFilePath().empty() &&
       item->GetState() != DownloadItem::CANCELLED)
@@ -182,18 +195,75 @@
   if (!ShouldShowDownloadItem(item))
     return;
 
-  for (auto& observer : observers_) {
-    observer.OnItemUpdated(
-        OfflineItemUtils::CreateOfflineItem(name_space_, item));
+  if (item->GetState() == DownloadItem::COMPLETE) {
+    // TODO(crbug.com/938152): May be move this to DownloadItem.
+    AddCompletedDownload(item);
+    return;
   }
+
+  UpdateObservers(item);
 }
 
-void DownloadOfflineContentProvider::OnDownloadRemoved(DownloadManager* manager,
-                                                       DownloadItem* item) {
+void DownloadOfflineContentProvider::OnDownloadRemoved(DownloadItem* item) {
   if (!ShouldShowDownloadItem(item))
     return;
 
+#if defined(OS_ANDROID)
+  DownloadManagerBridge::RemoveCompletedDownload(item);
+#endif
+
   ContentId contentId(name_space_, item->GetGuid());
   for (auto& observer : observers_)
     observer.OnItemRemoved(contentId);
 }
+
+void DownloadOfflineContentProvider::AddCompletedDownload(DownloadItem* item) {
+#if defined(OS_ANDROID)
+  if (!item->GetTargetFilePath().IsContentUri()) {
+    DownloadManagerBridge::AddCompletedDownload(
+        item, base::BindOnce(
+                  &DownloadOfflineContentProvider::AddCompletedDownloadDone,
+                  weak_ptr_factory_.GetWeakPtr(), item));
+  } else {
+    AddCompletedDownloadDone(item, -1);
+  }
+#else
+  AddCompletedDownloadDone(item, -1);
+#endif
+}
+
+void DownloadOfflineContentProvider::AddCompletedDownloadDone(
+    DownloadItem* item,
+    int64_t system_download_id) {
+  UpdateObservers(item);
+}
+
+DownloadItem* DownloadOfflineContentProvider::GetDownload(
+    const std::string& download_guid) {
+#if defined(OS_ANDROID)
+  bool incognito =
+      manager_ ? manager_->GetBrowserContext()->IsOffTheRecord() : false;
+  return DownloadManagerService::GetInstance()->GetDownload(download_guid,
+                                                            incognito);
+#else
+  return manager_->GetDownloadByGuid(download_guid);
+#endif
+}
+
+void DownloadOfflineContentProvider::GetAllDownloads(
+    DownloadManager::DownloadVector* all_items) {
+#if defined(OS_ANDROID)
+  bool incognito =
+      manager_ ? manager_->GetBrowserContext()->IsOffTheRecord() : false;
+  DownloadManagerService::GetInstance()->GetAllDownloads(all_items, incognito);
+#else
+  manager_->GetAllDownloads(all_items);
+#endif
+}
+
+void DownloadOfflineContentProvider::UpdateObservers(DownloadItem* item) {
+  for (auto& observer : observers_) {
+    observer.OnItemUpdated(
+        OfflineItemUtils::CreateOfflineItem(name_space_, item));
+  }
+}
diff --git a/chrome/browser/download/download_offline_content_provider.h b/chrome/browser/download/download_offline_content_provider.h
index 1aa96dc..8a4f9d40 100644
--- a/chrome/browser/download/download_offline_content_provider.h
+++ b/chrome/browser/download/download_offline_content_provider.h
@@ -9,9 +9,10 @@
 #include <set>
 
 #include "base/macros.h"
-#include "components/download/content/public/all_download_item_notifier.h"
+#include "components/download/public/common/download_item.h"
 #include "components/offline_items_collection/core/offline_content_aggregator.h"
 #include "components/offline_items_collection/core/offline_content_provider.h"
+#include "content/public/browser/download_manager.h"
 
 using DownloadItem = download::DownloadItem;
 using DownloadManager = content::DownloadManager;
@@ -24,15 +25,19 @@
 
 class SkBitmap;
 
-// This class handles the task of observing a single DownloadManager and
-// notifies UI about updates about various downloads.
-class DownloadOfflineContentProvider
-    : public OfflineContentProvider,
-      public download::AllDownloadItemNotifier::Observer {
+// This class handles the task of observing the downloads associated with a
+// single DownloadManager (or in-progress download manager in service manager
+// only mode) and notifies UI about updates about various downloads.
+class DownloadOfflineContentProvider : public OfflineContentProvider,
+                                       public download::DownloadItem::Observer {
  public:
-  explicit DownloadOfflineContentProvider(DownloadManager* manager);
+  explicit DownloadOfflineContentProvider(OfflineContentAggregator* aggregator,
+                                          const std::string& name_space);
   ~DownloadOfflineContentProvider() override;
 
+  // Should be called when a DownloadManager is available.
+  void SetDownloadManager(DownloadManager* manager);
+
   // OfflineContentProvider implmentation.
   void OpenItem(LaunchLocation location, const ContentId& id) override;
   void RemoveItem(const ContentId& id) override;
@@ -52,20 +57,28 @@
   void AddObserver(OfflineContentProvider::Observer* observer) override;
   void RemoveObserver(OfflineContentProvider::Observer* observer) override;
 
- private:
-  // AllDownloadItemNotifier::Observer methods.
-  void OnDownloadUpdated(DownloadManager* manager, DownloadItem* item) override;
-  void OnDownloadRemoved(DownloadManager* manager, DownloadItem* item) override;
+  // Entry point for associating this class with a download item. Must be called
+  // for all new and in-progress downloads, after which this class will start
+  // observing the given download.
+  void OnDownloadStarted(DownloadItem* download_item);
 
+ private:
+  void OnDownloadUpdated(DownloadItem* item) override;
+  void OnDownloadRemoved(DownloadItem* item) override;
+
+  void GetAllDownloads(DownloadManager::DownloadVector* all_items);
+  DownloadItem* GetDownload(const std::string& download_guid);
   void OnThumbnailRetrieved(const ContentId& id,
                             VisualsCallback callback,
                             const SkBitmap& bitmap);
+  void AddCompletedDownload(DownloadItem* item);
+  void AddCompletedDownloadDone(DownloadItem* item, int64_t system_download_id);
+  void UpdateObservers(DownloadItem* item);
 
-  DownloadManager* manager_;
-  download::AllDownloadItemNotifier download_notifier_;
   base::ObserverList<OfflineContentProvider::Observer>::Unchecked observers_;
   OfflineContentAggregator* aggregator_;
   std::string name_space_;
+  DownloadManager* manager_;
 
   base::WeakPtrFactory<DownloadOfflineContentProvider> weak_ptr_factory_;
 
diff --git a/chrome/browser/download/download_ui_controller.cc b/chrome/browser/download/download_ui_controller.cc
index 8150809..7b50f1050 100644
--- a/chrome/browser/download/download_ui_controller.cc
+++ b/chrome/browser/download/download_ui_controller.cc
@@ -112,9 +112,13 @@
 DownloadUIController::Delegate::~Delegate() {
 }
 
-DownloadUIController::DownloadUIController(content::DownloadManager* manager,
-                                           std::unique_ptr<Delegate> delegate)
-    : download_notifier_(manager, this), delegate_(std::move(delegate)) {
+DownloadUIController::DownloadUIController(
+    content::DownloadManager* manager,
+    std::unique_ptr<Delegate> delegate,
+    DownloadOfflineContentProvider* provider)
+    : download_notifier_(manager, this),
+      delegate_(std::move(delegate)),
+      download_provider_(provider) {
 #if defined(OS_ANDROID)
   if (!delegate_)
     delegate_.reset(new AndroidUIControllerDelegate());
@@ -202,4 +206,6 @@
 
   DownloadItemModel(item).SetWasUINotified(true);
   delegate_->OnNewDownloadReady(item);
+  if (download_provider_)
+    download_provider_->OnDownloadStarted(item);
 }
diff --git a/chrome/browser/download/download_ui_controller.h b/chrome/browser/download/download_ui_controller.h
index e590dea..8500bee 100644
--- a/chrome/browser/download/download_ui_controller.h
+++ b/chrome/browser/download/download_ui_controller.h
@@ -9,6 +9,7 @@
 #include <set>
 
 #include "base/macros.h"
+#include "chrome/browser/download/download_offline_content_provider.h"
 #include "components/download/content/public/all_download_item_notifier.h"
 
 // This class handles the task of observing a single DownloadManager for
@@ -36,7 +37,8 @@
   //
   // Currently explicit delegates are only used for testing.
   DownloadUIController(content::DownloadManager* manager,
-                       std::unique_ptr<Delegate> delegate);
+                       std::unique_ptr<Delegate> delegate,
+                       DownloadOfflineContentProvider* provider);
 
   ~DownloadUIController() override;
 
@@ -49,6 +51,7 @@
   download::AllDownloadItemNotifier download_notifier_;
 
   std::unique_ptr<Delegate> delegate_;
+  DownloadOfflineContentProvider* download_provider_;
 
   DISALLOW_COPY_AND_ASSIGN(DownloadUIController);
 };
diff --git a/chrome/browser/download/download_ui_controller_unittest.cc b/chrome/browser/download/download_ui_controller_unittest.cc
index b154e01..b0dd933c 100644
--- a/chrome/browser/download/download_ui_controller_unittest.cc
+++ b/chrome/browser/download/download_ui_controller_unittest.cc
@@ -97,6 +97,8 @@
   // delegate results in the DownloadItem* being stored in |notified_item_|.
   std::unique_ptr<DownloadUIController::Delegate> GetTestDelegate();
 
+  DownloadOfflineContentProvider* GetDownloadProvider() { return nullptr; }
+
   MockDownloadManager* manager() { return manager_.get(); }
 
   // Returns the DownloadManager::Observer registered by a test case. This is
@@ -269,7 +271,8 @@
 // a non-empty path.  I.e. once the download target has been determined.
 TEST_F(DownloadUIControllerTest, DownloadUIController_NotifyBasic) {
   std::unique_ptr<MockDownloadItem> item(CreateMockInProgressDownload());
-  DownloadUIController controller(manager(), GetTestDelegate());
+  DownloadUIController controller(manager(), GetTestDelegate(),
+                                  GetDownloadProvider());
   EXPECT_CALL(*item, GetTargetFilePath())
       .WillOnce(ReturnRefOfCopy(base::FilePath()));
 
@@ -291,7 +294,8 @@
 // A download that's created in an interrupted state should also be displayed.
 TEST_F(DownloadUIControllerTest, DownloadUIController_NotifyBasic_Interrupted) {
   std::unique_ptr<MockDownloadItem> item = CreateMockInProgressDownload();
-  DownloadUIController controller(manager(), GetTestDelegate());
+  DownloadUIController controller(manager(), GetTestDelegate(),
+                                  GetDownloadProvider());
   EXPECT_CALL(*item, GetState())
       .WillRepeatedly(Return(download::DownloadItem::INTERRUPTED));
 
@@ -305,7 +309,8 @@
 // additional OnDownloadUpdated() notification.
 TEST_F(DownloadUIControllerTest, DownloadUIController_NotifyReadyOnCreate) {
   std::unique_ptr<MockDownloadItem> item(CreateMockInProgressDownload());
-  DownloadUIController controller(manager(), GetTestDelegate());
+  DownloadUIController controller(manager(), GetTestDelegate(),
+                                  GetDownloadProvider());
 
   ASSERT_TRUE(manager_observer());
   manager_observer()->OnDownloadCreated(manager(), item.get());
@@ -314,7 +319,8 @@
 
 // The UI shouldn't be notified of downloads that were restored from history.
 TEST_F(DownloadUIControllerTest, DownloadUIController_HistoryDownload) {
-  DownloadUIController controller(manager(), GetTestDelegate());
+  DownloadUIController controller(manager(), GetTestDelegate(),
+                                  GetDownloadProvider());
   // DownloadHistory should already have been created. It performs a query of
   // existing downloads upon creation. We'll use the callback to inject a
   // history download.
diff --git a/chrome/browser/download/notification/download_notification_browsertest.cc b/chrome/browser/download/notification/download_notification_browsertest.cc
index a678baab..e1d5f90 100644
--- a/chrome/browser/download/notification/download_notification_browsertest.cc
+++ b/chrome/browser/download/notification/download_notification_browsertest.cc
@@ -857,7 +857,9 @@
   EXPECT_EQ(download::DownloadItem::CANCELLED, downloads[0]->GetState());
 }
 
-IN_PROC_BROWSER_TEST_F(DownloadNotificationTest, IncognitoDownloadFile) {
+// TODO(crbug.com/938672): Reenable this.
+IN_PROC_BROWSER_TEST_F(DownloadNotificationTest,
+                       DISABLED_IncognitoDownloadFile) {
   PrepareIncognitoBrowser();
 
   // Starts an incognito download.
@@ -900,8 +902,9 @@
   chrome::CloseWindow(incognito_browser());
 }
 
+// TODO(crbug.com/938672): Reenable this.
 IN_PROC_BROWSER_TEST_F(DownloadNotificationTest,
-                       SimultaneousIncognitoAndNormalDownloads) {
+                       DISABLED_SimultaneousIncognitoAndNormalDownloads) {
   PrepareIncognitoBrowser();
 
   GURL url_incognito(SlowDownloadInterceptor::kUnknownSizeUrl);
diff --git a/chrome/browser/extensions/api/bookmark_manager_private/bookmark_manager_private_api.cc b/chrome/browser/extensions/api/bookmark_manager_private/bookmark_manager_private_api.cc
index ff33336..a51781f 100644
--- a/chrome/browser/extensions/api/bookmark_manager_private/bookmark_manager_private_api.cc
+++ b/chrome/browser/extensions/api/bookmark_manager_private/bookmark_manager_private_api.cc
@@ -26,7 +26,6 @@
 #include "chrome/browser/undo/bookmark_undo_service_factory.h"
 #include "chrome/common/extensions/api/bookmark_manager_private.h"
 #include "chrome/common/extensions/extension_constants.h"
-#include "chrome/grit/generated_resources.h"
 #include "components/bookmarks/browser/bookmark_model.h"
 #include "components/bookmarks/browser/bookmark_node_data.h"
 #include "components/bookmarks/browser/bookmark_utils.h"
@@ -464,84 +463,6 @@
   return true;
 }
 
-ExtensionFunction::ResponseAction
-BookmarkManagerPrivateGetStringsFunction::Run() {
-  std::unique_ptr<base::DictionaryValue> localized_strings(
-      new base::DictionaryValue());
-
-  localized_strings->SetString("title",
-      l10n_util::GetStringUTF16(IDS_BOOKMARK_MANAGER_TITLE));
-  localized_strings->SetString("search_button",
-      l10n_util::GetStringUTF16(IDS_BOOKMARK_MANAGER_SEARCH_BUTTON));
-  localized_strings->SetString("folders_menu",
-      l10n_util::GetStringUTF16(IDS_BOOKMARK_MANAGER_FOLDERS_MENU));
-  localized_strings->SetString("organize_menu",
-      l10n_util::GetStringUTF16(IDS_BOOKMARK_MANAGER_ORGANIZE_MENU));
-  localized_strings->SetString("show_in_folder",
-      l10n_util::GetStringUTF16(IDS_BOOKMARK_MANAGER_SHOW_IN_FOLDER));
-  localized_strings->SetString("sort",
-      l10n_util::GetStringUTF16(IDS_BOOKMARK_MANAGER_SORT));
-  localized_strings->SetString("import_menu",
-      l10n_util::GetStringUTF16(IDS_BOOKMARK_MANAGER_IMPORT_MENU));
-  localized_strings->SetString("export_menu",
-      l10n_util::GetStringUTF16(IDS_BOOKMARK_MANAGER_EXPORT_MENU));
-  localized_strings->SetString("rename_folder",
-      l10n_util::GetStringUTF16(IDS_BOOKMARK_BAR_RENAME_FOLDER));
-  localized_strings->SetString("edit",
-      l10n_util::GetStringUTF16(IDS_BOOKMARK_BAR_EDIT));
-  localized_strings->SetString("should_open_all",
-      l10n_util::GetStringUTF16(IDS_BOOKMARK_BAR_SHOULD_OPEN_ALL));
-  localized_strings->SetString("open_incognito",
-      l10n_util::GetStringUTF16(IDS_BOOKMARK_BAR_OPEN_INCOGNITO));
-  localized_strings->SetString("open_in_new_tab",
-      l10n_util::GetStringUTF16(IDS_BOOKMARK_BAR_OPEN_IN_NEW_TAB));
-  localized_strings->SetString("open_in_new_window",
-      l10n_util::GetStringUTF16(IDS_BOOKMARK_BAR_OPEN_IN_NEW_WINDOW));
-  localized_strings->SetString("add_new_bookmark",
-      l10n_util::GetStringUTF16(IDS_BOOKMARK_BAR_ADD_NEW_BOOKMARK));
-  localized_strings->SetString("new_folder",
-      l10n_util::GetStringUTF16(IDS_BOOKMARK_BAR_NEW_FOLDER));
-  localized_strings->SetString("open_all",
-      l10n_util::GetStringUTF16(IDS_BOOKMARK_BAR_OPEN_ALL));
-  localized_strings->SetString("open_all_new_window",
-      l10n_util::GetStringUTF16(IDS_BOOKMARK_BAR_OPEN_ALL_NEW_WINDOW));
-  localized_strings->SetString("open_all_incognito",
-      l10n_util::GetStringUTF16(IDS_BOOKMARK_BAR_OPEN_ALL_INCOGNITO));
-  localized_strings->SetString("remove",
-      l10n_util::GetStringUTF16(IDS_BOOKMARK_BAR_REMOVE));
-  localized_strings->SetString("copy",
-      l10n_util::GetStringUTF16(IDS_CONTENT_CONTEXT_COPY));
-  localized_strings->SetString("cut",
-      l10n_util::GetStringUTF16(IDS_CONTENT_CONTEXT_CUT));
-  localized_strings->SetString("paste",
-      l10n_util::GetStringUTF16(IDS_CONTENT_CONTEXT_PASTE));
-  localized_strings->SetString("delete",
-      l10n_util::GetStringUTF16(IDS_CONTENT_CONTEXT_DELETE));
-  localized_strings->SetString("undo_delete",
-      l10n_util::GetStringUTF16(IDS_UNDO_DELETE));
-  localized_strings->SetString("new_folder_name",
-      l10n_util::GetStringUTF16(IDS_BOOKMARK_EDITOR_NEW_FOLDER_NAME));
-  localized_strings->SetString("name_input_placeholder",
-      l10n_util::GetStringUTF16(IDS_BOOKMARK_MANAGER_NAME_INPUT_PLACE_HOLDER));
-  localized_strings->SetString("url_input_placeholder",
-      l10n_util::GetStringUTF16(IDS_BOOKMARK_MANAGER_URL_INPUT_PLACE_HOLDER));
-  localized_strings->SetString("invalid_url",
-      l10n_util::GetStringUTF16(IDS_BOOKMARK_MANAGER_INVALID_URL));
-  localized_strings->SetString("recent",
-      l10n_util::GetStringUTF16(IDS_BOOKMARK_MANAGER_RECENT));
-  localized_strings->SetString("search",
-      l10n_util::GetStringUTF16(IDS_BOOKMARK_MANAGER_SEARCH));
-  localized_strings->SetString("save",
-      l10n_util::GetStringUTF16(IDS_SAVE));
-  localized_strings->SetString("cancel",
-      l10n_util::GetStringUTF16(IDS_CANCEL));
-
-  const std::string& app_locale = g_browser_process->GetApplicationLocale();
-  webui::SetLoadTimeDataDefaults(app_locale, localized_strings.get());
-
-  return RespondNow(OneArgument(std::move(localized_strings)));
-}
-
 bool BookmarkManagerPrivateStartDragFunction::RunOnReady() {
   if (!EditBookmarksEnabled())
     return false;
diff --git a/chrome/browser/extensions/api/bookmark_manager_private/bookmark_manager_private_api.h b/chrome/browser/extensions/api/bookmark_manager_private/bookmark_manager_private_api.h
index d14e965..e24bc87 100644
--- a/chrome/browser/extensions/api/bookmark_manager_private/bookmark_manager_private_api.h
+++ b/chrome/browser/extensions/api/bookmark_manager_private/bookmark_manager_private_api.h
@@ -204,19 +204,6 @@
   bool RunOnReady() override;
 };
 
-class BookmarkManagerPrivateGetStringsFunction
-    : public UIThreadExtensionFunction {
- public:
-  DECLARE_EXTENSION_FUNCTION("bookmarkManagerPrivate.getStrings",
-                             BOOKMARKMANAGERPRIVATE_GETSTRINGS)
-
- protected:
-  ~BookmarkManagerPrivateGetStringsFunction() override {}
-
-  // UIThreadExtensionFunction:
-  ResponseAction Run() override;
-};
-
 class BookmarkManagerPrivateStartDragFunction
     : public extensions::BookmarksFunction {
  public:
diff --git a/chrome/browser/extensions/api/cookies/cookies_helpers.cc b/chrome/browser/extensions/api/cookies/cookies_helpers.cc
index 0fa2604..bb8cce5 100644
--- a/chrome/browser/extensions/api/cookies/cookies_helpers.cc
+++ b/chrome/browser/extensions/api/cookies/cookies_helpers.cc
@@ -130,8 +130,8 @@
   } else {
     net::CookieOptions options;
     options.set_include_httponly();
-    options.set_same_site_cookie_mode(
-        net::CookieOptions::SameSiteCookieMode::INCLUDE_STRICT_AND_LAX);
+    options.set_same_site_cookie_context(
+        net::CookieOptions::SameSiteCookieContext::SAME_SITE_STRICT);
     options.set_do_not_update_access_time();
 
     manager->GetCookieList(url, options, std::move(callback));
diff --git a/chrome/browser/extensions/api/resources_private/resources_private_api.cc b/chrome/browser/extensions/api/resources_private/resources_private_api.cc
index 1cecccf..ef039e9 100644
--- a/chrome/browser/extensions/api/resources_private/resources_private_api.cc
+++ b/chrome/browser/extensions/api/resources_private/resources_private_api.cc
@@ -68,6 +68,9 @@
   SetL10nString(dict, "annotationPen", IDS_PDF_ANNOTATION_PEN);
   SetL10nString(dict, "annotationHighlighter", IDS_PDF_ANNOTATION_HIGHLIGHTER);
   SetL10nString(dict, "annotationEraser", IDS_PDF_ANNOTATION_ERASER);
+  SetL10nString(dict, "annotationUndo", IDS_PDF_ANNOTATION_UNDO);
+  SetL10nString(dict, "annotationRedo", IDS_PDF_ANNOTATION_REDO);
+  SetL10nString(dict, "annotationExpand", IDS_PDF_ANNOTATION_EXPAND);
   SetL10nString(dict, "annotationColorBlack", IDS_PDF_ANNOTATION_COLOR_BLACK);
   SetL10nString(dict, "annotationColorRed", IDS_PDF_ANNOTATION_COLOR_RED);
   SetL10nString(dict, "annotationColorYellow", IDS_PDF_ANNOTATION_COLOR_YELLOW);
@@ -118,6 +121,12 @@
   SetL10nString(dict, "annotationSize12", IDS_PDF_ANNOTATION_SIZE12);
   SetL10nString(dict, "annotationSize16", IDS_PDF_ANNOTATION_SIZE16);
   SetL10nString(dict, "annotationSize20", IDS_PDF_ANNOTATION_SIZE20);
+  SetL10nString(dict, "annotationFormWarningTitle",
+                IDS_PDF_DISCARD_FORM_CHANGES);
+  SetL10nString(dict, "annotationFormWarningDetail",
+                IDS_PDF_DISCARD_FORM_CHANGES_DETAIL);
+  SetL10nString(dict, "annotationFormWarningKeepEditing", IDS_PDF_KEEP_EDITING);
+  SetL10nString(dict, "annotationFormWarningDiscard", IDS_PDF_DISCARD);
 #endif
 }
 
diff --git a/chrome/browser/extensions/api/socket/tcp_socket_unittest.cc b/chrome/browser/extensions/api/socket/tcp_socket_unittest.cc
index f9627253..3ea72e0 100644
--- a/chrome/browser/extensions/api/socket/tcp_socket_unittest.cc
+++ b/chrome/browser/extensions/api/socket/tcp_socket_unittest.cc
@@ -512,14 +512,6 @@
                                                         success_);
   }
   std::unique_ptr<net::SSLClientSocket> CreateSSLClientSocket(
-      std::unique_ptr<net::ClientSocketHandle>,
-      const net::HostPortPair&,
-      const net::SSLConfig&,
-      const net::SSLClientSocketContext&) override {
-    NOTIMPLEMENTED();
-    return std::unique_ptr<net::SSLClientSocket>();
-  }
-  std::unique_ptr<net::SSLClientSocket> CreateSSLClientSocket(
       std::unique_ptr<net::StreamSocket>,
       const net::HostPortPair&,
       const net::SSLConfig&,
@@ -528,21 +520,6 @@
     return std::unique_ptr<net::SSLClientSocket>();
   }
   std::unique_ptr<net::ProxyClientSocket> CreateProxyClientSocket(
-      std::unique_ptr<net::ClientSocketHandle> transport_socket,
-      const std::string& user_agent,
-      const net::HostPortPair& endpoint,
-      const net::ProxyServer& proxy_server,
-      net::HttpAuthController* http_auth_controller,
-      bool tunnel,
-      bool using_spdy,
-      net::NextProto negotiated_protocol,
-      net::ProxyDelegate* proxy_delegate,
-      bool is_https_proxy,
-      const net::NetworkTrafficAnnotationTag& traffic_annotation) override {
-    NOTIMPLEMENTED();
-    return nullptr;
-  }
-  std::unique_ptr<net::ProxyClientSocket> CreateProxyClientSocket(
       std::unique_ptr<net::StreamSocket> stream_socket,
       const std::string& user_agent,
       const net::HostPortPair& endpoint,
diff --git a/chrome/browser/extensions/extension_uninstall_dialog.cc b/chrome/browser/extensions/extension_uninstall_dialog.cc
index c17540d..4b79106 100644
--- a/chrome/browser/extensions/extension_uninstall_dialog.cc
+++ b/chrome/browser/extensions/extension_uninstall_dialog.cc
@@ -98,6 +98,7 @@
 
   extension_ = extension;
   uninstall_reason_ = reason;
+  uninstall_source_ = source;
 
   if (parent() && parent_window_tracker_->WasNativeWindowClosed()) {
     OnDialogClosed(CLOSE_ACTION_CANCELED);
diff --git a/chrome/browser/extensions/extension_uninstall_dialog.h b/chrome/browser/extensions/extension_uninstall_dialog.h
index 0612e33..e7cf0e7 100644
--- a/chrome/browser/extensions/extension_uninstall_dialog.h
+++ b/chrome/browser/extensions/extension_uninstall_dialog.h
@@ -119,6 +119,7 @@
       return triggering_extension_.get(); }
   const gfx::ImageSkia& icon() const { return icon_->image_skia(); }
   gfx::NativeWindow parent() { return parent_; }
+  UninstallSource uninstall_source() { return uninstall_source_; }
 
  private:
   // Uninstalls the extension. Returns true on success, and populates |error| on
@@ -172,6 +173,8 @@
 
   UninstallReason uninstall_reason_ = UNINSTALL_REASON_FOR_TESTING;
 
+  UninstallSource uninstall_source_ = UNINSTALL_SOURCE_FOR_TESTING;
+
   ScopedObserver<ExtensionRegistry, ExtensionRegistryObserver> observer_;
 
   base::ThreadChecker thread_checker_;
diff --git a/chrome/browser/favicon/favicon_utils.cc b/chrome/browser/favicon/favicon_utils.cc
index c88fd18..81a9cc3c0 100644
--- a/chrome/browser/favicon/favicon_utils.cc
+++ b/chrome/browser/favicon/favicon_utils.cc
@@ -12,9 +12,12 @@
 #include "content/public/browser/navigation_controller.h"
 #include "content/public/browser/navigation_entry.h"
 #include "content/public/common/favicon_url.h"
+#include "ui/base/resource/resource_bundle.h"
 #include "ui/gfx/image/image.h"
 #include "ui/gfx/image/image_skia.h"
 #include "ui/gfx/image/image_skia_operations.h"
+#include "ui/native_theme/native_theme.h"
+#include "ui/resources/grit/ui_resources.h"
 
 namespace favicon {
 
@@ -90,4 +93,13 @@
   return favicon;
 }
 
+gfx::Image GetDefaultFavicon() {
+  const ui::NativeTheme* native_theme =
+      ui::NativeTheme::GetInstanceForNativeUi();
+  bool is_dark = native_theme && native_theme->SystemDarkModeEnabled();
+  int resource_id = is_dark ? IDR_DEFAULT_FAVICON_DARK : IDR_DEFAULT_FAVICON;
+  return ui::ResourceBundle::GetSharedInstance().GetNativeImageNamed(
+      resource_id);
+}
+
 }  // namespace favicon
diff --git a/chrome/browser/favicon/favicon_utils.h b/chrome/browser/favicon/favicon_utils.h
index e985c0ea..581a6e6 100644
--- a/chrome/browser/favicon/favicon_utils.h
+++ b/chrome/browser/favicon/favicon_utils.h
@@ -33,6 +33,10 @@
 // network error, desaturate the favicon.
 gfx::Image TabFaviconFromWebContents(content::WebContents* contents);
 
+// Returns the image to use when no favicon is available, taking dark mode
+// into account if necessary.
+gfx::Image GetDefaultFavicon();
+
 }  // namespace favicon
 
 #endif  // CHROME_BROWSER_FAVICON_FAVICON_UTILS_H_
diff --git a/chrome/browser/flag-metadata.json b/chrome/browser/flag-metadata.json
index d949b7e..d2967ab 100644
--- a/chrome/browser/flag-metadata.json
+++ b/chrome/browser/flag-metadata.json
@@ -924,8 +924,8 @@
   },
   {
     "name": "enable-bloated-renderer-detection",
-    // "owners": [ "your-team" ],
-    "expiry_milestone": 76
+    "owners": [ "ulan" ],
+    "expiry_milestone": 75
   },
   {
     "name": "enable-block-tab-unders",
@@ -1282,9 +1282,19 @@
     "expiry_milestone": 76
   },
   {
-    "name": "enable-layered-api",
-    // "owners": [ "your-team" ],
-    "expiry_milestone": 76
+    "name": "enable-built-in-module-all",
+    "owners": [ "hiroshige", "domenic" ],
+    "expiry_milestone": 80
+  },
+  {
+    "name": "enable-built-in-module-infra",
+    "owners": [ "hiroshige", "domenic" ],
+    "expiry_milestone": 80
+  },
+  {
+    "name": "enable-built-in-module-kv-storage",
+    "owners": [ "hiroshige", "domenic" ],
+    "expiry_milestone": 80
   },
   {
     "name": "enable-blink-gen-property-trees",
@@ -2319,6 +2329,11 @@
     "expiry_milestone": 80
   },
   {
+    "name": "native-filesystem-api",
+    "owners": [ "mek", "pwnall" ],
+    "expiry_milestone":  80
+  },
+  {
     "name": "network-service",
     // "owners": [ "your-team" ],
     "expiry_milestone": 76
@@ -3062,11 +3077,6 @@
     "expiry_milestone": -1
   },
   {
-    "name": "use-ddljson-api",
-    // "owners": [ "your-team" ],
-    "expiry_milestone": 76
-  },
-  {
     "name": "use-google-local-ntp",
     "owners": [ "ramyan" ],
     "expiry_milestone": 75
diff --git a/chrome/browser/flag_descriptions.cc b/chrome/browser/flag_descriptions.cc
index 5a37916..ac93910 100644
--- a/chrome/browser/flag_descriptions.cc
+++ b/chrome/browser/flag_descriptions.cc
@@ -634,11 +634,23 @@
     "This requires enable-lite-page-server-previews to be enabled along with "
     "network-service.";
 
-const char kLayeredAPIName[] = "Experimental layered APIs";
-const char kLayeredAPIDescription[] =
-    "Enable layered API infrastructure, as well as several experimental "
-    "layered APIs. The syntax and the APIs exposed are experimental and will "
-    "change over time.";
+const char kBuiltInModuleAllName[] = "All experimental built-in modules";
+const char kBuiltInModuleAllDescription[] =
+    "Enable all experimental built-in modules, as well as built-in module "
+    "infrastructure and import maps. The syntax and the APIs exposed are "
+    "experimental and will change over time.";
+
+const char kBuiltInModuleInfraName[] = "Built-in module infra and import maps";
+const char kBuiltInModuleInfraDescription[] =
+    "Enable built-in module infrastructure and import maps. Individual "
+    "built-in modules should be enabled by other flags. The syntax and the "
+    "APIs exposed are experimental and will change over time.";
+
+const char kBuiltInModuleKvStorageName[] = "kv-storage built-in module";
+const char kBuiltInModuleKvStorageDescription[] =
+    "Enable kv-storage built-in module, as well as built-in module "
+    "infrastructure and import maps. The syntax and the APIs exposed are "
+    "experimental and will change over time.";
 
 const char kEnableBlinkGenPropertyTreesName[] = "Enable BlinkGenPropertyTrees";
 const char kEnableBlinkGenPropertyTreesDescription[] =
@@ -986,7 +998,7 @@
     "Experimental Productivity Features";
 const char kExperimentalProductivityFeaturesDescription[] =
     "Enable support for experimental developer productivity features, such as "
-    "Layered APIs and policies for avoiding slow rendering.";
+    "built-in modules and policies for avoiding slow rendering.";
 
 const char kExperimentalSecurityFeaturesName[] =
     "Potentially annoying security features";
@@ -1214,6 +1226,11 @@
 const char kMobileIdentityConsistencyDescription[] =
     "Enables stronger identity consistency on mobile";
 
+const char kNativeFilesystemAPIName[] = "Native Filesystem API";
+const char kNativeFilesystemAPIDescription[] =
+    "Enables the experimental Native Filesystem API, giving websites access to "
+    "the native filesystem";
+
 const char kNewAudioRenderingMixingStrategyName[] =
     "New audio rendering mixing strategy";
 const char kNewAudioRenderingMixingStrategyDescription[] =
@@ -1924,7 +1941,6 @@
     "Force the Translate Triggering on English pages experiment to be enabled "
     "with the selected language model active.";
 
-
 const char kTreatInsecureOriginAsSecureName[] =
     "Insecure origins treated as secure";
 const char kTreatInsecureOriginAsSecureDescription[] =
@@ -1951,10 +1967,6 @@
 const char kUiPartialSwapName[] = "Partial swap";
 const char kUiPartialSwapDescription[] = "Sets partial swap behavior.";
 
-const char kUseDdljsonApiName[] = "Use new ddljson API for Doodles";
-const char kUseDdljsonApiDescription[] =
-    "Enables the new ddljson API to fetch Doodles for the NTP.";
-
 const char kUsePdfCompositorServiceName[] =
     "Use PDF compositor service for printing";
 const char kUsePdfCompositorServiceDescription[] =
diff --git a/chrome/browser/flag_descriptions.h b/chrome/browser/flag_descriptions.h
index d2f184f..93b418ae 100644
--- a/chrome/browser/flag_descriptions.h
+++ b/chrome/browser/flag_descriptions.h
@@ -400,8 +400,14 @@
 extern const char kEnableURLLoaderLitePageServerPreviewsName[];
 extern const char kEnableURLLoaderLitePageServerPreviewsDescription[];
 
-extern const char kLayeredAPIName[];
-extern const char kLayeredAPIDescription[];
+extern const char kBuiltInModuleAllName[];
+extern const char kBuiltInModuleAllDescription[];
+
+extern const char kBuiltInModuleInfraName[];
+extern const char kBuiltInModuleInfraDescription[];
+
+extern const char kBuiltInModuleKvStorageName[];
+extern const char kBuiltInModuleKvStorageDescription[];
 
 extern const char kEnableBlinkGenPropertyTreesName[];
 extern const char kEnableBlinkGenPropertyTreesDescription[];
@@ -734,6 +740,9 @@
 extern const char kMobileIdentityConsistencyName[];
 extern const char kMobileIdentityConsistencyDescription[];
 
+extern const char kNativeFilesystemAPIName[];
+extern const char kNativeFilesystemAPIDescription[];
+
 extern const char kNewAudioRenderingMixingStrategyName[];
 extern const char kNewAudioRenderingMixingStrategyDescription[];
 
@@ -1155,9 +1164,6 @@
 extern const char kUiPartialSwapName[];
 extern const char kUiPartialSwapDescription[];
 
-extern const char kUseDdljsonApiName[];
-extern const char kUseDdljsonApiDescription[];
-
 extern const char kUsePdfCompositorServiceName[];
 extern const char kUsePdfCompositorServiceDescription[];
 
diff --git a/chrome/browser/loader/signed_exchange_policy_browsertest.cc b/chrome/browser/loader/signed_exchange_policy_browsertest.cc
new file mode 100644
index 0000000..9d65bae
--- /dev/null
+++ b/chrome/browser/loader/signed_exchange_policy_browsertest.cc
@@ -0,0 +1,128 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/strings/utf_string_conversions.h"
+#include "base/test/scoped_feature_list.h"
+#include "base/values.h"
+#include "chrome/browser/ssl/cert_verifier_browser_test.h"
+#include "chrome/browser/ui/browser.h"
+#include "chrome/test/base/ui_test_utils.h"
+#include "components/policy/core/browser/browser_policy_connector.h"
+#include "components/policy/core/common/mock_configuration_policy_provider.h"
+#include "components/policy/core/common/policy_map.h"
+#include "components/policy/policy_constants.h"
+#include "content/public/test/browser_test_utils.h"
+#include "content/public/test/signed_exchange_browser_test_helper.h"
+#include "services/network/public/cpp/features.h"
+
+struct SignedExchangePolicyBrowserTestParam {
+  explicit SignedExchangePolicyBrowserTestParam(bool network_service_enabled)
+      : network_service_enabled(network_service_enabled) {}
+  const bool network_service_enabled;
+};
+
+class SignedExchangePolicyBrowserTest
+    : public CertVerifierBrowserTest,
+      public testing::WithParamInterface<SignedExchangePolicyBrowserTestParam> {
+ public:
+  SignedExchangePolicyBrowserTest() = default;
+  ~SignedExchangePolicyBrowserTest() override = default;
+
+ protected:
+  void PreRunTestOnMainThread() override {
+    InProcessBrowserTest::PreRunTestOnMainThread();
+    sxg_test_helper_.InstallMockCert(mock_cert_verifier());
+    sxg_test_helper_.InstallMockCertChainInterceptor();
+  }
+
+  void SetUpInProcessBrowserTestFixture() override {
+    CertVerifierBrowserTest::SetUpInProcessBrowserTestFixture();
+    EXPECT_CALL(policy_provider_, IsInitializationComplete(testing::_))
+        .WillRepeatedly(testing::Return(true));
+    policy::BrowserPolicyConnector::SetPolicyProviderForTesting(
+        &policy_provider_);
+  }
+
+  policy::MockConfigurationPolicyProvider policy_provider_;
+
+ private:
+  void SetUp() override {
+    std::vector<base::Feature> enable_features;
+    std::vector<base::Feature> disable_features;
+    if (GetParam().network_service_enabled) {
+      enable_features.push_back(network::features::kNetworkService);
+    } else {
+      disable_features.push_back(network::features::kNetworkService);
+    }
+    feature_list_.InitWithFeatures(enable_features, disable_features);
+    sxg_test_helper_.SetUp();
+    InProcessBrowserTest::SetUp();
+  }
+
+  void TearDownOnMainThread() override {
+    sxg_test_helper_.TearDownOnMainThread();
+  }
+
+  base::test::ScopedFeatureList feature_list_;
+  content::SignedExchangeBrowserTestHelper sxg_test_helper_;
+
+  DISALLOW_COPY_AND_ASSIGN(SignedExchangePolicyBrowserTest);
+};
+
+IN_PROC_BROWSER_TEST_P(SignedExchangePolicyBrowserTest, BlackList) {
+  embedded_test_server()->ServeFilesFromSourceDirectory("content/test/data");
+  ASSERT_TRUE(embedded_test_server()->Start());
+
+  const GURL inner_url("https://test.example.org/test/");
+  const GURL url =
+      embedded_test_server()->GetURL("/sxg/test.example.org_test.sxg");
+
+  base::string16 expected_title(base::UTF8ToUTF16(inner_url.spec()));
+  content::WebContents* contents =
+      browser()->tab_strip_model()->GetActiveWebContents();
+  content::TitleWatcher title_watcher(contents, expected_title);
+  ui_test_utils::NavigateToURL(browser(), url);
+  EXPECT_EQ(expected_title, title_watcher.WaitAndGetTitle());
+
+  base::ListValue blacklist;
+  blacklist.AppendString("test.example.org");
+  policy::PolicyMap policies;
+  policies.Set(policy::key::kURLBlacklist, policy::POLICY_LEVEL_MANDATORY,
+               policy::POLICY_SCOPE_USER, policy::POLICY_SOURCE_CLOUD,
+               blacklist.CreateDeepCopy(), nullptr);
+
+#if defined(OS_CHROMEOS)
+  policy::SetEnterpriseUsersDefaults(&policies);
+#endif
+  policy_provider_.UpdateChromePolicy(policies);
+  base::RunLoop loop;
+  loop.RunUntilIdle();
+
+  // Updates of the URLBlacklist are done on IO, after building the blacklist
+  // on the blocking pool, which is initiated from IO.
+  content::RunAllPendingInMessageLoop(content::BrowserThread::IO);
+  content::RunAllTasksUntilIdle();
+  content::RunAllPendingInMessageLoop(content::BrowserThread::IO);
+
+  ui_test_utils::NavigateToURL(browser(), url);
+
+  base::string16 blocked_page_title(base::UTF8ToUTF16("test.example.org"));
+  EXPECT_EQ(blocked_page_title, contents->GetTitle());
+
+  // Verify that the expected error page is being displayed.
+  bool result = false;
+  EXPECT_TRUE(content::ExecuteScriptAndExtractBool(
+      contents,
+      "var textContent = document.body.textContent;"
+      "var hasError = textContent.indexOf('ERR_BLOCKED_BY_ADMINISTRATOR') >= 0;"
+      "domAutomationController.send(hasError);",
+      &result));
+  EXPECT_TRUE(result);
+}
+
+INSTANTIATE_TEST_SUITE_P(
+    SignedExchangePolicyBrowserTest,
+    SignedExchangePolicyBrowserTest,
+    testing::Values(SignedExchangePolicyBrowserTestParam(false),
+                    SignedExchangePolicyBrowserTestParam(true)));
diff --git a/chrome/browser/local_discovery/service_discovery_client_mdns.cc b/chrome/browser/local_discovery/service_discovery_client_mdns.cc
index b713c397..d8767b13 100644
--- a/chrome/browser/local_discovery/service_discovery_client_mdns.cc
+++ b/chrome/browser/local_discovery/service_discovery_client_mdns.cc
@@ -21,7 +21,6 @@
 #include "content/public/browser/browser_task_traits.h"
 #include "content/public/browser/browser_thread.h"
 #include "content/public/browser/network_service_instance.h"
-#include "net/socket/datagram_client_socket.h"
 #include "net/socket/datagram_server_socket.h"
 
 namespace net {
@@ -132,17 +131,15 @@
       : interfaces_(interfaces), net_log_(net_log) {}
 
   // net::MDnsSocketFactory implementation:
-  void CreateSocketPairs(
-      std::vector<net::MDnsSendRecvSocketPair>* socket_pairs) override {
+  void CreateSockets(std::vector<std::unique_ptr<net::DatagramServerSocket>>*
+                         sockets) override {
     for (size_t i = 0; i < interfaces_.size(); ++i) {
       DCHECK(interfaces_[i].second == net::ADDRESS_FAMILY_IPV4 ||
              interfaces_[i].second == net::ADDRESS_FAMILY_IPV6);
-      net::MDnsSendRecvSocketPair socket_pair(CreateAndBindMDnsSocketPair(
+      std::unique_ptr<net::DatagramServerSocket> socket(CreateAndBindMDnsSocket(
           interfaces_[i].second, interfaces_[i].first, net_log_));
-      const auto& send_socket = socket_pair.first;
-      const auto& recv_socket = socket_pair.second;
-      if (send_socket && recv_socket)
-        socket_pairs->push_back(std::move(socket_pair));
+      if (socket)
+        sockets->push_back(std::move(socket));
     }
   }
 
diff --git a/chrome/browser/media/android/cdm/media_drm_origin_id_manager.cc b/chrome/browser/media/android/cdm/media_drm_origin_id_manager.cc
index 2d30da7..6e4f4b15a 100644
--- a/chrome/browser/media/android/cdm/media_drm_origin_id_manager.cc
+++ b/chrome/browser/media/android/cdm/media_drm_origin_id_manager.cc
@@ -8,6 +8,7 @@
 #include <utility>
 
 #include "base/bind.h"
+#include "base/feature_list.h"
 #include "base/logging.h"
 #include "base/task/post_task.h"
 #include "base/threading/thread_task_runner_handle.h"
@@ -22,6 +23,7 @@
 #include "content/public/browser/network_service_instance.h"
 #include "content/public/browser/provision_fetcher_factory.h"
 #include "media/base/android/media_drm_bridge.h"
+#include "media/base/media_switches.h"
 #include "media/base/provision_fetcher.h"
 #include "services/network/public/cpp/network_connection_tracker.h"
 #include "services/network/public/cpp/shared_url_loader_factory.h"
@@ -313,6 +315,16 @@
     : pref_service_(pref_service), weak_factory_(this) {
   DVLOG(1) << __func__;
   DCHECK(pref_service_);
+
+  // This manager can be started when the user's profile is loaded, if
+  // |kMediaDrmPreprovisioningAtStartup| is enabled. In that case attempt to
+  // pre-provisioning origin IDs if needed. If this manager is only loaded when
+  // needed (|kMediaDrmPreprovisioningAtStartup| not enabled), then the caller
+  // is most likely going to call GetOriginId(), so let it pre-provision origin
+  // IDs if necessary. This flag is also used by testing so that it can check
+  // pre-provisioning directly.
+  if (base::FeatureList::IsEnabled(media::kMediaDrmPreprovisioningAtStartup))
+    PreProvisionIfNecessary();
 }
 
 MediaDrmOriginIdManager::~MediaDrmOriginIdManager() {
diff --git a/chrome/browser/media/android/cdm/media_drm_origin_id_manager_factory.cc b/chrome/browser/media/android/cdm/media_drm_origin_id_manager_factory.cc
index 438834d..87106d0 100644
--- a/chrome/browser/media/android/cdm/media_drm_origin_id_manager_factory.cc
+++ b/chrome/browser/media/android/cdm/media_drm_origin_id_manager_factory.cc
@@ -6,11 +6,13 @@
 
 #include <utility>
 
+#include "base/feature_list.h"
 #include "base/logging.h"
 #include "base/memory/ptr_util.h"
 #include "chrome/browser/media/android/cdm/media_drm_origin_id_manager.h"
 #include "chrome/browser/profiles/profile.h"
 #include "components/keyed_service/content/browser_context_dependency_manager.h"
+#include "media/base/media_switches.h"
 
 // static
 MediaDrmOriginIdManager* MediaDrmOriginIdManagerFactory::GetForProfile(
@@ -45,3 +47,12 @@
 
   return context;
 }
+
+bool MediaDrmOriginIdManagerFactory::ServiceIsCreatedWithBrowserContext()
+    const {
+  // Create this service when the context is created if it should perform
+  // pre-provisioning at startup. Creation will end up calling
+  // GetBrowserContextToUse() above which returns NULL for incognito contexts,
+  // and thus no instance will be created for them.
+  return base::FeatureList::IsEnabled(media::kMediaDrmPreprovisioningAtStartup);
+}
diff --git a/chrome/browser/media/android/cdm/media_drm_origin_id_manager_factory.h b/chrome/browser/media/android/cdm/media_drm_origin_id_manager_factory.h
index 95b5642..ed6004b 100644
--- a/chrome/browser/media/android/cdm/media_drm_origin_id_manager_factory.h
+++ b/chrome/browser/media/android/cdm/media_drm_origin_id_manager_factory.h
@@ -36,6 +36,8 @@
 
   content::BrowserContext* GetBrowserContextToUse(
       content::BrowserContext* context) const override;
+
+  bool ServiceIsCreatedWithBrowserContext() const override;
 };
 
 #endif  // CHROME_BROWSER_MEDIA_ANDROID_CDM_MEDIA_DRM_ORIGIN_ID_MANAGER_FACTORY_H_
diff --git a/chrome/browser/media/android/cdm/media_drm_origin_id_manager_unittest.cc b/chrome/browser/media/android/cdm/media_drm_origin_id_manager_unittest.cc
index 1eb597e..faad038 100644
--- a/chrome/browser/media/android/cdm/media_drm_origin_id_manager_unittest.cc
+++ b/chrome/browser/media/android/cdm/media_drm_origin_id_manager_unittest.cc
@@ -14,6 +14,7 @@
 #include "base/optional.h"
 #include "base/run_loop.h"
 #include "base/stl_util.h"
+#include "base/test/scoped_feature_list.h"
 #include "base/test/scoped_task_environment.h"
 #include "base/unguessable_token.h"
 #include "base/value_conversions.h"
@@ -22,6 +23,7 @@
 #include "components/sync_preferences/testing_pref_service_syncable.h"
 #include "content/public/test/test_browser_thread_bundle.h"
 #include "media/base/android/media_drm_bridge.h"
+#include "media/base/media_switches.h"
 #include "services/network/test/test_network_connection_tracker.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
@@ -45,8 +47,15 @@
 
 class MediaDrmOriginIdManagerTest : public testing::Test {
  public:
-  MediaDrmOriginIdManagerTest() {
-    profile_ = std::make_unique<TestingProfile>();
+  // By default MediaDrmOriginIdManager will attempt to pre-provision origin
+  // IDs at startup. For most tests this should be disabled.
+  void Initialize(bool enable_preprovision_at_startup = false) {
+    scoped_feature_list_.InitWithFeatureState(
+        media::kMediaDrmPreprovisioningAtStartup,
+        enable_preprovision_at_startup);
+
+    TestingProfile::Builder profile_builder;
+    profile_ = profile_builder.Build();
     origin_id_manager_ =
         MediaDrmOriginIdManagerFactory::GetForProfile(profile_.get());
     origin_id_manager_->SetProvisioningResultCBForTesting(
@@ -77,7 +86,6 @@
 
   void PreProvision() {
     origin_id_manager_->PreProvisionIfNecessary();
-    test_browser_thread_bundle_.RunUntilIdle();
   }
 
   std::string DisplayPref(const base::Value* value) {
@@ -95,26 +103,73 @@
     return profile_->GetTestingPrefService()->GetDictionary(path);
   }
 
+  // On devices that support per-application provisioning pre-provisioning
+  // should fully populate the list of pre-provisioned origin IDs (as long as
+  // provisioning succeeds). On devices that don't the list should be empty.
+  void CheckPreferenceForPreProvisioning() {
+    DVLOG(1) << "Checking preference " << kMediaDrmOriginIds;
+    auto* pref = FindPreference(kMediaDrmOriginIds);
+    EXPECT_TRUE(pref);
+    EXPECT_EQ(kMediaDrmOriginIds, pref->name());
+    EXPECT_EQ(base::Value::Type::DICTIONARY, pref->GetType());
+
+    auto* dict = pref->GetValue();
+    EXPECT_TRUE(dict->is_dict());
+    DVLOG(1) << DisplayPref(pref->GetValue());
+
+    auto* list = dict->FindKey(kAvailableOriginIds);
+    if (media::MediaDrmBridge::IsPerApplicationProvisioningSupported()) {
+      // PreProvision() should have pre-provisioned
+      // |kExpectedPreferenceListSize| origin IDs.
+      DVLOG(1) << "Per-application provisioning is supported.";
+      EXPECT_TRUE(list->is_list());
+      EXPECT_EQ(list->GetList().size(), kExpectedPreferenceListSize);
+    } else {
+      // No pre-provisioned origin IDs should exist. In fact, the dictionary
+      // should not have any entries.
+      DVLOG(1) << "Per-application provisioning is NOT supported.";
+      EXPECT_FALSE(list);
+      EXPECT_EQ(dict->DictSize(), 0u);
+    }
+  }
+
  protected:
   content::TestBrowserThreadBundle test_browser_thread_bundle_{
       base::test::ScopedTaskEnvironment::MainThreadType::MOCK_TIME,
       base::test::ScopedTaskEnvironment::NowSource::MAIN_THREAD_MOCK_TIME};
+  base::test::ScopedFeatureList scoped_feature_list_;
   std::unique_ptr<TestingProfile> profile_;
   MediaDrmOriginIdManager* origin_id_manager_;
 };
 
-TEST_F(MediaDrmOriginIdManagerTest, Creation) {
+TEST_F(MediaDrmOriginIdManagerTest, DisablePreProvisioningAtStartup) {
   // Test verifies that the construction of MediaDrmOriginIdManager is
-  // successful.
+  // successful. Pre-provisioning origin IDs at startup should be disabled
+  // so no calls to GetProvisioningResult() are expected.
+  Initialize();
+
+  EXPECT_FALSE(
+      base::FeatureList::IsEnabled(media::kMediaDrmPreprovisioningAtStartup));
+
+  test_browser_thread_bundle_.RunUntilIdle();
+
+  // Preference should not exist. Not using GetDictionary() as it will
+  // create the preference if it doesn't exist.
+  EXPECT_FALSE(
+      profile_->GetTestingPrefService()->HasPrefPath(kMediaDrmOriginIds));
 }
 
 TEST_F(MediaDrmOriginIdManagerTest, OneOriginId) {
   EXPECT_CALL(*this, GetProvisioningResult()).WillRepeatedly(Return(true));
+  Initialize();
+
   EXPECT_TRUE(GetOriginId());
 }
 
 TEST_F(MediaDrmOriginIdManagerTest, TwoOriginIds) {
   EXPECT_CALL(*this, GetProvisioningResult()).WillRepeatedly(Return(true));
+  Initialize();
+
   MediaDrmOriginId origin_id1 = GetOriginId();
   MediaDrmOriginId origin_id2 = GetOriginId();
   EXPECT_TRUE(origin_id1);
@@ -125,45 +180,34 @@
 TEST_F(MediaDrmOriginIdManagerTest, PreProvision) {
   // On devices that support per-application provisioning PreProvision() will
   // pre-provisioned several origin IDs and populate the preference. On devices
-  // that don't, the list will be empty. Note that simply finding the preference
-  // creates an empty one (as FindPreference() only returns NULL if the
-  // preference is not registered).
+  // that don't, the list will be empty.
   EXPECT_CALL(*this, GetProvisioningResult()).WillRepeatedly(Return(true));
+  Initialize();
+
   PreProvision();
+  test_browser_thread_bundle_.RunUntilIdle();
 
-  DVLOG(1) << "Checking preference " << kMediaDrmOriginIds;
-  auto* pref = FindPreference(kMediaDrmOriginIds);
-  EXPECT_TRUE(pref);
-  EXPECT_EQ(kMediaDrmOriginIds, pref->name());
-  EXPECT_EQ(base::Value::Type::DICTIONARY, pref->GetType());
+  CheckPreferenceForPreProvisioning();
+}
 
-  auto* dict = pref->GetValue();
-  EXPECT_TRUE(dict->is_dict());
-  DVLOG(1) << DisplayPref(pref->GetValue());
+TEST_F(MediaDrmOriginIdManagerTest, PreProvisionAtStartup) {
+  // Initialize without disabling kMediaDrmPreprovisioningAtStartup. Check
+  // that pre-provisioning actually runs at profile creation (on devices
+  // that support it).
+  EXPECT_CALL(*this, GetProvisioningResult()).WillRepeatedly(Return(true));
+  Initialize(true);
+  test_browser_thread_bundle_.RunUntilIdle();
 
-  if (media::MediaDrmBridge::IsPerApplicationProvisioningSupported()) {
-    DVLOG(1) << "Per-application provisioning is supported.";
-
-    // PreProvision() should have pre-provisioned |kExpectedPreferenceListSize|
-    // origin IDs.
-    auto* list = dict->FindKey(kAvailableOriginIds);
-    EXPECT_TRUE(list->is_list());
-    EXPECT_EQ(list->GetList().size(), kExpectedPreferenceListSize);
-  } else {
-    DVLOG(1) << "Per-application provisioning is NOT supported.";
-
-    // No pre-provisioned origin IDs should exist. In fact, the dictionary
-    // should not have any entries.
-    auto* list = dict->FindKey(kAvailableOriginIds);
-    EXPECT_FALSE(list);
-    EXPECT_EQ(dict->DictSize(), 0u);
-  }
+  CheckPreferenceForPreProvisioning();
 }
 
 TEST_F(MediaDrmOriginIdManagerTest, GetOriginIdCreatesList) {
   // After fetching an origin ID the code should pre-provision more origins
-  // and fill up the list.
+  // and fill up the list. This is independent of whether the device supports
+  // per-application provisioning or not.
   EXPECT_CALL(*this, GetProvisioningResult()).WillRepeatedly(Return(true));
+  Initialize();
+
   GetOriginId();
   test_browser_thread_bundle_.RunUntilIdle();
 
@@ -185,6 +229,8 @@
   // of pre-provisioned origin IDs (asynchronously). It doesn't matter if the
   // device supports per-application provisioning or not.
   EXPECT_CALL(*this, GetProvisioningResult()).WillRepeatedly(Return(true));
+  Initialize();
+
   MediaDrmOriginId origin_id = GetOriginId();
   test_browser_thread_bundle_.RunUntilIdle();
 
@@ -199,16 +245,23 @@
 TEST_F(MediaDrmOriginIdManagerTest, ProvisioningFail) {
   // Provisioning fails, so GetOriginId() returns an empty origin ID.
   EXPECT_CALL(*this, GetProvisioningResult()).WillOnce(testing::Return(false));
+  Initialize();
+
   EXPECT_FALSE(GetOriginId());
 
+  test_browser_thread_bundle_.RunUntilIdle();
+
   // After failure the preference should contain |kExpireableToken| only if
-  // per-application provisioning is not supported.
+  // per-application provisioning is NOT supported.
   DVLOG(1) << "Checking preference " << kMediaDrmOriginIds;
   auto* dict = GetDictionary(kMediaDrmOriginIds);
   DVLOG(1) << DisplayPref(dict);
+
   if (media::MediaDrmBridge::IsPerApplicationProvisioningSupported()) {
+    DVLOG(1) << "Per-application provisioning is supported.";
     EXPECT_FALSE(dict->FindKey(kExpirableToken));
   } else {
+    DVLOG(1) << "Per-application provisioning is NOT supported.";
     EXPECT_TRUE(dict->FindKey(kExpirableToken));
   }
 }
@@ -218,6 +271,8 @@
   EXPECT_CALL(*this, GetProvisioningResult())
       .WillOnce(Return(false))
       .WillRepeatedly(Return(true));
+  Initialize();
+
   EXPECT_FALSE(GetOriginId());
   EXPECT_TRUE(GetOriginId());  // Provisioning will succeed on the second call.
 
@@ -229,6 +284,11 @@
   auto* dict = GetDictionary(kMediaDrmOriginIds);
   DVLOG(1) << DisplayPref(dict);
   EXPECT_FALSE(dict->FindKey(kExpirableToken));
+
+  // As well, the list of available pre-provisioned origin IDs should be full.
+  auto* list = dict->FindKey(kAvailableOriginIds);
+  EXPECT_TRUE(list->is_list());
+  EXPECT_EQ(list->GetList().size(), kExpectedPreferenceListSize);
 }
 
 TEST_F(MediaDrmOriginIdManagerTest, ProvisioningAfterExpiration) {
@@ -237,6 +297,8 @@
   EXPECT_CALL(*this, GetProvisioningResult())
       .WillOnce(Return(false))
       .WillRepeatedly(Return(true));
+  Initialize();
+
   EXPECT_FALSE(GetOriginId());
   test_browser_thread_bundle_.RunUntilIdle();
 
@@ -271,6 +333,7 @@
     // to pre-provision origin IDs any time.
     DVLOG(1) << "Per-application provisioning is supported.";
     EXPECT_EQ(list->GetList().size(), kExpectedPreferenceListSize);
+    EXPECT_FALSE(dict->FindKey(kExpirableToken));
   } else {
     // Per-application provisioning is not supported, so attempting to
     // pre-provision origin IDs after |kExpirationDelta| should not do anything.
@@ -283,6 +346,7 @@
 
 TEST_F(MediaDrmOriginIdManagerTest, Incognito) {
   // No MediaDrmOriginIdManager should be created for an incognito profile.
+  Initialize();
   auto* incognito_profile = profile_->GetOffTheRecordProfile();
   EXPECT_FALSE(
       MediaDrmOriginIdManagerFactory::GetForProfile(incognito_profile));
@@ -298,6 +362,8 @@
   EXPECT_CALL(*this, GetProvisioningResult())
       .WillOnce(Return(false))
       .WillRepeatedly(Return(true));
+  Initialize();
+
   EXPECT_FALSE(GetOriginId());
   test_browser_thread_bundle_.RunUntilIdle();
 
@@ -345,6 +411,8 @@
   EXPECT_CALL(*this, GetProvisioningResult())
       .Times(kConnectionAttempts + 1)
       .WillOnce(Return(false));
+  Initialize();
+
   EXPECT_FALSE(GetOriginId());
   test_browser_thread_bundle_.RunUntilIdle();
 
diff --git a/chrome/browser/media/media_engagement_service.cc b/chrome/browser/media/media_engagement_service.cc
index 6f925648..36efee3 100644
--- a/chrome/browser/media/media_engagement_service.cc
+++ b/chrome/browser/media/media_engagement_service.cc
@@ -4,6 +4,8 @@
 
 #include "chrome/browser/media/media_engagement_service.h"
 
+#include <functional>
+
 #include "base/bind.h"
 #include "base/metrics/histogram_macros.h"
 #include "base/time/clock.h"
@@ -263,7 +265,7 @@
       ->ClearSettingsForOneTypeWithPredicate(
           CONTENT_SETTINGS_TYPE_MEDIA_ENGAGEMENT, base::Time(),
           base::Time::Max(),
-          base::Bind(&MediaEngagementFilterAdapter, base::ConstRef(url)));
+          base::Bind(&MediaEngagementFilterAdapter, std::cref(url)));
 }
 
 double MediaEngagementService::GetEngagementScore(const GURL& url) const {
diff --git a/chrome/browser/media/router/discovery/discovery_network_monitor_unittest.cc b/chrome/browser/media/router/discovery/discovery_network_monitor_unittest.cc
index 13e2735..01ffa80 100644
--- a/chrome/browser/media/router/discovery/discovery_network_monitor_unittest.cc
+++ b/chrome/browser/media/router/discovery/discovery_network_monitor_unittest.cc
@@ -4,6 +4,7 @@
 
 #include "chrome/browser/media/router/discovery/discovery_network_monitor.h"
 
+#include <functional>
 #include <memory>
 
 #include "base/bind.h"
@@ -170,7 +171,7 @@
     EXPECT_EQ(refresh_network_id, network_id);
   };
   discovery_network_monitor->GetNetworkId(
-      base::BindOnce(check_network_id, base::ConstRef(current_network_id)));
+      base::BindOnce(check_network_id, std::cref(current_network_id)));
   thread_bundle.RunUntilIdle();
 }
 
diff --git a/chrome/browser/media_galleries/media_file_system_registry_unittest.cc b/chrome/browser/media_galleries/media_file_system_registry_unittest.cc
index dffa9da..95d8319 100644
--- a/chrome/browser/media_galleries/media_file_system_registry_unittest.cc
+++ b/chrome/browser/media_galleries/media_file_system_registry_unittest.cc
@@ -9,6 +9,7 @@
 #include <stddef.h>
 
 #include <algorithm>
+#include <functional>
 #include <memory>
 #include <set>
 #include <vector>
@@ -514,8 +515,7 @@
       single_web_contents_.get(), no_permissions_extension_.get(),
       base::Bind(&ProfileState::CompareResults, base::Unretained(this),
                  base::StringPrintf("%s (no permission)", test.c_str()),
-                 base::ConstRef(empty_names),
-                 base::ConstRef(empty_expectation)));
+                 std::cref(empty_names), std::cref(empty_expectation)));
   content::RunAllTasksUntilIdle();
   EXPECT_EQ(1, GetAndClearComparisonCount());
 
@@ -524,8 +524,8 @@
       single_web_contents_.get(), regular_permission_extension_.get(),
       base::Bind(&ProfileState::CompareResults, base::Unretained(this),
                  base::StringPrintf("%s (regular permission)", test.c_str()),
-                 base::ConstRef(compare_names_read_),
-                 base::ConstRef(regular_extension_galleries)));
+                 std::cref(compare_names_read_),
+                 std::cref(regular_extension_galleries)));
   content::RunAllTasksUntilIdle();
   EXPECT_EQ(1, GetAndClearComparisonCount());
 
@@ -534,8 +534,8 @@
       single_web_contents_.get(), all_permission_extension_.get(),
       base::Bind(&ProfileState::CompareResults, base::Unretained(this),
                  base::StringPrintf("%s (all permission)", test.c_str()),
-                 base::ConstRef(compare_names_all_),
-                 base::ConstRef(all_extension_galleries)));
+                 std::cref(compare_names_all_),
+                 std::cref(all_extension_galleries)));
   content::RunAllTasksUntilIdle();
   EXPECT_EQ(1, GetAndClearComparisonCount());
 }
diff --git a/chrome/browser/metrics/process_memory_metrics_emitter.cc b/chrome/browser/metrics/process_memory_metrics_emitter.cc
index b5d2075..c512785 100644
--- a/chrome/browser/metrics/process_memory_metrics_emitter.cc
+++ b/chrome/browser/metrics/process_memory_metrics_emitter.cc
@@ -662,6 +662,17 @@
   }
 
   if (emit_metrics_for_all_processes) {
+    size_t native_resident_kb =
+        global_dump_->aggregated_metrics().native_library_resident_kb();
+
+    // |native_resident_kb| is only calculated for android devices that support
+    // code ordering. Otherwise it is equal to zero and should not be reported.
+    if (native_resident_kb != 0) {
+      MEMORY_METRICS_HISTOGRAM_KB(
+          "Memory.NativeLibrary.MappedAndResidentMemoryFootprint",
+          native_resident_kb);
+    }
+
     UMA_HISTOGRAM_MEMORY_LARGE_MB(
         "Memory.Experimental.Total2.PrivateMemoryFootprint",
         private_footprint_total_kb / 1024);
diff --git a/chrome/browser/metrics/process_memory_metrics_emitter_unittest.cc b/chrome/browser/metrics/process_memory_metrics_emitter_unittest.cc
index bd99f8c..9e18b36 100644
--- a/chrome/browser/metrics/process_memory_metrics_emitter_unittest.cc
+++ b/chrome/browser/metrics/process_memory_metrics_emitter_unittest.cc
@@ -1,4 +1,4 @@
-// Copyright kTestRendererPid2017 The Chromium Authors. All rights reserved.
+// 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.
 
@@ -308,6 +308,7 @@
 
 constexpr int kTestRendererPrivateMemoryFootprint = 130;
 constexpr int kTestRendererSharedMemoryFootprint = 135;
+constexpr int kNativeLibraryResidentMemoryFootprint = 27560;
 
 #if !defined(OS_MACOSX)
 constexpr int kTestRendererResidentSet = 110;
@@ -804,9 +805,13 @@
 
   GlobalMemoryDumpPtr global_dump(
       memory_instrumentation::mojom::GlobalMemoryDump::New());
+  global_dump->aggregated_metrics =
+      memory_instrumentation::mojom::AggregatedMetrics::New();
   MetricMap expected_metrics = GetExpectedRendererMetrics();
   PopulateRendererMetrics(global_dump, expected_metrics, kTestRendererPid201);
   PopulateRendererMetrics(global_dump, expected_metrics, kTestRendererPid202);
+  global_dump->aggregated_metrics->native_library_resident_kb =
+      kNativeLibraryResidentMemoryFootprint;
 
   // No histograms should have been recorded yet.
   histograms.ExpectTotalCount("Memory.Renderer.PrivateMemoryFootprint", 0);
@@ -817,6 +822,8 @@
   histograms.ExpectTotalCount("Memory.Total.RendererPrivateMemoryFootprint", 0);
   histograms.ExpectTotalCount("Memory.Total.SharedMemoryFootprint", 0);
   histograms.ExpectTotalCount("Memory.Total.ResidentSet", 0);
+  histograms.ExpectTotalCount(
+      "Memory.NativeLibrary.MappedAndResidentMemoryFootprint", 0);
 
   // Simulate some metrics emission.
   scoped_refptr<ProcessMemoryMetricsEmitterFake> emitter =
@@ -849,6 +856,9 @@
   histograms.ExpectUniqueSample("Memory.Total.ResidentSet",
                                 2 * kTestRendererResidentSet, 1);
 #endif
+  histograms.ExpectUniqueSample(
+      "Memory.NativeLibrary.MappedAndResidentMemoryFootprint",
+      kNativeLibraryResidentMemoryFootprint, 1);
 }
 
 TEST_F(ProcessMemoryMetricsEmitterTest, MainFramePMFEmitted) {
diff --git a/chrome/browser/net/dns_probe_browsertest.cc b/chrome/browser/net/dns_probe_browsertest.cc
index a16abd3c..a6de880b 100644
--- a/chrome/browser/net/dns_probe_browsertest.cc
+++ b/chrome/browser/net/dns_probe_browsertest.cc
@@ -45,7 +45,6 @@
 using base::BindOnce;
 using base::Callback;
 using base::Closure;
-using base::ConstRef;
 using base::FilePath;
 using base::Unretained;
 using content::BrowserThread;
diff --git a/chrome/browser/notifications/BUILD.gn b/chrome/browser/notifications/BUILD.gn
index df9627a..1390b7f 100644
--- a/chrome/browser/notifications/BUILD.gn
+++ b/chrome/browser/notifications/BUILD.gn
@@ -8,3 +8,10 @@
     "//chrome/browser/notifications/scheduler",
   ]
 }
+
+group("unit_tests") {
+  testonly = true
+  deps = [
+    "//chrome/browser/notifications/scheduler:unit_tests",
+  ]
+}
diff --git a/chrome/browser/notifications/fullscreen_notification_blocker.cc b/chrome/browser/notifications/fullscreen_notification_blocker.cc
index ad10782..5429c12 100644
--- a/chrome/browser/notifications/fullscreen_notification_blocker.cc
+++ b/chrome/browser/notifications/fullscreen_notification_blocker.cc
@@ -6,19 +6,16 @@
 
 #include "base/metrics/histogram_macros.h"
 #include "base/time/time.h"
-#include "chrome/browser/chrome_notification_types.h"
 #include "chrome/browser/fullscreen.h"
-#include "content/public/browser/notification_service.h"
-#include "ui/message_center/public/cpp/notifier_id.h"
 
-using message_center::NotifierId;
+namespace {
+const int kFullscreenStatePollingIntervalSeconds = 1;
+}
 
 FullscreenNotificationBlocker::FullscreenNotificationBlocker(
     message_center::MessageCenter* message_center)
     : NotificationBlocker(message_center),
       is_fullscreen_mode_(false) {
-  registrar_.Add(this, chrome::NOTIFICATION_FULLSCREEN_CHANGED,
-                 content::NotificationService::AllSources());
 }
 
 FullscreenNotificationBlocker::~FullscreenNotificationBlocker() {
@@ -29,6 +26,13 @@
   is_fullscreen_mode_ = IsFullScreenMode();
   if (is_fullscreen_mode_ != was_fullscreen_mode)
     NotifyBlockingStateChanged();
+
+  if (is_fullscreen_mode_) {
+    timer_.Start(
+        FROM_HERE,
+        base::TimeDelta::FromSeconds(kFullscreenStatePollingIntervalSeconds),
+        this, &FullscreenNotificationBlocker::CheckState);
+  }
 }
 
 bool FullscreenNotificationBlocker::ShouldShowNotificationAsPopup(
@@ -44,11 +48,3 @@
 
   return enabled;
 }
-
-void FullscreenNotificationBlocker::Observe(
-    int type,
-    const content::NotificationSource& source,
-    const content::NotificationDetails& details) {
-  DCHECK_EQ(chrome::NOTIFICATION_FULLSCREEN_CHANGED, type);
-  CheckState();
-}
diff --git a/chrome/browser/notifications/fullscreen_notification_blocker.h b/chrome/browser/notifications/fullscreen_notification_blocker.h
index 99c5d4c..dc7de5de 100644
--- a/chrome/browser/notifications/fullscreen_notification_blocker.h
+++ b/chrome/browser/notifications/fullscreen_notification_blocker.h
@@ -6,15 +6,13 @@
 #define CHROME_BROWSER_NOTIFICATIONS_FULLSCREEN_NOTIFICATION_BLOCKER_H_
 
 #include "base/macros.h"
-#include "content/public/browser/notification_observer.h"
-#include "content/public/browser/notification_registrar.h"
+#include "base/timer/timer.h"
 #include "ui/message_center/notification_blocker.h"
 
 // A notification blocker which checks the fullscreen state. This is not used on
 // ChromeOS as ash has its own fullscreen notification blocker.
 class FullscreenNotificationBlocker
-    : public message_center::NotificationBlocker,
-      public content::NotificationObserver {
+    : public message_center::NotificationBlocker {
  public:
   explicit FullscreenNotificationBlocker(
       message_center::MessageCenter* message_center);
@@ -26,14 +24,9 @@
       const message_center::Notification& notification) const override;
 
  private:
-  // content::NotificationObserver override.
-  void Observe(int type,
-               const content::NotificationSource& source,
-               const content::NotificationDetails& details) override;
-
   bool is_fullscreen_mode_;
 
-  content::NotificationRegistrar registrar_;
+  base::OneShotTimer timer_;
 
   DISALLOW_COPY_AND_ASSIGN(FullscreenNotificationBlocker);
 };
diff --git a/chrome/browser/notifications/notification_display_service_impl.cc b/chrome/browser/notifications/notification_display_service_impl.cc
index 8e86371..765345c3 100644
--- a/chrome/browser/notifications/notification_display_service_impl.cc
+++ b/chrome/browser/notifications/notification_display_service_impl.cc
@@ -31,6 +31,10 @@
 #include "chrome/browser/notifications/notification_platform_bridge_message_center.h"
 #endif
 
+#if defined(OS_LINUX) || defined(OS_MACOSX)
+#include "chrome/browser/send_tab_to_self/desktop_notification_handler.h"
+#endif
+
 #if defined(OS_WIN)
 #include "base/strings/utf_string_conversions.h"
 #include "chrome/browser/notifications/notification_platform_bridge_win.h"
@@ -116,6 +120,14 @@
         std::make_unique<NonPersistentNotificationHandler>());
     AddNotificationHandler(NotificationHandler::Type::WEB_PERSISTENT,
                            std::make_unique<PersistentNotificationHandler>());
+
+#if defined(OS_LINUX) || defined(OS_MACOSX)
+    AddNotificationHandler(
+        NotificationHandler::Type::SEND_TAB_TO_SELF,
+        std::make_unique<send_tab_to_self::DesktopNotificationHandler>(
+            profile));
+#endif
+
 #if BUILDFLAG(ENABLE_EXTENSIONS)
     AddNotificationHandler(
         NotificationHandler::Type::EXTENSION,
diff --git a/chrome/browser/notifications/notification_handler.h b/chrome/browser/notifications/notification_handler.h
index e9e9f0b..877f5bb 100644
--- a/chrome/browser/notifications/notification_handler.h
+++ b/chrome/browser/notifications/notification_handler.h
@@ -25,7 +25,8 @@
     WEB_PERSISTENT = 0,
     WEB_NON_PERSISTENT = 1,
     EXTENSION = 2,
-    TRANSIENT = 3,  // A generic type for any notification that does not outlive
+    SEND_TAB_TO_SELF = 3,
+    TRANSIENT = 4,  // A generic type for any notification that does not outlive
                     // the browser instance and is controlled by a
                     // NotificationDelegate.
     MAX = TRANSIENT,
diff --git a/chrome/browser/notifications/notification_platform_bridge_linux.cc b/chrome/browser/notifications/notification_platform_bridge_linux.cc
index d3f80d5..aa6d15f 100644
--- a/chrome/browser/notifications/notification_platform_bridge_linux.cc
+++ b/chrome/browser/notifications/notification_platform_bridge_linux.cc
@@ -631,7 +631,8 @@
       actions.push_back(kDefaultButtonId);
       actions.push_back("Activate");
       // Always add a settings button for web notifications.
-      if (notification_type != NotificationHandler::Type::EXTENSION) {
+      if (notification_type != NotificationHandler::Type::EXTENSION &&
+          notification_type != NotificationHandler::Type::SEND_TAB_TO_SELF) {
         actions.push_back(kSettingsButtonId);
         actions.push_back(
             l10n_util::GetStringUTF8(IDS_NOTIFICATION_BUTTON_SETTINGS));
diff --git a/chrome/browser/notifications/notification_platform_bridge_mac.mm b/chrome/browser/notifications/notification_platform_bridge_mac.mm
index db62e9d..08078c5f 100644
--- a/chrome/browser/notifications/notification_platform_bridge_mac.mm
+++ b/chrome/browser/notifications/notification_platform_bridge_mac.mm
@@ -249,8 +249,11 @@
     [builder setIcon:notification.icon().ToNSImage()];
   }
 
-  [builder setShowSettingsButton:(notification_type !=
-                                  NotificationHandler::Type::EXTENSION)];
+  [builder
+      setShowSettingsButton:(notification_type !=
+                                 NotificationHandler::Type::EXTENSION &&
+                             notification_type !=
+                                 NotificationHandler::Type::SEND_TAB_TO_SELF)];
   std::vector<message_center::ButtonInfo> buttons = notification.buttons();
   if (!buttons.empty()) {
     DCHECK_LE(buttons.size(), blink::kWebNotificationMaxActions);
diff --git a/chrome/browser/notifications/scheduler/BUILD.gn b/chrome/browser/notifications/scheduler/BUILD.gn
index 02f3450..46880a6 100644
--- a/chrome/browser/notifications/scheduler/BUILD.gn
+++ b/chrome/browser/notifications/scheduler/BUILD.gn
@@ -41,13 +41,32 @@
 # Internal library that embedders should not directly depend on.
 source_set("lib") {
   sources = [
+    "collection_store.h",
     "notification_schedule_service_impl.cc",
     "notification_schedule_service_impl.h",
+    "proto_db_collection_store.cc",
+    "proto_db_collection_store.h",
   ]
 
   deps = [
     ":public",
     "//base",
     "//components/keyed_service/core",
+    "//components/leveldb_proto",
+  ]
+}
+
+source_set("unit_tests") {
+  testonly = true
+  sources = [
+    "proto_db_collection_store_unittest.cc",
+  ]
+
+  deps = [
+    ":lib",
+    ":public",
+    "//components/leveldb_proto:test_support",
+    "//testing/gmock",
+    "//testing/gtest",
   ]
 }
diff --git a/chrome/browser/notifications/scheduler/collection_store.h b/chrome/browser/notifications/scheduler/collection_store.h
new file mode 100644
index 0000000..46dac7e
--- /dev/null
+++ b/chrome/browser/notifications/scheduler/collection_store.h
@@ -0,0 +1,54 @@
+// 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_NOTIFICATIONS_SCHEDULER_COLLECTION_STORE_H_
+#define CHROME_BROWSER_NOTIFICATIONS_SCHEDULER_COLLECTION_STORE_H_
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "base/callback.h"
+#include "base/macros.h"
+
+namespace notifications {
+
+// A storage interface which loads a collection of data type T into memory
+// during initialization.
+template <typename T>
+class CollectionStore {
+ public:
+  using Entries = std::unique_ptr<std::vector<T>>;
+  using LoadCallback = base::OnceCallback<void(bool, Entries)>;
+  using InitCallback = base::OnceCallback<void(bool)>;
+  using UpdateCallback = base::OnceCallback<void(bool)>;
+
+ protected:
+  // Initializes the database.
+  virtual void Init(InitCallback callback) = 0;
+
+  // Initializes the database and loads all entries into memory.
+  virtual void InitAndLoad(LoadCallback callback) = 0;
+
+  // Loads one entry into memory.
+  virtual void Load(const std::string& key, LoadCallback callback) = 0;
+
+  // Adds an entry to the storage.
+  virtual void Add(const std::string& key,
+                   const T& entry,
+                   UpdateCallback callback) = 0;
+
+  // Deletes an entry from storage.
+  virtual void Delete(const std::string& key, UpdateCallback callback) = 0;
+
+  CollectionStore() = default;
+  virtual ~CollectionStore() = default;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(CollectionStore);
+};
+
+}  // namespace notifications
+
+#endif  // CHROME_BROWSER_NOTIFICATIONS_SCHEDULER_COLLECTION_STORE_H_
diff --git a/chrome/browser/notifications/scheduler/proto_db_collection_store.cc b/chrome/browser/notifications/scheduler/proto_db_collection_store.cc
new file mode 100644
index 0000000..e35382a0
--- /dev/null
+++ b/chrome/browser/notifications/scheduler/proto_db_collection_store.cc
@@ -0,0 +1,14 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/notifications/scheduler/proto_db_collection_store.h"
+
+namespace notifications {
+
+bool ExactMatchKeyFilter(const std::string& key_to_load,
+                         const std::string& current_key) {
+  return key_to_load == current_key;
+}
+
+}  // namespace notifications
diff --git a/chrome/browser/notifications/scheduler/proto_db_collection_store.h b/chrome/browser/notifications/scheduler/proto_db_collection_store.h
new file mode 100644
index 0000000..a353123
--- /dev/null
+++ b/chrome/browser/notifications/scheduler/proto_db_collection_store.h
@@ -0,0 +1,142 @@
+// 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_NOTIFICATIONS_SCHEDULER_PROTO_DB_COLLECTION_STORE_H_
+#define CHROME_BROWSER_NOTIFICATIONS_SCHEDULER_PROTO_DB_COLLECTION_STORE_H_
+
+#include <memory>
+#include <string>
+#include <utility>
+#include <vector>
+
+#include "base/bind.h"
+#include "base/callback.h"
+#include "base/macros.h"
+#include "base/memory/weak_ptr.h"
+#include "chrome/browser/notifications/scheduler/collection_store.h"
+#include "components/leveldb_proto/public/proto_database.h"
+
+namespace notifications {
+
+bool ExactMatchKeyFilter(const std::string& key_to_load,
+                         const std::string& current_key);
+
+// A storage that uses level db which persists protobuffer type P, to load
+// collection of data type T into memory.
+// Subclass should implement the proto, entry conversion.(T <==> P)
+template <typename T, typename P>
+class ProtoDbCollectionStore : public CollectionStore<T> {
+ public:
+  using Entries = typename CollectionStore<T>::Entries;
+  using InitCallback = typename CollectionStore<T>::InitCallback;
+  using LoadCallback = typename CollectionStore<T>::LoadCallback;
+  using UpdateCallback = typename CollectionStore<T>::UpdateCallback;
+  using KeyProtoVector = std::vector<std::pair<std::string, P>>;
+  using KeyVector = std::vector<std::string>;
+
+  // Database configuration.
+  struct DbConfig {
+    std::string client_name;
+  };
+
+  ProtoDbCollectionStore(std::unique_ptr<leveldb_proto::ProtoDatabase<P>> db,
+                         const std::string& db_client_name)
+      : db_(std::move(db)),
+        db_client_name_(db_client_name),
+        weak_ptr_factory_(this) {
+    DCHECK(db_);
+  }
+
+  ~ProtoDbCollectionStore() override = default;
+
+  // CollectionStore<T> implementation.
+  void Init(InitCallback callback) override {
+    db_->Init(
+        db_client_name_,
+        base::BindOnce(&ProtoDbCollectionStore::OnDbInitialized,
+                       weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
+  }
+
+  void InitAndLoad(LoadCallback callback) override {
+    db_->Init(
+        db_client_name_,
+        base::BindOnce(&ProtoDbCollectionStore::OnDbInitializedBeforeLoad,
+                       weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
+  }
+
+  void Load(const std::string& key, LoadCallback callback) override {
+    db_->LoadEntriesWithFilter(
+        base::BindRepeating(&ExactMatchKeyFilter, key),
+        base::BindOnce(&ProtoDbCollectionStore::OnEntriesLoaded,
+                       weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
+  }
+
+  void Add(const std::string& key,
+           const T& entry,
+           UpdateCallback callback) override {
+    auto protos_to_save = std::make_unique<KeyProtoVector>();
+    protos_to_save->emplace_back(std::make_pair(key, EntryToProto(entry)));
+    db_->UpdateEntries(std::move(protos_to_save), std::make_unique<KeyVector>(),
+                       std::move(callback));
+  }
+
+  void Delete(const std::string& key, UpdateCallback callback) override {
+    auto keys_to_delete = std::make_unique<KeyVector>();
+    keys_to_delete->emplace_back(key);
+    db_->UpdateEntries(std::make_unique<KeyProtoVector>(),
+                       std::move(keys_to_delete), std::move(callback));
+  }
+
+ protected:
+  // Conversion from in memory entry type T to protobuff type P.
+  virtual P EntryToProto(const T& entry) = 0;
+
+  // Conversion from protobuff type P to in memory entry type T.
+  virtual T ProtoToEntry(const P& proto) = 0;
+
+ private:
+  void OnDbInitialized(InitCallback callback,
+                       leveldb_proto::Enums::InitStatus status) {
+    bool success = (status == leveldb_proto::Enums::InitStatus::kOK);
+    std::move(callback).Run(success);
+  }
+
+  void OnDbInitializedBeforeLoad(LoadCallback callback,
+                                 leveldb_proto::Enums::InitStatus status) {
+    bool success = (status == leveldb_proto::Enums::InitStatus::kOK);
+    // Failed to open db.
+    if (!success) {
+      std::move(callback).Run(false, Entries());
+      return;
+    }
+
+    db_->LoadEntries(base::BindOnce(&ProtoDbCollectionStore::OnEntriesLoaded,
+                                    weak_ptr_factory_.GetWeakPtr(),
+                                    std::move(callback)));
+  }
+
+  void OnEntriesLoaded(LoadCallback callback,
+                       bool success,
+                       std::unique_ptr<std::vector<P>> protos) {
+    // Failed to load data.
+    if (!success) {
+      std::move(callback).Run(false, Entries());
+      return;
+    }
+
+    Entries entries = std::make_unique<std::vector<T>>();
+    for (const auto& proto : *protos)
+      entries->emplace_back(ProtoToEntry(proto));
+
+    std::move(callback).Run(true, std::move(entries));
+  }
+
+  std::unique_ptr<leveldb_proto::ProtoDatabase<P>> db_;
+  std::string db_client_name_;
+  base::WeakPtrFactory<ProtoDbCollectionStore<T, P>> weak_ptr_factory_;
+};
+
+}  // namespace notifications
+
+#endif  // CHROME_BROWSER_NOTIFICATIONS_SCHEDULER_PROTO_DB_COLLECTION_STORE_H_
diff --git a/chrome/browser/notifications/scheduler/proto_db_collection_store_unittest.cc b/chrome/browser/notifications/scheduler/proto_db_collection_store_unittest.cc
new file mode 100644
index 0000000..0bf6429
--- /dev/null
+++ b/chrome/browser/notifications/scheduler/proto_db_collection_store_unittest.cc
@@ -0,0 +1,226 @@
+// 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/notifications/scheduler/proto_db_collection_store.h"
+
+#include <map>
+#include <memory>
+#include <string>
+#include <utility>
+
+#include "base/run_loop.h"
+#include "base/test/scoped_task_environment.h"
+#include "components/leveldb_proto/testing/fake_db.h"
+#include "components/leveldb_proto/testing/proto/test_db.pb.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace notifications {
+namespace {
+
+// Entry for test which will serialize into leveldb_proto.TestProto.
+struct TestEntry {
+  bool operator==(const TestEntry& other) const {
+    return id == other.id && data == other.data;
+  }
+  std::string id;
+  std::string data;
+};
+
+// A implementation of ProtoDbCollectionStore used to test
+// ProtoDbCollectionStore<T, P>.
+class ProtoStoreForTest
+    : public ProtoDbCollectionStore<TestEntry, leveldb_proto::TestProto> {
+ public:
+  using Entries =
+      typename ProtoDbCollectionStore<TestEntry,
+                                      leveldb_proto::TestProto>::Entries;
+  ProtoStoreForTest(
+      std::unique_ptr<leveldb_proto::ProtoDatabase<leveldb_proto::TestProto>>
+          db)
+      : ProtoDbCollectionStore(std::move(db),
+                               std::string("ProtoStoreForTest")) {}
+  ~ProtoStoreForTest() override = default;
+
+ private:
+  // ProtoDbCollectionStore implementation.
+  leveldb_proto::TestProto EntryToProto(const TestEntry& entry) override {
+    leveldb_proto::TestProto proto;
+    proto.set_id(entry.id);
+    proto.set_data(entry.data);
+    return proto;
+  }
+
+  TestEntry ProtoToEntry(const leveldb_proto::TestProto& proto) override {
+    TestEntry entry;
+    entry.id = proto.id();
+    entry.data = proto.data();
+    return entry;
+  }
+
+  DISALLOW_COPY_AND_ASSIGN(ProtoStoreForTest);
+};
+
+// Verifies that |entry| and |proto| contains the same data.
+void VerifyEntryProto(const TestEntry& entry,
+                      const leveldb_proto::TestProto& proto) {
+  EXPECT_EQ(entry.id, proto.id());
+  EXPECT_EQ(entry.data, proto.data());
+}
+
+const char kProtoKey[] = "guid_1234";
+const char kProtoId[] = "proto_id";
+const char kProtoData[] = "data_1234";
+
+class ProtoDbCollectionStoreTest : public testing::Test {
+ public:
+  ProtoDbCollectionStoreTest() : db_(nullptr) {}
+  ~ProtoDbCollectionStoreTest() override = default;
+
+  void SetUp() override {
+    leveldb_proto::TestProto proto;
+    proto.set_id(kProtoId);
+    proto.set_data(kProtoData);
+    db_protos_.emplace(kProtoKey, proto);
+    auto db =
+        std::make_unique<leveldb_proto::test::FakeDB<leveldb_proto::TestProto>>(
+            &db_protos_);
+    db_ = db.get();
+    store_ = std::make_unique<ProtoStoreForTest>(std::move(db));
+  }
+
+  void OnDbInitAndLoad(base::RepeatingClosure quit_closure,
+                       bool expected_success,
+                       bool success,
+                       ProtoStoreForTest::Entries entries) {
+    EXPECT_EQ(expected_success, success);
+    entries_ = std::move(entries);
+    quit_closure.Run();
+  }
+
+ protected:
+  ProtoStoreForTest* store() { return store_.get(); }
+  leveldb_proto::test::FakeDB<leveldb_proto::TestProto>* db() { return db_; }
+  const std::map<std::string, leveldb_proto::TestProto>& db_protos() const {
+    return db_protos_;
+  }
+  std::vector<TestEntry>* entries() const { return entries_.get(); }
+
+ private:
+  base::test::ScopedTaskEnvironment scoped_task_environment_;
+  std::unique_ptr<ProtoStoreForTest> store_;
+  std::unique_ptr<std::vector<TestEntry>> entries_;
+  std::map<std::string, leveldb_proto::TestProto> db_protos_;
+  leveldb_proto::test::FakeDB<leveldb_proto::TestProto>* db_;
+
+  DISALLOW_COPY_AND_ASSIGN(ProtoDbCollectionStoreTest);
+};
+
+TEST_F(ProtoDbCollectionStoreTest, Init) {
+  store()->Init(base::BindOnce([](bool success) { EXPECT_TRUE(success); }));
+  db()->InitStatusCallback(leveldb_proto::Enums::InitStatus::kOK);
+  base::RunLoop().RunUntilIdle();
+}
+
+TEST_F(ProtoDbCollectionStoreTest, InitFailed) {
+  store()->Init(base::BindOnce([](bool success) { EXPECT_FALSE(success); }));
+  db()->InitStatusCallback(leveldb_proto::Enums::InitStatus::kCorrupt);
+  base::RunLoop().RunUntilIdle();
+}
+
+TEST_F(ProtoDbCollectionStoreTest, InitAndLoad) {
+  base::RunLoop run_loop;
+  store()->InitAndLoad(
+      base::BindOnce(&ProtoDbCollectionStoreTest::OnDbInitAndLoad,
+                     base::Unretained(this), run_loop.QuitClosure(), true));
+  db()->InitStatusCallback(leveldb_proto::Enums::InitStatus::kOK);
+  db()->LoadCallback(true);
+
+  EXPECT_EQ(entries()->size(), 1u);
+  VerifyEntryProto(entries()->front(), db_protos().begin()->second);
+}
+
+TEST_F(ProtoDbCollectionStoreTest, InitAndLoadFailedInit) {
+  store()->InitAndLoad(
+      base::BindOnce([](bool success, ProtoStoreForTest::Entries entries) {
+        EXPECT_FALSE(success);
+        EXPECT_EQ(entries, nullptr);
+      }));
+
+  db()->InitStatusCallback(leveldb_proto::Enums::InitStatus::kError);
+}
+
+TEST_F(ProtoDbCollectionStoreTest, InitAndLoadFailedLoad) {
+  store()->InitAndLoad(
+      base::BindOnce([](bool success, ProtoStoreForTest::Entries entries) {
+        EXPECT_FALSE(success);
+        EXPECT_EQ(entries, nullptr);
+      }));
+
+  db()->InitStatusCallback(leveldb_proto::Enums::InitStatus::kOK);
+  db()->LoadCallback(false);
+}
+
+TEST_F(ProtoDbCollectionStoreTest, LoadOne) {
+  store()->Init(base::DoNothing());
+  db()->InitStatusCallback(leveldb_proto::Enums::InitStatus::kOK);
+
+  base::RunLoop run_loop;
+  store()->Load(
+      kProtoKey,
+      base::BindOnce(&ProtoDbCollectionStoreTest::OnDbInitAndLoad,
+                     base::Unretained(this), run_loop.QuitClosure(), true));
+  db()->LoadCallback(true);
+  VerifyEntryProto(entries()->front(), db_protos().begin()->second);
+}
+
+TEST_F(ProtoDbCollectionStoreTest, Add) {
+  store()->Init(base::DoNothing());
+  db()->InitStatusCallback(leveldb_proto::Enums::InitStatus::kOK);
+
+  // Add a new entry.
+  std::string new_key = std::string(kProtoKey) + "_new";
+  TestEntry new_entry;
+  new_entry.id = "new_id";
+  new_entry.data = "new_data";
+  store()->Add(new_key, new_entry,
+               base::BindOnce([](bool success) { EXPECT_TRUE(success); }));
+
+  // Load the new entry just added.
+  db()->UpdateCallback(true);
+  store()->Load(new_key, base::BindOnce([](bool success,
+                                           ProtoStoreForTest::Entries entries) {
+                  EXPECT_TRUE(success);
+                  EXPECT_TRUE(entries);
+                  EXPECT_EQ(entries->size(), 1u);
+                  EXPECT_EQ(entries->front().id, "new_id");
+                  EXPECT_EQ(entries->front().data, "new_data");
+                }));
+  db()->LoadCallback(true);
+}
+
+TEST_F(ProtoDbCollectionStoreTest, Delete) {
+  store()->InitAndLoad(base::DoNothing());
+  db()->InitStatusCallback(leveldb_proto::Enums::InitStatus::kOK);
+  db()->LoadCallback(true);
+
+  // Delete the only entry.
+  store()->Delete(kProtoKey,
+                  base::BindOnce([](bool success) { EXPECT_TRUE(success); }));
+  db()->UpdateCallback(true);
+
+  // Load the deleted entry.
+  store()->Load(
+      kProtoKey,
+      base::BindOnce([](bool success, ProtoStoreForTest::Entries entries) {
+        EXPECT_TRUE(success);
+        EXPECT_TRUE(entries);
+        EXPECT_EQ(entries->size(), 0u);
+      }));
+
+  db()->LoadCallback(true);
+}
+
+}  // namespace
+}  // namespace notifications
diff --git a/chrome/browser/offline_items_collection/offline_content_aggregator_factory.cc b/chrome/browser/offline_items_collection/offline_content_aggregator_factory.cc
index 31935d0..8bfc240 100644
--- a/chrome/browser/offline_items_collection/offline_content_aggregator_factory.cc
+++ b/chrome/browser/offline_items_collection/offline_content_aggregator_factory.cc
@@ -5,11 +5,17 @@
 #include "chrome/browser/offline_items_collection/offline_content_aggregator_factory.h"
 
 #include "base/memory/singleton.h"
+#include "build/build_config.h"
 #include "chrome/browser/profiles/incognito_helpers.h"
 #include "components/keyed_service/content/browser_context_dependency_manager.h"
 #include "components/offline_items_collection/core/offline_content_aggregator.h"
 #include "content/public/browser/browser_context.h"
 
+#if defined(OS_ANDROID)
+static offline_items_collection::OfflineContentAggregator*
+    g_offline_content_aggregator = nullptr;
+#endif
+
 // static
 OfflineContentAggregatorFactory*
 OfflineContentAggregatorFactory::GetInstance() {
@@ -20,8 +26,16 @@
 offline_items_collection::OfflineContentAggregator*
 OfflineContentAggregatorFactory::GetForBrowserContext(
     content::BrowserContext* context) {
+#if defined(OS_ANDROID)
+  if (!g_offline_content_aggregator) {
+    g_offline_content_aggregator =
+        new offline_items_collection::OfflineContentAggregator();
+  }
+  return g_offline_content_aggregator;
+#else
   return static_cast<offline_items_collection::OfflineContentAggregator*>(
       GetInstance()->GetServiceForBrowserContext(context, true));
+#endif
 }
 
 OfflineContentAggregatorFactory::OfflineContentAggregatorFactory()
diff --git a/chrome/browser/page_load_metrics/observers/prerender_page_load_metrics_observer.cc b/chrome/browser/page_load_metrics/observers/prerender_page_load_metrics_observer.cc
deleted file mode 100644
index cccf701..0000000
--- a/chrome/browser/page_load_metrics/observers/prerender_page_load_metrics_observer.cc
+++ /dev/null
@@ -1,86 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "chrome/browser/page_load_metrics/observers/prerender_page_load_metrics_observer.h"
-
-#include <memory>
-
-#include "base/logging.h"
-#include "chrome/browser/prerender/prerender_manager.h"
-#include "chrome/browser/prerender/prerender_manager_factory.h"
-#include "content/public/browser/web_contents.h"
-#include "net/http/http_response_headers.h"
-
-// static
-std::unique_ptr<page_load_metrics::PageLoadMetricsObserver>
-PrerenderPageLoadMetricsObserver::CreateIfNeeded(
-    content::WebContents* web_contents) {
-  prerender::PrerenderManager* manager =
-      prerender::PrerenderManagerFactory::GetForBrowserContext(
-          web_contents->GetBrowserContext());
-  if (!manager)
-    return nullptr;
-
-  if (manager->PageLoadMetricsObserverDisabledForTesting())
-    return nullptr;
-
-  return std::make_unique<PrerenderPageLoadMetricsObserver>(manager,
-                                                            web_contents);
-}
-
-PrerenderPageLoadMetricsObserver::PrerenderPageLoadMetricsObserver(
-    prerender::PrerenderManager* manager,
-    content::WebContents* web_contents)
-    : prerender_manager_(manager),
-      web_contents_(web_contents),
-      is_no_store_(false),
-      was_hidden_(false) {
-  DCHECK(prerender_manager_);
-  DCHECK(web_contents_);
-}
-
-page_load_metrics::PageLoadMetricsObserver::ObservePolicy
-PrerenderPageLoadMetricsObserver::OnStart(
-    content::NavigationHandle* navigation_handle,
-    const GURL& currently_committed_url,
-    bool started_in_foreground) {
-  DCHECK(!started_in_foreground);
-
-  start_ticks_ = navigation_handle->NavigationStart();
-  return CONTINUE_OBSERVING;
-}
-
-page_load_metrics::PageLoadMetricsObserver::ObservePolicy
-PrerenderPageLoadMetricsObserver::OnCommit(
-    content::NavigationHandle* navigation_handle,
-    ukm::SourceId source_id) {
-  const net::HttpResponseHeaders* response_headers =
-      navigation_handle->GetResponseHeaders();
-
-  is_no_store_ = response_headers &&
-                 response_headers->HasHeaderValue("cache-control", "no-store");
-  return CONTINUE_OBSERVING;
-}
-
-void PrerenderPageLoadMetricsObserver::OnFirstContentfulPaintInPage(
-    const page_load_metrics::mojom::PageLoadTiming& timing,
-    const page_load_metrics::PageLoadExtraInfo& extra_info) {
-  DCHECK(!start_ticks_.is_null());
-  prerender_manager_->RecordPrerenderFirstContentfulPaint(
-      extra_info.start_url, web_contents_, is_no_store_, was_hidden_,
-      start_ticks_ + *timing.paint_timing->first_contentful_paint);
-}
-
-page_load_metrics::PageLoadMetricsObserver::ObservePolicy
-PrerenderPageLoadMetricsObserver::OnHidden(
-    const page_load_metrics::mojom::PageLoadTiming& timing,
-    const page_load_metrics::PageLoadExtraInfo& extra_info) {
-  was_hidden_ = true;
-  return CONTINUE_OBSERVING;
-}
-
-void PrerenderPageLoadMetricsObserver::SetNavigationStartTicksForTesting(
-    base::TimeTicks ticks) {
-  start_ticks_ = ticks;
-}
diff --git a/chrome/browser/page_load_metrics/observers/prerender_page_load_metrics_observer.h b/chrome/browser/page_load_metrics/observers/prerender_page_load_metrics_observer.h
deleted file mode 100644
index 4c2a597..0000000
--- a/chrome/browser/page_load_metrics/observers/prerender_page_load_metrics_observer.h
+++ /dev/null
@@ -1,63 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CHROME_BROWSER_PAGE_LOAD_METRICS_OBSERVERS_PRERENDER_PAGE_LOAD_METRICS_OBSERVER_H_
-#define CHROME_BROWSER_PAGE_LOAD_METRICS_OBSERVERS_PRERENDER_PAGE_LOAD_METRICS_OBSERVER_H_
-
-#include "base/macros.h"
-#include "chrome/browser/page_load_metrics/page_load_metrics_observer.h"
-#include "services/metrics/public/cpp/ukm_source.h"
-#include "url/gurl.h"
-
-namespace content {
-class WebContents;
-}
-
-namespace prerender {
-class PrerenderManager;
-}
-
-// Observer responsible for recording First Contentful Paing metrics related to
-// Prerender.
-//
-// To record FCP metrics for non-Prerender loads, the
-// |NoStatePrefetchPageLoadMetricsObserver| is used.
-class PrerenderPageLoadMetricsObserver
-    : public page_load_metrics::PageLoadMetricsObserver {
- public:
-  // Returns a PrerenderPageLoadMetricsObserver, or nullptr if it is not needed.
-  static std::unique_ptr<page_load_metrics::PageLoadMetricsObserver>
-  CreateIfNeeded(content::WebContents* web_contents);
-
-  // Public for testing. Normally one should use CreateIfNeeded. Both the
-  // manager and the web_contents must outlive this observer.
-  PrerenderPageLoadMetricsObserver(prerender::PrerenderManager* manager,
-                                   content::WebContents* web_contents);
-
-  // page_load_metrics::PageLoadMetricsObserver:
-  ObservePolicy OnStart(content::NavigationHandle* navigation_handle,
-                        const GURL& currently_committed_url,
-                        bool started_in_foreground) override;
-  ObservePolicy OnCommit(content::NavigationHandle* navigation_handle,
-                         ukm::SourceId source_id) override;
-  void OnFirstContentfulPaintInPage(
-      const page_load_metrics::mojom::PageLoadTiming& timing,
-      const page_load_metrics::PageLoadExtraInfo& extra_info) override;
-  ObservePolicy OnHidden(
-      const page_load_metrics::mojom::PageLoadTiming& timing,
-      const page_load_metrics::PageLoadExtraInfo& extra_info) override;
-
-  void SetNavigationStartTicksForTesting(base::TimeTicks ticks);
-
- private:
-  prerender::PrerenderManager* const prerender_manager_;
-  content::WebContents* web_contents_;
-  base::TimeTicks start_ticks_;
-  bool is_no_store_;
-  bool was_hidden_;
-
-  DISALLOW_COPY_AND_ASSIGN(PrerenderPageLoadMetricsObserver);
-};
-
-#endif  // CHROME_BROWSER_PAGE_LOAD_METRICS_OBSERVERS_PRERENDER_PAGE_LOAD_METRICS_OBSERVER_H_
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 9b0740b..202352e5 100644
--- a/chrome/browser/page_load_metrics/page_load_metrics_initialize.cc
+++ b/chrome/browser/page_load_metrics/page_load_metrics_initialize.cc
@@ -33,7 +33,6 @@
 #include "chrome/browser/page_load_metrics/observers/offline_page_previews_page_load_metrics_observer.h"
 #include "chrome/browser/page_load_metrics/observers/omnibox_suggestion_used_page_load_metrics_observer.h"
 #include "chrome/browser/page_load_metrics/observers/page_capping_page_load_metrics_observer.h"
-#include "chrome/browser/page_load_metrics/observers/prerender_page_load_metrics_observer.h"
 #include "chrome/browser/page_load_metrics/observers/previews_lite_page_redirect_metrics_observer.h"
 #include "chrome/browser/page_load_metrics/observers/previews_page_load_metrics_observer.h"
 #include "chrome/browser/page_load_metrics/observers/previews_ukm_observer.h"
@@ -168,12 +167,6 @@
         std::make_unique<LocalNetworkRequestsPageLoadMetricsObserver>());
     tracker->AddObserver(
         std::make_unique<StaleWhileRevalidatePageLoadMetricsObserver>());
-  } else {
-    std::unique_ptr<page_load_metrics::PageLoadMetricsObserver>
-        prerender_observer =
-            PrerenderPageLoadMetricsObserver::CreateIfNeeded(web_contents_);
-    if (prerender_observer)
-      tracker->AddObserver(std::move(prerender_observer));
   }
   tracker->AddObserver(
       std::make_unique<OmniboxSuggestionUsedMetricsObserver>(IsPrerendering()));
diff --git a/chrome/browser/password_manager/password_accessory_controller_impl.cc b/chrome/browser/password_manager/password_accessory_controller_impl.cc
index fac1ca5..a7759ab 100644
--- a/chrome/browser/password_manager/password_accessory_controller_impl.cc
+++ b/chrome/browser/password_manager/password_accessory_controller_impl.cc
@@ -248,7 +248,8 @@
           ? IDS_PASSWORD_MANAGER_ACCESSORY_PASSWORD_LIST_EMPTY_MESSAGE
           : IDS_PASSWORD_MANAGER_ACCESSORY_PASSWORD_LIST_TITLE,
       base::ASCIIToUTF16(origin.host()));
-  AccessorySheetData data(passwords_title_str);
+  AccessorySheetData data(autofill::FallbackSheetType::PASSWORD,
+                          passwords_title_str);
 
   // Create a username and a password element for every suggestion.
   for (const SuggestionElementData& suggestion : suggestions) {
diff --git a/chrome/browser/password_manager/password_accessory_controller_impl_unittest.cc b/chrome/browser/password_manager/password_accessory_controller_impl_unittest.cc
index fde2278..c7055ece 100644
--- a/chrome/browser/password_manager/password_accessory_controller_impl_unittest.cc
+++ b/chrome/browser/password_manager/password_accessory_controller_impl_unittest.cc
@@ -64,7 +64,7 @@
 class AccessorySheetDataBuilder {
  public:
   explicit AccessorySheetDataBuilder(const base::string16& title)
-      : accessory_sheet_data_(title) {}
+      : accessory_sheet_data_(autofill::FallbackSheetType::PASSWORD, title) {}
 
   ~AccessorySheetDataBuilder() = default;
 
diff --git a/chrome/browser/password_manager/password_store_x.cc b/chrome/browser/password_manager/password_store_x.cc
index ed8e313..f34a00c 100644
--- a/chrome/browser/password_manager/password_store_x.cc
+++ b/chrome/browser/password_manager/password_store_x.cc
@@ -555,7 +555,7 @@
   PasswordStoreDefault::ShutdownOnUIThread();
 }
 
-bool PasswordStoreX::ReadAllLogins(
+password_manager::FormRetrievalResult PasswordStoreX::ReadAllLogins(
     password_manager::PrimaryKeyToFormMap* key_to_form_map) {
   // This method is called from the PasswordSyncBridge which supports only
   // PasswordStoreDefault. Therefore, on Linux, it should be called only if the
diff --git a/chrome/browser/password_manager/password_store_x.h b/chrome/browser/password_manager/password_store_x.h
index c7cda92e..a783fef 100644
--- a/chrome/browser/password_manager/password_store_x.h
+++ b/chrome/browser/password_manager/password_store_x.h
@@ -139,7 +139,7 @@
 
  protected:
   // Implements PasswordStoreSync interface.
-  bool ReadAllLogins(
+  password_manager::FormRetrievalResult ReadAllLogins(
       password_manager::PrimaryKeyToFormMap* key_to_form_map) override;
   password_manager::PasswordStoreChangeList RemoveLoginByPrimaryKeySync(
       int primary_key) override;
diff --git a/chrome/browser/performance_manager/decorators/page_almost_idle_decorator.cc b/chrome/browser/performance_manager/decorators/page_almost_idle_decorator.cc
index 17c546d..ed3c874d4 100644
--- a/chrome/browser/performance_manager/decorators/page_almost_idle_decorator.cc
+++ b/chrome/browser/performance_manager/decorators/page_almost_idle_decorator.cc
@@ -47,14 +47,6 @@
   }
 }
 
-void PageAlmostIdleDecorator::OnPagePropertyChanged(
-    PageNodeImpl* page_node,
-    resource_coordinator::mojom::PropertyType property_type,
-    int64_t value) {
-  if (property_type == resource_coordinator::mojom::PropertyType::kIsLoading)
-    UpdateLoadIdleStatePage(page_node);
-}
-
 void PageAlmostIdleDecorator::OnProcessPropertyChanged(
     ProcessNodeImpl* process_node,
     resource_coordinator::mojom::PropertyType property_type,
@@ -80,6 +72,10 @@
   UpdateLoadIdleStatePage(page_node);
 }
 
+void PageAlmostIdleDecorator::OnIsLoadingChanged(PageNodeImpl* page_node) {
+  UpdateLoadIdleStatePage(page_node);
+}
+
 void PageAlmostIdleDecorator::UpdateLoadIdleStateFrame(
     FrameNodeImpl* frame_node) {
   // Only main frames are relevant in the load idle state.
@@ -118,14 +114,14 @@
   // Otherwise do normal state transitions.
   switch (data->load_idle_state_) {
     case LoadIdleState::kLoadingNotStarted: {
-      if (!IsLoading(page_node))
+      if (!page_node->is_loading())
         return;
       data->load_idle_state_ = LoadIdleState::kLoading;
       return;
     }
 
     case LoadIdleState::kLoading: {
-      if (IsLoading(page_node))
+      if (page_node->is_loading())
         return;
       data->load_idle_state_ = LoadIdleState::kLoadedNotIdling;
       data->loading_stopped_ = now;
@@ -214,12 +210,6 @@
 }
 
 // static
-bool PageAlmostIdleDecorator::IsLoading(const PageNodeImpl* page_node) {
-  return page_node->GetPropertyOrDefault(
-      resource_coordinator::mojom::PropertyType::kIsLoading, 0u);
-}
-
-// static
 bool PageAlmostIdleDecorator::IsIdling(const PageNodeImpl* page_node) {
   // Get the Frame CU for the main frame associated with this page.
   const FrameNodeImpl* main_frame_node = page_node->GetMainFrameNode();
diff --git a/chrome/browser/performance_manager/decorators/page_almost_idle_decorator.h b/chrome/browser/performance_manager/decorators/page_almost_idle_decorator.h
index 42860df..b3213a7 100644
--- a/chrome/browser/performance_manager/decorators/page_almost_idle_decorator.h
+++ b/chrome/browser/performance_manager/decorators/page_almost_idle_decorator.h
@@ -35,16 +35,13 @@
       FrameNodeImpl* frame_node,
       resource_coordinator::mojom::PropertyType property_type,
       int64_t value) override;
-  void OnPagePropertyChanged(
-      PageNodeImpl* page_node,
-      resource_coordinator::mojom::PropertyType property_type,
-      int64_t value) override;
   void OnProcessPropertyChanged(
       ProcessNodeImpl* process_node,
       resource_coordinator::mojom::PropertyType property_type,
       int64_t value) override;
   void OnPageEventReceived(PageNodeImpl* page_node,
                            resource_coordinator::mojom::Event event) override;
+  void OnIsLoadingChanged(PageNodeImpl* page_node) override;
 
  protected:
   using LoadIdleState = PageAlmostIdleData::LoadIdleState;
@@ -84,7 +81,6 @@
   // Convenience accessors for state associated with a |page_node|.
   static PageAlmostIdleData* GetOrCreateData(PageNodeImpl* page_node);
   static PageAlmostIdleData* GetData(PageNodeImpl* page_node);
-  static bool IsLoading(const PageNodeImpl* page_node);
   static bool IsIdling(const PageNodeImpl* page_node);
 
  private:
diff --git a/chrome/browser/performance_manager/decorators/page_almost_idle_decorator_test_utils.cc b/chrome/browser/performance_manager/decorators/page_almost_idle_decorator_test_utils.cc
index 989e7b16..4d7c2f7 100644
--- a/chrome/browser/performance_manager/decorators/page_almost_idle_decorator_test_utils.cc
+++ b/chrome/browser/performance_manager/decorators/page_almost_idle_decorator_test_utils.cc
@@ -74,11 +74,11 @@
       NOTREACHED();
       break;
     case PageAlmostIdleData::LoadIdleState::kLoadingNotStarted:
-      DCHECK(!page_node->GetPropertyOrDefault(PropertyType::kIsLoading, 0));
+      DCHECK(!page_node->is_loading());
       page_node->SetIsLoading(true);
       FALLTHROUGH;
     case PageAlmostIdleData::LoadIdleState::kLoading:
-      DCHECK(page_node->GetPropertyOrDefault(PropertyType::kIsLoading, 0));
+      DCHECK(page_node->is_loading());
       page_node->SetIsLoading(false);
       FALLTHROUGH;
     case PageAlmostIdleData::LoadIdleState::kLoadedNotIdling:
diff --git a/chrome/browser/performance_manager/decorators/page_almost_idle_decorator_unittest.cc b/chrome/browser/performance_manager/decorators/page_almost_idle_decorator_unittest.cc
index d8283df..ba9c0212 100644
--- a/chrome/browser/performance_manager/decorators/page_almost_idle_decorator_unittest.cc
+++ b/chrome/browser/performance_manager/decorators/page_almost_idle_decorator_unittest.cc
@@ -79,10 +79,6 @@
         PageAlmostIdleDecorator::GetData(page_node));
   }
 
-  static bool IsLoading(const PageNodeImpl* page_node) {
-    return PageAlmostIdleDecorator::IsLoading(page_node);
-  }
-
   static bool IsIdling(const PageNodeImpl* page_node) {
     return PageAlmostIdleDecorator::IsIdling(page_node);
   }
@@ -224,15 +220,15 @@
 
   // The loading property hasn't yet been set. Then IsLoading should return
   // false as the default value.
-  EXPECT_FALSE(PageAlmostIdleDecoratorTestHelper::IsLoading(page_node));
+  EXPECT_FALSE(page_node->is_loading());
 
   // Once the loading property has been set it should return that value.
   page_node->SetIsLoading(false);
-  EXPECT_FALSE(PageAlmostIdleDecoratorTestHelper::IsLoading(page_node));
+  EXPECT_FALSE(page_node->is_loading());
   page_node->SetIsLoading(true);
-  EXPECT_TRUE(PageAlmostIdleDecoratorTestHelper::IsLoading(page_node));
+  EXPECT_TRUE(page_node->is_loading());
   page_node->SetIsLoading(false);
-  EXPECT_FALSE(PageAlmostIdleDecoratorTestHelper::IsLoading(page_node));
+  EXPECT_FALSE(page_node->is_loading());
 }
 
 TEST_F(PageAlmostIdleDecoratorTest, IsIdling) {
diff --git a/chrome/browser/performance_manager/graph/page_node_impl.cc b/chrome/browser/performance_manager/graph/page_node_impl.cc
index 2eed990..40f98885 100644
--- a/chrome/browser/performance_manager/graph/page_node_impl.cc
+++ b/chrome/browser/performance_manager/graph/page_node_impl.cc
@@ -64,8 +64,11 @@
 }
 
 void PageNodeImpl::SetIsLoading(bool is_loading) {
-  SetProperty(resource_coordinator::mojom::PropertyType::kIsLoading,
-              is_loading);
+  if (is_loading_ == is_loading)
+    return;
+  is_loading_ = is_loading;
+  for (auto& observer : observers())
+    observer.OnIsLoadingChanged(this);
 }
 
 void PageNodeImpl::SetVisibility(bool visible) {
diff --git a/chrome/browser/performance_manager/graph/page_node_impl.h b/chrome/browser/performance_manager/graph/page_node_impl.h
index a10e6f9..1e587f0 100644
--- a/chrome/browser/performance_manager/graph/page_node_impl.h
+++ b/chrome/browser/performance_manager/graph/page_node_impl.h
@@ -73,6 +73,7 @@
   FrameNodeImpl* GetMainFrameNode() const;
 
   // Accessors.
+  bool is_loading() const { return is_loading_; }
   base::TimeTicks usage_estimate_time() const { return usage_estimate_time_; }
   void set_usage_estimate_time(base::TimeTicks usage_estimate_time) {
     usage_estimate_time_ = usage_estimate_time;
@@ -220,6 +221,10 @@
   // PageAlmostIdleDecorator.
   bool page_almost_idle_ = false;
 
+  // The loading state. This is driven by instrumentation in the browser
+  // process.
+  bool is_loading_ = false;
+
   // TODO(chrisha): Hide away this type using a base class, and expose a
   // strongly typed "WebContentsUserData" like mechanism.
   // "User data" storage for the PageAlmostIdleDecorator.
diff --git a/chrome/browser/performance_manager/graph/page_node_impl_unittest.cc b/chrome/browser/performance_manager/graph/page_node_impl_unittest.cc
index a535b7bd..2447e2e 100644
--- a/chrome/browser/performance_manager/graph/page_node_impl_unittest.cc
+++ b/chrome/browser/performance_manager/graph/page_node_impl_unittest.cc
@@ -183,28 +183,20 @@
   MockSinglePageInSingleProcessGraph cu_graph(coordination_unit_graph());
   auto* page_cu = cu_graph.page.get();
 
-  // First attempt should fail, as the property is unset.
-  int64_t loading = 0;
-  EXPECT_FALSE(page_cu->GetProperty(
-      resource_coordinator::mojom::PropertyType::kIsLoading, &loading));
+  // This should be initialized to false.
+  EXPECT_FALSE(page_cu->is_loading());
 
-  // Set to false and the property should read false.
+  // Set to false and the property should stay false.
   page_cu->SetIsLoading(false);
-  EXPECT_TRUE(page_cu->GetProperty(
-      resource_coordinator::mojom::PropertyType::kIsLoading, &loading));
-  EXPECT_EQ(0u, loading);
+  EXPECT_FALSE(page_cu->is_loading());
 
   // Set to true and the property should read true.
   page_cu->SetIsLoading(true);
-  EXPECT_TRUE(page_cu->GetProperty(
-      resource_coordinator::mojom::PropertyType::kIsLoading, &loading));
-  EXPECT_EQ(1u, loading);
+  EXPECT_TRUE(page_cu->is_loading());
 
   // Set to false and the property should read false again.
   page_cu->SetIsLoading(false);
-  EXPECT_TRUE(page_cu->GetProperty(
-      resource_coordinator::mojom::PropertyType::kIsLoading, &loading));
-  EXPECT_EQ(0u, loading);
+  EXPECT_FALSE(page_cu->is_loading());
 }
 
 TEST_F(PageNodeImplTest, OnAllFramesInPageFrozen) {
diff --git a/chrome/browser/performance_manager/observers/coordination_unit_graph_observer.h b/chrome/browser/performance_manager/observers/coordination_unit_graph_observer.h
index 1121d95..09ab527 100644
--- a/chrome/browser/performance_manager/observers/coordination_unit_graph_observer.h
+++ b/chrome/browser/performance_manager/observers/coordination_unit_graph_observer.h
@@ -95,6 +95,9 @@
                                      resource_coordinator::mojom::Event event) {
   }
 
+  // Called when the "is loading" state changes on a page node.
+  virtual void OnIsLoadingChanged(PageNodeImpl* page_node) {}
+
   // Called when page almost idle state changes. This is a computed property and
   // will only be maintained if a PageAlmostIdleDecorator exists on the graph.
   virtual void OnPageAlmostIdleChanged(PageNodeImpl* page_node) {}
diff --git a/chrome/browser/policy/chrome_browser_policy_connector.cc b/chrome/browser/policy/chrome_browser_policy_connector.cc
index 429417f..02799010 100644
--- a/chrome/browser/policy/chrome_browser_policy_connector.cc
+++ b/chrome/browser/policy/chrome_browser_policy_connector.cc
@@ -51,11 +51,20 @@
 #include "components/policy/core/common/cloud/machine_level_user_cloud_policy_manager.h"
 #endif
 
+#if defined(OS_WIN)
+#include "chrome/browser/browser_switcher/browser_switcher_policy_migrator.h"
+#endif
+
 namespace policy {
 
 namespace {
 
-void AddMigrators(ConfigurationPolicyProvider* provider) {}
+void AddMigrators(ConfigurationPolicyProvider* provider) {
+#if defined(OS_WIN)
+  provider->AddMigrator(
+      std::make_unique<browser_switcher::BrowserSwitcherPolicyMigrator>());
+#endif
+}
 
 bool ProviderHasPolicies(const ConfigurationPolicyProvider* provider) {
   if (!provider)
diff --git a/chrome/browser/policy/chrome_extension_policy_migrator.cc b/chrome/browser/policy/chrome_extension_policy_migrator.cc
index 3169494..4f36933 100644
--- a/chrome/browser/policy/chrome_extension_policy_migrator.cc
+++ b/chrome/browser/policy/chrome_extension_policy_migrator.cc
@@ -41,7 +41,9 @@
     PolicyMap::Entry* entry = extension_map->GetMutable(migration.old_name);
     if (entry) {
       if (!chrome_map.Get(migration.new_name)) {
-        chrome_map.Set(migration.new_name, entry->DeepCopy());
+        auto new_entry = entry->DeepCopy();
+        migration.transform.Run(new_entry.value.get());
+        chrome_map.Set(migration.new_name, std::move(new_entry));
       }
       // TODO(crbug/869958): Mark the old policy as deprecated for
       // chrome://policy.
diff --git a/chrome/browser/policy/chrome_extension_policy_migrator_unittest.cc b/chrome/browser/policy/chrome_extension_policy_migrator_unittest.cc
index f4bf6d01..1e4bfde6 100644
--- a/chrome/browser/policy/chrome_extension_policy_migrator_unittest.cc
+++ b/chrome/browser/policy/chrome_extension_policy_migrator_unittest.cc
@@ -21,21 +21,23 @@
 const char kOldPolicy2[] = "OldPolicyTwo";
 const char kOldPolicy3[] = "OldPolicyThree";
 const char kOldPolicy4[] = "OldPolicyFour";
+const char kOldPolicy5[] = "OldPolicyFive";
 const char kNewPolicy1[] = "NewPolicyOne";
 const char kNewPolicy2[] = "NewPolicyTwo";
 const char kNewPolicy3[] = "NewPolicyThree";
+const char kNewPolicy4[] = "NewPolicyFour";
 
 const int kOldValue1 = 111;
 const int kOldValue2 = 222;
 const int kOldValue3 = 333;
 const int kOldValue4 = 444;
+const int kOldValue5 = 555;
 const int kNewValue3 = 999;
+const int kNewValue4 = 888;
 
-const ExtensionPolicyMigrator::Migration kMigrations[] = {
-    {kOldPolicy1, kNewPolicy1},
-    {kOldPolicy2, kNewPolicy2},
-    {kOldPolicy3, kNewPolicy3},
-};
+void MultiplyByTwo(base::Value* val) {
+  *val = base::Value(val->GetInt() * 2);
+}
 
 void SetPolicy(PolicyMap* policy,
                const char* policy_name,
@@ -47,9 +49,17 @@
 class TestingPolicyMigrator : public ChromeExtensionPolicyMigrator {
  public:
   void Migrate(PolicyBundle* bundle) override {
+    using Migration = ExtensionPolicyMigrator::Migration;
+    const Migration migrations[] = {
+        Migration(kOldPolicy1, kNewPolicy1),
+        Migration(kOldPolicy2, kNewPolicy2),
+        Migration(kOldPolicy3, kNewPolicy3),
+        Migration(kOldPolicy4, kNewPolicy4,
+                  base::BindRepeating(&MultiplyByTwo)),
+    };
     CopyPoliciesIfUnset(bundle,
                         extensions::HashedExtensionId(kExtensionId).value(),
-                        kMigrations);
+                        migrations);
   }
 };
 
@@ -73,11 +83,13 @@
             std::make_unique<base::Value>(kOldValue3));
   SetPolicy(&extension_map, kOldPolicy4,
             std::make_unique<base::Value>(kOldValue4));
+  SetPolicy(&extension_map, kOldPolicy5,
+            std::make_unique<base::Value>(kOldValue5));
 
   TestingPolicyMigrator().Migrate(&bundle);
 
   // Policies in kMigrations should be renamed + copied into the Chrome domain.
-  EXPECT_EQ(3u, chrome_map.size());
+  EXPECT_EQ(4u, chrome_map.size());
   ASSERT_TRUE(chrome_map.GetValue(kNewPolicy1));
   EXPECT_EQ(base::Value(kOldValue1), *chrome_map.GetValue(kNewPolicy1));
   ASSERT_TRUE(chrome_map.GetValue(kNewPolicy2));
@@ -85,6 +97,9 @@
   // kNewPolicy3 is already set, and should not be overwritten.
   ASSERT_TRUE(chrome_map.GetValue(kNewPolicy3));
   EXPECT_EQ(base::Value(kNewValue3), *chrome_map.GetValue(kNewPolicy3));
+  // This policy was transformed by MultiplyByTwo.
+  ASSERT_TRUE(chrome_map.GetValue(kNewPolicy4));
+  EXPECT_EQ(base::Value(kNewValue4), *chrome_map.GetValue(kNewPolicy4));
 }
 
 }  // namespace policy
diff --git a/chrome/browser/policy/configuration_policy_handler_list_factory.cc b/chrome/browser/policy/configuration_policy_handler_list_factory.cc
index df32dde..b9635fbf 100644
--- a/chrome/browser/policy/configuration_policy_handler_list_factory.cc
+++ b/chrome/browser/policy/configuration_policy_handler_list_factory.cc
@@ -958,6 +958,9 @@
   { key::kEnterpriseHardwarePlatformAPIEnabled,
     prefs::kEnterpriseHardwarePlatformAPIEnabled,
     base::Value::Type::BOOLEAN },
+  { key::kAllowPopupsDuringPageUnload,
+    prefs::kAllowPopupsDuringPageUnload,
+    base::Value::Type::BOOLEAN },
 
 #if defined(OS_WIN) || defined(OS_MACOSX) || \
     (defined(OS_LINUX) && !defined(OS_CHROMEOS))
diff --git a/chrome/browser/policy/policy_network_browsertest.cc b/chrome/browser/policy/policy_network_browsertest.cc
index 0a208c1..2afe52b 100644
--- a/chrome/browser/policy/policy_network_browsertest.cc
+++ b/chrome/browser/policy/policy_network_browsertest.cc
@@ -224,7 +224,13 @@
 // just crash the network service once, and then test all network contexts in
 // some particular order.
 
-IN_PROC_BROWSER_TEST_F(QuicAllowedPolicyIsTrue, QuicAllowedForSystem) {
+// TODO(crbug.com/938139): Flaky on ChromeOS with Network Service
+#if defined(OS_CHROMEOS)
+#define MAYBE_QuicAllowedForSystem DISABLED_QuicAllowedForSystem
+#else
+#define MAYBE_QuicAllowedForSystem QuicAllowedForSystem
+#endif
+IN_PROC_BROWSER_TEST_F(QuicAllowedPolicyIsTrue, MAYBE_QuicAllowedForSystem) {
   EXPECT_TRUE(IsQuicEnabledForSystem());
 
   // If using the network service, crash the service, and make sure QUIC is
diff --git a/chrome/browser/prefs/browser_prefs.cc b/chrome/browser/prefs/browser_prefs.cc
index 5060969..3523036a 100644
--- a/chrome/browser/prefs/browser_prefs.cc
+++ b/chrome/browser/prefs/browser_prefs.cc
@@ -310,6 +310,7 @@
 #if defined(OS_CHROMEOS) && BUILDFLAG(ENABLE_APP_LIST)
 #include "chrome/browser/chromeos/apps/apk_web_app_service.h"
 #include "chrome/browser/ui/app_list/arc/arc_app_list_prefs.h"
+#include "chrome/browser/ui/app_list/search/arc/arc_app_reinstall_search_provider.h"
 #endif
 
 #if defined(OS_MACOSX)
@@ -782,6 +783,7 @@
 #endif
 
 #if defined(OS_CHROMEOS) && BUILDFLAG(ENABLE_APP_LIST)
+  app_list::ArcAppReinstallSearchProvider::RegisterProfilePrefs(registry);
   ArcAppListPrefs::RegisterProfilePrefs(registry);
   chromeos::ApkWebAppService::RegisterProfilePrefs(registry);
 #endif
diff --git a/chrome/browser/prerender/prerender_browsertest.cc b/chrome/browser/prerender/prerender_browsertest.cc
index c6b1d58..e9522ad 100644
--- a/chrome/browser/prerender/prerender_browsertest.cc
+++ b/chrome/browser/prerender/prerender_browsertest.cc
@@ -41,7 +41,6 @@
 #include "chrome/browser/external_protocol/external_protocol_handler.h"
 #include "chrome/browser/net/prediction_options.h"
 #include "chrome/browser/page_load_metrics/metrics_web_contents_observer.h"
-#include "chrome/browser/page_load_metrics/observers/prerender_page_load_metrics_observer.h"
 #include "chrome/browser/page_load_metrics/page_load_tracker.h"
 #include "chrome/browser/password_manager/password_store_factory.h"
 #include "chrome/browser/predictors/autocomplete_action_predictor.h"
@@ -518,12 +517,6 @@
       base::FilePath().AppendASCII(file_name));
 }
 
-page_load_metrics::PageLoadExtraInfo GenericPageLoadExtraInfo(
-    const GURL& dest_url) {
-  return page_load_metrics::PageLoadExtraInfo::CreateForTesting(
-      dest_url, false /* started_in_foreground */);
-}
-
 }  // namespace
 
 class PrerenderBrowserTest : public test_utils::PrerenderInProcessBrowserTest {
@@ -1028,30 +1021,7 @@
   std::unique_ptr<content::URLLoaderInterceptor> interceptor_;
 };
 
-// Checks that a page is correctly prerendered in the case of a
-// <link rel=prerender> tag and then loaded into a tab in response to a
-// navigation.
-IN_PROC_BROWSER_TEST_F(PrerenderBrowserTest, PrerenderPage) {
-  test_utils::FirstContentfulPaintManagerWaiter* fcp_waiter =
-      test_utils::FirstContentfulPaintManagerWaiter::Create(
-          GetPrerenderManager());
-  PrerenderTestURL("/prerender/prerender_page.html", FINAL_STATUS_USED, 1);
-  EXPECT_EQ(1, GetPrerenderDomContentLoadedEventCountForLinkNumber(0));
 
-  ChannelDestructionWatcher channel_close_watcher;
-  channel_close_watcher.WatchChannel(
-      GetActiveWebContents()->GetMainFrame()->GetProcess());
-  NavigateToDestURL();
-  channel_close_watcher.WaitForChannelClose();
-  fcp_waiter->Wait();
-
-  histogram_tester().ExpectTotalCount(
-      "Prerender.websame_PrefetchTTFCP.Warm.Cacheable.Visible", 1);
-  histogram_tester().ExpectTotalCount(
-      "Prerender.websame_PerceivedTTFCPRecorded.Visible", 1);
-
-  ASSERT_TRUE(IsEmptyPrerenderLinkManager());
-}
 
 // Checks that the correct page load metrics observers are produced without a
 // prerender.
@@ -1075,42 +1045,6 @@
       "Prerender.websame_PrefetchTTFCP.Warm.Cacheable.Visible", 0);
 }
 
-// Checks that the correct page load metrics observers are produced with a
-// prerender.
-IN_PROC_BROWSER_TEST_F(PrerenderBrowserTest, PageLoadMetricsPrerender) {
-  test_utils::FirstContentfulPaintManagerWaiter* prerender_fcp_waiter =
-      test_utils::FirstContentfulPaintManagerWaiter::Create(
-          GetPrerenderManager());
-  PrerenderTestURL("/prerender/prerender_page.html", FINAL_STATUS_USED, 1);
-  NavigateToDestURL();
-  prerender_fcp_waiter->Wait();
-
-  histogram_tester().ExpectTotalCount(
-      "Prerender.websame_PrefetchTTFCP.Warm.Cacheable.Visible", 1);
-
-  // Histograms only emitted during the simple load which does not happen here
-  // (as prefetch_loader.html has an empty body, it does not generate a FCP).
-  histogram_tester().ExpectTotalCount(
-      "Prerender.none_PrefetchTTFCP.Reference.Cacheable.Visible", 0);
-  histogram_tester().ExpectTotalCount(
-      "PageLoad.PaintTiming.NavigationToFirstContentfulPaint", 0);
-}
-
-// Checks that cross-domain prerenders emit the correct histograms.
-IN_PROC_BROWSER_TEST_F(PrerenderBrowserTest, PrerenderPageCrossDomain) {
-  test_utils::FirstContentfulPaintManagerWaiter* fcp_waiter =
-      test_utils::FirstContentfulPaintManagerWaiter::Create(
-          GetPrerenderManager());
-  PrerenderTestURL(GetCrossDomainTestUrl("prerender/prerender_page.html"),
-                   FINAL_STATUS_USED, 1);
-
-  NavigateToDestURL();
-  fcp_waiter->Wait();
-
-  histogram_tester().ExpectTotalCount(
-      "Prerender.webcross_PrefetchTTFCP.Warm.Cacheable.Visible", 1);
-}
-
 // Checks that pending prerenders launch and receive proper event treatment.
 IN_PROC_BROWSER_TEST_F(PrerenderBrowserTest, PrerenderPagePending) {
   std::unique_ptr<TestPrerender> prerender = PrerenderTestURL(
@@ -3112,312 +3046,6 @@
   EXPECT_GT(priority, net::IDLE);
 }
 
-// Flaky on chromium.linux/Linux Tests (dbg). https://crbug.com/832597
-IN_PROC_BROWSER_TEST_F(PrerenderBrowserTest,
-                       DISABLED_FirstContentfulPaintTimingSimple) {
-  GetPrerenderManager()->DisablePageLoadMetricsObserverForTesting();
-  base::SimpleTestTickClock* clock = OverridePrerenderManagerTimeTicks();
-  PrerenderTestURL("/prerender/prerender_page.html", FINAL_STATUS_USED, 1);
-
-  base::TimeTicks load_start = clock->NowTicks();
-  clock->Advance(base::TimeDelta::FromSeconds(1));
-  NavigateToDestURL();
-
-  PrerenderPageLoadMetricsObserver observer(GetPrerenderManager(),
-                                            GetActiveWebContents());
-  observer.SetNavigationStartTicksForTesting(load_start);
-
-  page_load_metrics::mojom::PageLoadTiming timing;
-  page_load_metrics::InitPageLoadTimingForTest(&timing);
-  timing.navigation_start = base::Time::FromDoubleT(1);  // Non-null time.
-  timing.paint_timing->first_contentful_paint =
-      base::TimeDelta::FromMilliseconds(2654);
-  PopulateRequiredTimingFields(&timing);
-  observer.OnFirstContentfulPaintInPage(timing,
-                                        GenericPageLoadExtraInfo(dest_url()));
-
-  histogram_tester().ExpectTotalCount(
-      "Prerender.websame_PrefetchTTFCP.Warm.Cacheable.Visible", 1);
-  histogram_tester().ExpectUniqueSample(
-      "Prerender.websame_PrefetchTTFCP.Warm.Cacheable.Visible", 1654, 1);
-}
-
-IN_PROC_BROWSER_TEST_F(PrerenderBrowserTest, FirstContentfulPaintTimingReuse) {
-  GetPrerenderManager()->DisablePageLoadMetricsObserverForTesting();
-  base::SimpleTestTickClock* clock = OverridePrerenderManagerTimeTicks();
-
-  GURL url = embedded_test_server()->GetURL("/prerender/prerender_page.html");
-  base::RunLoop hanging_request_waiter;
-  CreateHangingFirstRequestInterceptor(url, GetTestPath("prerender_page.html"),
-                                       hanging_request_waiter.QuitClosure());
-  // As this load will be canceled, it is not waited for, and hence no
-  // javascript is executed.
-  DisableJavascriptCalls();
-  PrerenderTestURL(url, FINAL_STATUS_CANCELLED, 0);
-  hanging_request_waiter.Run();
-
-  // This prerender cancels and reuses the first.
-  clock->Advance(base::TimeDelta::FromSeconds(1));
-  base::TimeTicks load_start = clock->NowTicks();
-  EnableJavascriptCalls();
-  PrerenderTestURL(url, FINAL_STATUS_USED, 1);
-  clock->Advance(base::TimeDelta::FromSeconds(1));
-
-  NavigateToDestURL();
-  PrerenderPageLoadMetricsObserver observer(GetPrerenderManager(),
-                                            GetActiveWebContents());
-  observer.SetNavigationStartTicksForTesting(load_start);
-
-  page_load_metrics::mojom::PageLoadTiming timing;
-  page_load_metrics::InitPageLoadTimingForTest(&timing);
-  timing.navigation_start = base::Time::FromDoubleT(1);  // Non-null time.
-  timing.paint_timing->first_contentful_paint =
-      base::TimeDelta::FromMilliseconds(2361);
-  PopulateRequiredTimingFields(&timing);
-  observer.OnFirstContentfulPaintInPage(timing,
-                                        GenericPageLoadExtraInfo(dest_url()));
-
-  histogram_tester().ExpectTotalCount(
-      "Prerender.websame_PrefetchTTFCP.Warm.Cacheable.Visible", 1);
-  // If the first prerender had been used, the perceived TTFCP would have been
-  // under a second: 2362ms - 2 sec worth of Advance().
-  histogram_tester().ExpectUniqueSample(
-      "Prerender.websame_PrefetchTTFCP.Warm.Cacheable.Visible", 1361, 1);
-}
-
-IN_PROC_BROWSER_TEST_F(PrerenderBrowserTest,
-                       FirstContentfulPaintTimingTimeout) {
-  GetPrerenderManager()->DisablePageLoadMetricsObserverForTesting();
-  base::SimpleTestTickClock* clock = OverridePrerenderManagerTimeTicks();
-
-  // Make the first prerender time out.
-  base::TimeDelta time_out_delta =
-      GetPrerenderManager()->config().time_to_live +
-      base::TimeDelta::FromSeconds(10);
-  SetMidLoadClockAdvance(clock, time_out_delta);
-
-  GURL url = embedded_test_server()->GetURL("/prerender/prerender_page.html");
-  PrerenderTestURL(url, FINAL_STATUS_TIMED_OUT, 1);
-
-  ClearMidLoadClock();
-  base::TimeTicks load_start = clock->NowTicks();
-  PrerenderTestURL(url, FINAL_STATUS_USED, 1);
-
-  clock->Advance(base::TimeDelta::FromSeconds(1));
-  NavigateToDestURL();
-
-  PrerenderPageLoadMetricsObserver observer(GetPrerenderManager(),
-                                            GetActiveWebContents());
-  observer.SetNavigationStartTicksForTesting(load_start);
-
-  page_load_metrics::mojom::PageLoadTiming timing;
-  page_load_metrics::InitPageLoadTimingForTest(&timing);
-  timing.navigation_start = base::Time::FromDoubleT(1);  // Non-null time.
-  timing.paint_timing->first_contentful_paint =
-      base::TimeDelta::FromMilliseconds(2361);
-  PopulateRequiredTimingFields(&timing);
-  observer.OnFirstContentfulPaintInPage(timing,
-                                        GenericPageLoadExtraInfo(dest_url()));
-
-  histogram_tester().ExpectTotalCount(
-      "Prerender.websame_PrefetchTTFCP.Warm.Cacheable.Visible", 1);
-  // If the timed out had been used, the perceived TTFCP would have been
-  // negative.
-  histogram_tester().ExpectUniqueSample(
-      "Prerender.websame_PrefetchTTFCP.Warm.Cacheable.Visible", 1361, 1);
-}
-
-IN_PROC_BROWSER_TEST_F(PrerenderBrowserTest,
-                       FirstContentfulPaintTimingNoCommit) {
-  GetPrerenderManager()->DisablePageLoadMetricsObserverForTesting();
-  base::SimpleTestTickClock* clock = OverridePrerenderManagerTimeTicks();
-
-  GURL url = embedded_test_server()->GetURL("/prerender/prerender_page.html");
-  base::FilePath url_file = ui_test_utils::GetTestFilePath(
-      base::FilePath(),
-      base::FilePath(FILE_PATH_LITERAL("prerender/prerender_page.html")));
-
-  base::RunLoop prerender_start_loop;
-  CreateHangingFirstRequestInterceptor(url, url_file,
-                                       prerender_start_loop.QuitClosure());
-  // As this load is uncommitted, it is not waited for, and hence no
-  // javascript is executed.
-  DisableJavascriptCalls();
-  PrerenderTestURL(url, FINAL_STATUS_NAVIGATION_UNCOMMITTED, 0);
-  prerender_start_loop.Run();
-
-  clock->Advance(base::TimeDelta::FromSeconds(1));
-  NavigateToDestURLWithDisposition(WindowOpenDisposition::CURRENT_TAB, false);
-
-  PrerenderPageLoadMetricsObserver observer(GetPrerenderManager(),
-                                            GetActiveWebContents());
-  observer.SetNavigationStartTicksForTesting(clock->NowTicks());
-
-  page_load_metrics::mojom::PageLoadTiming timing;
-  page_load_metrics::InitPageLoadTimingForTest(&timing);
-  timing.navigation_start = base::Time::FromDoubleT(1);  // Non-null time.
-  timing.paint_timing->first_contentful_paint =
-      base::TimeDelta::FromMilliseconds(2362);
-  PopulateRequiredTimingFields(&timing);
-  observer.OnFirstContentfulPaintInPage(timing,
-                                        GenericPageLoadExtraInfo(dest_url()));
-
-  histogram_tester().ExpectTotalCount(
-      "Prerender.none_PrefetchTTFCP.Warm.Cacheable.Visible", 0);
-  histogram_tester().ExpectTotalCount(
-      "Prerender.none_PerceivedTTFCPRecorded.Visible", 1);
-
-  // Check that the prerender didn't happen with a defined origin.
-  histogram_tester().ExpectTotalCount(
-      "Prerender.websame_PrefetchTTFCP.Warm.Cacheable.Visible", 0);
-  histogram_tester().ExpectTotalCount(
-      "Prerender.websame_PerceivedTTFCPRecorded.Visible", 0);
-
-  // A FCP is fired by the observer, but as a swap did not occur the perceived
-  // time cannot be calculated, and an unrecorded perceived FCP time histogram
-  // entry is made.
-  histogram_tester().ExpectUniqueSample(
-      "Prerender.none_PerceivedTTFCPRecorded.Visible", 0, 1);
-}
-
-// Disabled on ChromeOS due to flakiness. See https://crbug.com/808578.
-#if defined(OS_CHROMEOS)
-#define MAYBE_FirstContentfulPaintTimingTwoPages \
-  DISABLED_FirstContentfulPaintTimingTwoPages
-#else
-#define MAYBE_FirstContentfulPaintTimingTwoPages \
-  FirstContentfulPaintTimingTwoPages
-#endif
-IN_PROC_BROWSER_TEST_F(PrerenderBrowserTest,
-                       MAYBE_FirstContentfulPaintTimingTwoPages) {
-  GetPrerenderManager()->DisablePageLoadMetricsObserverForTesting();
-  base::SimpleTestTickClock* clock = OverridePrerenderManagerTimeTicks();
-
-  // As this load will be canceled, it is not waited for, and hence no
-  // javascript is executed.
-  DisableJavascriptCalls();
-  // First prerender a different page from the usual target.
-  PrerenderTestURL("/prerender/prefetch_page.html", FINAL_STATUS_CANCELLED, 0);
-
-  clock->Advance(base::TimeDelta::FromSeconds(1));
-  base::TimeTicks load_start = clock->NowTicks();
-  EnableJavascriptCalls();
-  PrerenderTestURL("/prerender/prerender_page.html", FINAL_STATUS_USED, 1);
-
-  clock->Advance(base::TimeDelta::FromSeconds(1));
-  NavigateToDestURL();
-
-  PrerenderPageLoadMetricsObserver observer(GetPrerenderManager(),
-                                            GetActiveWebContents());
-  observer.SetNavigationStartTicksForTesting(load_start);
-
-  page_load_metrics::mojom::PageLoadTiming timing;
-  page_load_metrics::InitPageLoadTimingForTest(&timing);
-  timing.navigation_start = base::Time::FromDoubleT(1);  // Non-null time.
-  // The FCP time should end up on the edge of the bucket.
-  timing.paint_timing->first_contentful_paint =
-      base::TimeDelta::FromMilliseconds(2654);
-  PopulateRequiredTimingFields(&timing);
-  observer.OnFirstContentfulPaintInPage(timing,
-                                        GenericPageLoadExtraInfo(dest_url()));
-
-  histogram_tester().ExpectTotalCount(
-      "Prerender.websame_PrefetchTTFCP.Warm.Cacheable.Visible", 1);
-  histogram_tester().ExpectUniqueSample(
-      "Prerender.websame_PrefetchTTFCP.Warm.Cacheable.Visible", 1654, 1);
-}
-
-IN_PROC_BROWSER_TEST_F(PrerenderBrowserTest, FirstContentfulPaintHidden) {
-  GetPrerenderManager()->DisablePageLoadMetricsObserverForTesting();
-  base::SimpleTestTickClock* clock = OverridePrerenderManagerTimeTicks();
-
-  base::TimeTicks load_start = clock->NowTicks();
-  PrerenderTestURL("/prerender/prerender_page.html", FINAL_STATUS_USED, 1);
-
-  clock->Advance(base::TimeDelta::FromSeconds(1));
-  NavigateToDestURL();
-
-  PrerenderPageLoadMetricsObserver observer(GetPrerenderManager(),
-                                            GetActiveWebContents());
-  observer.SetNavigationStartTicksForTesting(load_start);
-
-  EXPECT_EQ(page_load_metrics::PageLoadMetricsObserver::CONTINUE_OBSERVING,
-            observer.OnHidden(page_load_metrics::mojom::PageLoadTiming(),
-                              GenericPageLoadExtraInfo(dest_url())));
-
-  page_load_metrics::mojom::PageLoadTiming timing;
-  page_load_metrics::InitPageLoadTimingForTest(&timing);
-  timing.navigation_start = base::Time::FromDoubleT(1);  // Non-null time.
-  timing.paint_timing->first_contentful_paint =
-      base::TimeDelta::FromMilliseconds(2654);
-  PopulateRequiredTimingFields(&timing);
-  observer.OnFirstContentfulPaintInPage(timing,
-                                        GenericPageLoadExtraInfo(dest_url()));
-
-  histogram_tester().ExpectTotalCount(
-      "Prerender.websame_PrefetchTTFCP.Warm.Cacheable.Hidden", 1);
-  histogram_tester().ExpectUniqueSample(
-      "Prerender.websame_PrefetchTTFCP.Warm.Cacheable.Hidden", 1654, 1);
-}
-
-// Flaky on chromium.linux/Linux Tests (dbg). https://crbug.com/832597
-IN_PROC_BROWSER_TEST_F(PrerenderBrowserTest,
-                       DISABLED_FirstContentfulPaintHiddenNoCommit) {
-  GetPrerenderManager()->DisablePageLoadMetricsObserverForTesting();
-  base::SimpleTestTickClock* clock = OverridePrerenderManagerTimeTicks();
-
-  GURL url = embedded_test_server()->GetURL("/prerender/prerender_page.html");
-  base::FilePath url_file = ui_test_utils::GetTestFilePath(
-      base::FilePath(),
-      base::FilePath(FILE_PATH_LITERAL("prerender/prerender_page.html")));
-
-  base::RunLoop prerender_start_loop;
-  CreateHangingFirstRequestInterceptor(url, url_file,
-                                       prerender_start_loop.QuitClosure());
-
-  // As this load is uncommitted, it is not waited for, and hence no
-  // javascript is executed.
-  DisableJavascriptCalls();
-  PrerenderTestURL(url, FINAL_STATUS_NAVIGATION_UNCOMMITTED, 0);
-  prerender_start_loop.Run();
-
-  clock->Advance(base::TimeDelta::FromSeconds(1));
-  NavigateToDestURLWithDisposition(WindowOpenDisposition::CURRENT_TAB, false);
-
-  PrerenderPageLoadMetricsObserver observer(GetPrerenderManager(),
-                                            GetActiveWebContents());
-  observer.SetNavigationStartTicksForTesting(clock->NowTicks());
-
-  EXPECT_EQ(page_load_metrics::PageLoadMetricsObserver::CONTINUE_OBSERVING,
-            observer.OnHidden(page_load_metrics::mojom::PageLoadTiming(),
-                              GenericPageLoadExtraInfo(dest_url())));
-
-  page_load_metrics::mojom::PageLoadTiming timing;
-  page_load_metrics::InitPageLoadTimingForTest(&timing);
-  timing.navigation_start = base::Time::FromDoubleT(1);  // Non-null time.
-  timing.paint_timing->first_contentful_paint =
-      base::TimeDelta::FromMilliseconds(2362);
-  PopulateRequiredTimingFields(&timing);
-  observer.OnFirstContentfulPaintInPage(timing,
-                                        GenericPageLoadExtraInfo(dest_url()));
-
-  histogram_tester().ExpectTotalCount(
-      "Prerender.none_PrefetchTTFCP.Warm.Cacheable.Hidden", 0);
-  histogram_tester().ExpectTotalCount(
-      "Prerender.none_PerceivedTTFCPRecorded.Hidden", 1);
-
-  // Check that the prerender didn't happen with a defined origin.
-  histogram_tester().ExpectTotalCount(
-      "Prerender.websame_PrefetchTTFCP.Warm.Cacheable.Hidden", 0);
-  histogram_tester().ExpectTotalCount(
-      "Prerender.websame_PerceivedTTFCPRecorded.Hidden", 0);
-
-  // A FCP is fired by the observer, but the manager should detect that the
-  // perceived time is not set and so update the following histogram.
-  histogram_tester().ExpectUniqueSample(
-      "Prerender.none_PerceivedTTFCPRecorded.Hidden", 0, 1);
-}
-
 // When instantiated, mocks out the global text-to-speech engine with something
 // that emulates speaking any phrase for the duration of 0ms.
 class TtsPlatformMock : public content::TtsPlatform {
diff --git a/chrome/browser/prerender/prerender_histograms.cc b/chrome/browser/prerender/prerender_histograms.cc
index 1469fccf..3eb5546 100644
--- a/chrome/browser/prerender/prerender_histograms.cc
+++ b/chrome/browser/prerender/prerender_histograms.cc
@@ -61,15 +61,6 @@
   return "none";
 }
 
-void PrerenderHistograms::RecordPerceivedFirstContentfulPaintStatus(
-    Origin origin,
-    bool successful,
-    bool was_hidden) const {
-  base::UmaHistogramBoolean(GetHistogramName(origin, "PerceivedTTFCPRecorded") +
-                                FirstContentfulPaintHiddenName(was_hidden),
-                            successful);
-}
-
 void PrerenderHistograms::RecordFinalStatus(
     Origin origin,
     FinalStatus final_status) const {
diff --git a/chrome/browser/prerender/prerender_histograms.h b/chrome/browser/prerender/prerender_histograms.h
index 51dbcb3..b5d3b4b 100644
--- a/chrome/browser/prerender/prerender_histograms.h
+++ b/chrome/browser/prerender/prerender_histograms.h
@@ -49,12 +49,6 @@
   // of the prerender.
   static std::string GetHistogramPrefix(Origin origin);
 
-  // Record that a first contentful paint occured, and whether we were able to
-  // successfuly record the perceived FCP.
-  void RecordPerceivedFirstContentfulPaintStatus(Origin origin,
-                                                 bool successful,
-                                                 bool was_hidden) const;
-
   // Record a PerSessionCount data point.
   void RecordPerSessionCount(Origin origin, int count) const;
 
diff --git a/chrome/browser/prerender/prerender_manager.cc b/chrome/browser/prerender/prerender_manager.cc
index 3e563a4..d6d856e 100644
--- a/chrome/browser/prerender/prerender_manager.cc
+++ b/chrome/browser/prerender/prerender_manager.cc
@@ -496,41 +496,6 @@
 
   histograms_->RecordPrefetchFirstContentfulPaintTime(
       origin, is_no_store, was_hidden, time, prefetch_age);
-
-  for (auto& observer : observers_) {
-    observer->OnFirstContentfulPaint();
-  }
-}
-
-void PrerenderManager::RecordPrerenderFirstContentfulPaint(
-    const GURL& url,
-    content::WebContents* web_contents,
-    bool is_no_store,
-    bool was_hidden,
-    base::TimeTicks first_contentful_paint) {
-  DCHECK(!first_contentful_paint.is_null());
-
-  PrerenderTabHelper* tab_helper =
-      PrerenderTabHelper::FromWebContents(web_contents);
-  DCHECK(tab_helper);
-
-  base::TimeDelta prefetch_age;
-  // The origin at prefetch is superceeded by the tab_helper origin for the
-  // histogram recording, below.
-  GetPrefetchInformation(url, &prefetch_age, nullptr);
-  OnPrefetchUsed(url);
-
-  base::TimeTicks swap_ticks = tab_helper->swap_ticks();
-  bool fcp_recorded = false;
-  if (!swap_ticks.is_null() && !first_contentful_paint.is_null()) {
-    histograms_->RecordPrefetchFirstContentfulPaintTime(
-        tab_helper->origin(), is_no_store, was_hidden,
-        first_contentful_paint - swap_ticks, prefetch_age);
-    fcp_recorded = true;
-  }
-  histograms_->RecordPerceivedFirstContentfulPaintStatus(
-      tab_helper->origin(), fcp_recorded, was_hidden);
-
   for (auto& observer : observers_) {
     observer->OnFirstContentfulPaint();
   }
diff --git a/chrome/browser/prerender/prerender_manager.h b/chrome/browser/prerender/prerender_manager.h
index d8ef34e..b190bda 100644
--- a/chrome/browser/prerender/prerender_manager.h
+++ b/chrome/browser/prerender/prerender_manager.h
@@ -202,17 +202,6 @@
                                          bool was_hidden,
                                          base::TimeDelta time);
 
-  // Records the perceived first contentful paint time for a prerendered page.
-  // The actual load may have started prior to navigation due to prerender
-  // hints. The FCP ticks is in absolute time; this has the disadvantage that
-  // the histogram will mix browser and renderer ticks, but there seems to be no
-  // way around that.
-  void RecordPrerenderFirstContentfulPaint(const GURL& url,
-                                           content::WebContents* web_contents,
-                                           bool is_no_store,
-                                           bool was_hidden,
-                                           base::TimeTicks ticks);
-
   static PrerenderManagerMode GetMode() { return mode_; }
   static void SetMode(PrerenderManagerMode mode) { mode_ = mode; }
 
diff --git a/chrome/browser/previews/previews_browsertest.cc b/chrome/browser/previews/previews_browsertest.cc
index 3ffb076..07b8714 100644
--- a/chrome/browser/previews/previews_browsertest.cc
+++ b/chrome/browser/previews/previews_browsertest.cc
@@ -238,6 +238,13 @@
     PreviewsBrowserTest::SetUp();
   }
 
+  void SetUpCommandLine(base::CommandLine* cmd) override {
+    cmd->AppendSwitch("enable-spdy-proxy-auth");
+    cmd->AppendSwitch("optimization-guide-disable-installer");
+    cmd->AppendSwitch("purge_hint_cache_store");
+    cmd->AppendSwitch(previews::switches::kIgnorePreviewsBlacklist);
+  }
+
   // Creates hint data for the |hint_setup_url|'s host and then performs a
   // navigation to |hint_setup_url| to trigger the hints to be loaded into the
   // hint cache so they will be available for a subsequent navigation to a test
@@ -283,24 +290,17 @@
       test_hints_component_creator_;
 };
 
-// Previews InfoBar (which these tests triggers) does not work on Mac.
-// See https://crbug.com/782322 for detail.
-// Also occasional flakes on win7 (https://crbug.com/789542).
-#if defined(OS_ANDROID) || defined(OS_LINUX)
-#define MAYBE_NoScriptPreviewsEnabled NoScriptPreviewsEnabled
-#define MAYBE_NoScriptPreviewsEnabledHttpRedirectToHttps \
-  NoScriptPreviewsEnabledHttpRedirectToHttps
+#if defined(OS_WIN) || defined(OS_MACOSX) || defined(OS_CHROMEOS)
+#define DISABLE_ON_WIN_MAC_CHROMESOS(x) DISABLED_##x
 #else
-#define MAYBE_NoScriptPreviewsEnabled DISABLED_NoScriptPreviewsEnabled
-#define MAYBE_NoScriptPreviewsEnabledHttpRedirectToHttps \
-  DISABLED_NoScriptPreviewsEnabledHttpRedirectToHttps
+#define DISABLE_ON_WIN_MAC_CHROMESOS(x) x
 #endif
 
 // Loads a webpage that has both script and noscript tags and also requests
 // a script resource. Verifies that the noscript tag is evaluated and the
 // script resource is not loaded.
 IN_PROC_BROWSER_TEST_F(PreviewsNoScriptBrowserTest,
-                       MAYBE_NoScriptPreviewsEnabled) {
+                       DISABLE_ON_WIN_MAC_CHROMESOS(NoScriptPreviewsEnabled)) {
   GURL url = https_url();
 
   // Whitelist NoScript for https_hint_setup_url()'s' host.
@@ -318,8 +318,9 @@
                                      "Previews.InfoBarAction.NoScript", 1);
 }
 
-IN_PROC_BROWSER_TEST_F(PreviewsNoScriptBrowserTest,
-                       NoScriptPreviewsEnabledButHttpRequest) {
+IN_PROC_BROWSER_TEST_F(
+    PreviewsNoScriptBrowserTest,
+    DISABLE_ON_WIN_MAC_CHROMESOS(NoScriptPreviewsEnabledButHttpRequest)) {
   GURL url = http_url();
 
   // Whitelist NoScript for http_hint_setup_url() host.
@@ -332,17 +333,9 @@
   EXPECT_FALSE(noscript_css_requested());
 }
 
-// Flaky in all platforms except Android. See https://crbug.com/803626 for
-// detail.
-#if defined(OS_ANDROID) || defined(OS_LINUX)
-#define MAYBE_NoScriptPreviewsEnabledButNoTransformDirective \
-  NoScriptPreviewsEnabledButNoTransformDirective
-#else
-#define MAYBE_NoScriptPreviewsEnabledButNoTransformDirective \
-  DISABLED_NoScriptPreviewsEnabledButNoTransformDirective
-#endif
 IN_PROC_BROWSER_TEST_F(PreviewsNoScriptBrowserTest,
-                       MAYBE_NoScriptPreviewsEnabledButNoTransformDirective) {
+                       DISABLE_ON_WIN_MAC_CHROMESOS(
+                           NoScriptPreviewsEnabledButNoTransformDirective)) {
   GURL url = https_no_transform_url();
 
   // Whitelist NoScript for https_hint_setup_url()'s' host.
@@ -359,8 +352,9 @@
       "Previews.CacheControlNoTransform.BlockedPreview", 5 /* NoScript */, 1);
 }
 
-IN_PROC_BROWSER_TEST_F(PreviewsNoScriptBrowserTest,
-                       MAYBE_NoScriptPreviewsEnabledHttpRedirectToHttps) {
+IN_PROC_BROWSER_TEST_F(
+    PreviewsNoScriptBrowserTest,
+    DISABLE_ON_WIN_MAC_CHROMESOS(NoScriptPreviewsEnabledHttpRedirectToHttps)) {
   GURL url = redirect_url();
 
   // Whitelist NoScript for http_hint_setup_url() host.
@@ -378,16 +372,9 @@
                                      "Previews.InfoBarAction.NoScript", 1);
 }
 
-// Flaky in all platforms except Android. See https://crbug.com/803626 for
-// detail.
-#if defined(OS_ANDROID) || defined(OS_LINUX)
-#define MAYBE_NoScriptPreviewsRecordsOptOut NoScriptPreviewsRecordsOptOut
-#else
-#define MAYBE_NoScriptPreviewsRecordsOptOut \
-  DISABLED_NoScriptPreviewsRecordsOptOut
-#endif
-IN_PROC_BROWSER_TEST_F(PreviewsNoScriptBrowserTest,
-                       MAYBE_NoScriptPreviewsRecordsOptOut) {
+IN_PROC_BROWSER_TEST_F(
+    PreviewsNoScriptBrowserTest,
+    DISABLE_ON_WIN_MAC_CHROMESOS(NoScriptPreviewsRecordsOptOut)) {
   GURL url = redirect_url();
 
   // Whitelist NoScript for http_hint_setup_url()'s' host.
@@ -412,20 +399,9 @@
                                      1);
 }
 
-// Previews InfoBar (which this test triggers) does not work on Mac.
-// See https://crbug.com/782322 for detail.
-// Also occasional flakes on win7 (https://crbug.com/789948) and Ubuntu 16.04
-// (https://crbug.com/831838)
-#if defined(OS_ANDROID)
-#define MAYBE_NoScriptPreviewsEnabledByWhitelist \
-  NoScriptPreviewsEnabledByWhitelist
-#else
-#define MAYBE_NoScriptPreviewsEnabledByWhitelist \
-  DISABLED_NoScriptPreviewsEnabledByWhitelist
-#endif
-
-IN_PROC_BROWSER_TEST_F(PreviewsNoScriptBrowserTest,
-                       MAYBE_NoScriptPreviewsEnabledByWhitelist) {
+IN_PROC_BROWSER_TEST_F(
+    PreviewsNoScriptBrowserTest,
+    DISABLE_ON_WIN_MAC_CHROMESOS(NoScriptPreviewsEnabledByWhitelist)) {
   GURL url = https_url();
 
   // Whitelist NoScript for https_hint_setup_url()'s' host.
@@ -438,8 +414,9 @@
   EXPECT_FALSE(noscript_js_requested());
 }
 
-IN_PROC_BROWSER_TEST_F(PreviewsNoScriptBrowserTest,
-                       NoScriptPreviewsNotEnabledByWhitelist) {
+IN_PROC_BROWSER_TEST_F(
+    PreviewsNoScriptBrowserTest,
+    DISABLE_ON_WIN_MAC_CHROMESOS(NoScriptPreviewsNotEnabledByWhitelist)) {
   GURL url = https_url();
 
   // Whitelist random site for NoScript.
@@ -496,6 +473,7 @@
   void SetUpCommandLine(base::CommandLine* cmd) override {
     CertVerifierBrowserTest::SetUpCommandLine(cmd);
     cmd->AppendSwitch("enable-spdy-proxy-auth");
+
     // Due to race conditions, it's possible that blacklist data is not loaded
     // at the time of first navigation. That may prevent Preview from
     // triggering, and causing the test to flake.
diff --git a/chrome/browser/previews/previews_lite_page_browsertest.cc b/chrome/browser/previews/previews_lite_page_browsertest.cc
index 5b1811b..e1bcd84 100644
--- a/chrome/browser/previews/previews_lite_page_browsertest.cc
+++ b/chrome/browser/previews/previews_lite_page_browsertest.cc
@@ -794,14 +794,15 @@
 // Previews InfoBar (which these tests trigger) does not work on Mac.
 // See https://crbug.com/782322 for detail.
 // Also occasional flakes on win7 (https://crbug.com/789542).
-#if defined(OS_WIN) || defined(OS_MACOSX)
-#define DISABLE_ON_WIN_MAC(x) DISABLED_##x
+#if defined(OS_WIN) || defined(OS_MACOSX) || defined(OS_CHROMEOS)
+#define DISABLE_ON_WIN_MAC_CHROMESOS(x) DISABLED_##x
 #else
-#define DISABLE_ON_WIN_MAC(x) x
+#define DISABLE_ON_WIN_MAC_CHROMESOS(x) x
 #endif
 
-IN_PROC_BROWSER_TEST_P(PreviewsLitePageServerBrowserTest,
-                       DISABLE_ON_WIN_MAC(LitePagePreviewsTriggering)) {
+IN_PROC_BROWSER_TEST_P(
+    PreviewsLitePageServerBrowserTest,
+    DISABLE_ON_WIN_MAC_CHROMESOS(LitePagePreviewsTriggering)) {
   // TODO(crbug.com/874150): Use ExpectUniqueSample in these tests.
   // The histograms in these tests can only be checked by the expected bucket,
   // and not by a unique sample. This is because each navigation to a preview
@@ -1004,8 +1005,9 @@
   }
 }
 
-IN_PROC_BROWSER_TEST_P(PreviewsLitePageServerBrowserTest,
-                       DISABLE_ON_WIN_MAC(LitePagePreviewsReloadDisabled)) {
+IN_PROC_BROWSER_TEST_P(
+    PreviewsLitePageServerBrowserTest,
+    DISABLE_ON_WIN_MAC_CHROMESOS(LitePagePreviewsReloadDisabled)) {
   // Start with a non-preview load.
   g_browser_process->network_quality_tracker()
       ->ReportEffectiveConnectionTypeForTesting(
@@ -1024,7 +1026,7 @@
 
 IN_PROC_BROWSER_TEST_P(
     PreviewsLitePageServerBrowserTest,
-    DISABLE_ON_WIN_MAC(ReloadingLitePagesDisablesLitePages)) {
+    DISABLE_ON_WIN_MAC_CHROMESOS(ReloadingLitePagesDisablesLitePages)) {
   base::test::ScopedFeatureList scoped_feature_list;
   scoped_feature_list.InitWithFeatures(
       {previews::features::kPreviewsReloadsAreSoftOptOuts}, {});
@@ -1040,8 +1042,9 @@
   VerifyPreviewNotLoaded();
 }
 
-IN_PROC_BROWSER_TEST_P(PreviewsLitePageServerBrowserTest,
-                       DISABLE_ON_WIN_MAC(LitePagePreviewsLoadOriginal)) {
+IN_PROC_BROWSER_TEST_P(
+    PreviewsLitePageServerBrowserTest,
+    DISABLE_ON_WIN_MAC_CHROMESOS(LitePagePreviewsLoadOriginal)) {
   base::HistogramTester histogram_tester;
   ui_test_utils::NavigateToURL(browser(), HttpsLitePageURL(kSuccess));
   VerifyPreviewLoaded();
@@ -1053,7 +1056,7 @@
 }
 
 IN_PROC_BROWSER_TEST_P(PreviewsLitePageServerBrowserTest,
-                       DISABLE_ON_WIN_MAC(LitePagePreviewsRedirect)) {
+                       DISABLE_ON_WIN_MAC_CHROMESOS(LitePagePreviewsRedirect)) {
   {
     // Verify the preview is triggered when an HTTP page redirects to HTTPS.
     base::HistogramTester histogram_tester;
@@ -1107,7 +1110,7 @@
 }
 
 IN_PROC_BROWSER_TEST_P(PreviewsLitePageServerBrowserTest,
-                       DISABLE_ON_WIN_MAC(LitePagePreviewsResponse)) {
+                       DISABLE_ON_WIN_MAC_CHROMESOS(LitePagePreviewsResponse)) {
   {
     // Verify the preview is not triggered when the server responds with bypass
     // 307.
@@ -1187,7 +1190,7 @@
 }
 
 IN_PROC_BROWSER_TEST_P(PreviewsLitePageServerBrowserTest,
-                       DISABLE_ON_WIN_MAC(LitePagePreviewsLoadshed)) {
+                       DISABLE_ON_WIN_MAC_CHROMESOS(LitePagePreviewsLoadshed)) {
   PreviewsService* previews_service =
       PreviewsServiceFactory::GetForProfile(browser()->profile());
   ASSERT_TRUE(previews_service);
@@ -1231,8 +1234,9 @@
   VerifyPreviewLoaded();
 }
 
-IN_PROC_BROWSER_TEST_P(PreviewsLitePageServerBrowserTest,
-                       DISABLE_ON_WIN_MAC(LitePageURLNotReportedToHistory)) {
+IN_PROC_BROWSER_TEST_P(
+    PreviewsLitePageServerBrowserTest,
+    DISABLE_ON_WIN_MAC_CHROMESOS(LitePageURLNotReportedToHistory)) {
   base::CancelableTaskTracker tracker_;
   history::HistoryService* history_service =
       HistoryServiceFactory::GetForProfile(browser()->profile(),
@@ -1291,8 +1295,9 @@
   ClearDeciderState();
 }
 
-IN_PROC_BROWSER_TEST_P(PreviewsLitePageServerBrowserTest,
-                       DISABLE_ON_WIN_MAC(LitePagePreviewsReportSavings)) {
+IN_PROC_BROWSER_TEST_P(
+    PreviewsLitePageServerBrowserTest,
+    DISABLE_ON_WIN_MAC_CHROMESOS(LitePagePreviewsReportSavings)) {
   PrefService* prefs = browser()->profile()->GetPrefs();
   prefs->SetBoolean(data_reduction_proxy::prefs::kDataUsageReportingEnabled,
                     true);
@@ -1313,8 +1318,9 @@
   EXPECT_EQ(GetTotalOriginalContentLength() - GetTotalDataUsage(), 40U);
 }
 
-IN_PROC_BROWSER_TEST_P(PreviewsLitePageServerBrowserTest,
-                       DISABLE_ON_WIN_MAC(LitePagePreviewsClientRedirect)) {
+IN_PROC_BROWSER_TEST_P(
+    PreviewsLitePageServerBrowserTest,
+    DISABLE_ON_WIN_MAC_CHROMESOS(LitePagePreviewsClientRedirect)) {
   // Navigate to a non-preview first.
   ui_test_utils::NavigateToURL(browser(), https_media_url());
   VerifyPreviewNotLoaded();
@@ -1327,8 +1333,9 @@
             https_media_url());
 }
 
-IN_PROC_BROWSER_TEST_P(PreviewsLitePageServerBrowserTest,
-                       DISABLE_ON_WIN_MAC(LitePagePreviewsNavigation)) {
+IN_PROC_BROWSER_TEST_P(
+    PreviewsLitePageServerBrowserTest,
+    DISABLE_ON_WIN_MAC_CHROMESOS(LitePagePreviewsNavigation)) {
   ui_test_utils::NavigateToURL(browser(), HttpsLitePageURL(kSuccess));
   VerifyPreviewLoaded();
 
@@ -1355,7 +1362,7 @@
 }
 
 IN_PROC_BROWSER_TEST_P(PreviewsLitePageServerBrowserTest,
-                       DISABLE_ON_WIN_MAC(LitePageCreatesPingback)) {
+                       DISABLE_ON_WIN_MAC_CHROMESOS(LitePageCreatesPingback)) {
   ui_test_utils::NavigateToURL(browser(), HttpsLitePageURL(kSuccess));
   VerifyPreviewLoaded();
 
@@ -1384,7 +1391,7 @@
                          testing::Bool());
 
 IN_PROC_BROWSER_TEST_P(PreviewsLitePageServerTimeoutBrowserTest,
-                       DISABLE_ON_WIN_MAC(LitePagePreviewsTimeout)) {
+                       DISABLE_ON_WIN_MAC_CHROMESOS(LitePagePreviewsTimeout)) {
   {
     // Ensure that a hung previews navigation doesn't wind up at the previews
     // server.
@@ -1429,8 +1436,9 @@
                          PreviewsLitePageServerBadServerBrowserTest,
                          testing::Bool());
 
-IN_PROC_BROWSER_TEST_P(PreviewsLitePageServerBadServerBrowserTest,
-                       DISABLE_ON_WIN_MAC(LitePagePreviewsBadServer)) {
+IN_PROC_BROWSER_TEST_P(
+    PreviewsLitePageServerBadServerBrowserTest,
+    DISABLE_ON_WIN_MAC_CHROMESOS(LitePagePreviewsBadServer)) {
   // TODO(crbug.com/874150): Use ExpectUniqueSample in this tests.
   // The histograms in this tests can only be checked by the expected bucket,
   // and not by a unique sample. This is because each navigation to a preview
@@ -1475,8 +1483,9 @@
                          PreviewsLitePageServerDataSaverBrowserTest,
                          testing::Bool());
 
-IN_PROC_BROWSER_TEST_P(PreviewsLitePageServerDataSaverBrowserTest,
-                       DISABLE_ON_WIN_MAC(LitePagePreviewsDSTriggering)) {
+IN_PROC_BROWSER_TEST_P(
+    PreviewsLitePageServerDataSaverBrowserTest,
+    DISABLE_ON_WIN_MAC_CHROMESOS(LitePagePreviewsDSTriggering)) {
   // Verify the preview is not triggered on HTTPS pageloads without DataSaver.
   ui_test_utils::NavigateToURL(browser(), HttpsLitePageURL(kSuccess));
   VerifyPreviewNotLoaded();
@@ -1510,7 +1519,7 @@
 
 IN_PROC_BROWSER_TEST_P(
     PreviewsLitePageServerNoDataSaverHeaderBrowserTest,
-    DISABLE_ON_WIN_MAC(LitePagePreviewsDSNoHeaderTriggering)) {
+    DISABLE_ON_WIN_MAC_CHROMESOS(LitePagePreviewsDSNoHeaderTriggering)) {
   // Verify the preview is not triggered on HTTPS pageloads without data saver.
   ui_test_utils::NavigateToURL(browser(), HttpsLitePageURL(kSuccess));
   VerifyPreviewNotLoaded();
@@ -1546,7 +1555,7 @@
 
 IN_PROC_BROWSER_TEST_P(
     PreviewsLitePageNotificationDSEnabledBrowserTest,
-    DISABLE_ON_WIN_MAC(LitePagePreviewsInfoBarDataSaverUser)) {
+    DISABLE_ON_WIN_MAC_CHROMESOS(LitePagePreviewsInfoBarDataSaverUser)) {
   // Ensure the preview is not shown the first time before the infobar is shown
   // for users who have DRP enabled.
   base::HistogramTester histogram_tester;
@@ -1613,7 +1622,7 @@
 
 IN_PROC_BROWSER_TEST_P(
     PreviewsLitePageNotificationDSDisabledBrowserTest,
-    DISABLE_ON_WIN_MAC(LitePagePreviewsInfoBarNonDataSaverUser)) {
+    DISABLE_ON_WIN_MAC_CHROMESOS(LitePagePreviewsInfoBarNonDataSaverUser)) {
   ui_test_utils::NavigateToURL(browser(), HttpsLitePageURL(kSuccess));
   VerifyPreviewNotLoaded();
   ClearDeciderState();
@@ -1639,8 +1648,9 @@
                          PreviewsLitePageControlBrowserTest,
                          testing::Bool());
 
-IN_PROC_BROWSER_TEST_P(PreviewsLitePageControlBrowserTest,
-                       DISABLE_ON_WIN_MAC(LitePagePreviewsControlGroup)) {
+IN_PROC_BROWSER_TEST_P(
+    PreviewsLitePageControlBrowserTest,
+    DISABLE_ON_WIN_MAC_CHROMESOS(LitePagePreviewsControlGroup)) {
   base::HistogramTester histogram_tester;
   ui_test_utils::NavigateToURL(browser(), HttpsLitePageURL(kSuccess));
   VerifyPreviewNotLoaded();
@@ -1690,6 +1700,7 @@
   void SetUpCommandLine(base::CommandLine* cmd) override {
     PreviewsLitePageServerBrowserTest::SetUpCommandLine(cmd);
     cmd->AppendSwitch("optimization-guide-disable-installer");
+    cmd->AppendSwitch("purge_hint_cache_store");
   }
 
  private:
@@ -1705,7 +1716,7 @@
 
 IN_PROC_BROWSER_TEST_P(
     PreviewsLitePageAndPageHintsBrowserTest,
-    DISABLE_ON_WIN_MAC(LitePagePreviewsDoesNotOverridePageHints)) {
+    DISABLE_ON_WIN_MAC_CHROMESOS(LitePagePreviewsDoesNotOverridePageHints)) {
   base::HistogramTester histogram_tester;
 
   // Whitelist test URL for resource loading hints.
diff --git a/chrome/browser/previews/resource_loading_hints/resource_loading_hints_browsertest.cc b/chrome/browser/previews/resource_loading_hints/resource_loading_hints_browsertest.cc
index d87116e6..02410f5d 100644
--- a/chrome/browser/previews/resource_loading_hints/resource_loading_hints_browsertest.cc
+++ b/chrome/browser/previews/resource_loading_hints/resource_loading_hints_browsertest.cc
@@ -126,6 +126,9 @@
   void SetUpCommandLine(base::CommandLine* cmd) override {
     cmd->AppendSwitch("enable-spdy-proxy-auth");
 
+    cmd->AppendSwitch("optimization-guide-disable-installer");
+    cmd->AppendSwitch("purge_hint_cache_store");
+
     // Due to race conditions, it's possible that blacklist data is not loaded
     // at the time of first navigation. That may prevent Preview from
     // triggering, and causing the test to flake.
@@ -349,15 +352,15 @@
 // Previews InfoBar (which these tests triggers) does not work on Mac.
 // See https://crbug.com/782322 for details. Also occasional flakes on win7
 // (https://crbug.com/789542).
-#if defined(OS_WIN) || defined(OS_MACOSX)
-#define DISABLE_ON_WIN_MAC(x) DISABLED_##x
+#if defined(OS_WIN) || defined(OS_MACOSX) || defined(OS_CHROMEOS)
+#define DISABLE_ON_WIN_MAC_CHROMESOS(x) DISABLED_##x
 #else
-#define DISABLE_ON_WIN_MAC(x) x
+#define DISABLE_ON_WIN_MAC_CHROMESOS(x) x
 #endif
 
 IN_PROC_BROWSER_TEST_F(
     ResourceLoadingHintsBrowserTest,
-    DISABLE_ON_WIN_MAC(ResourceLoadingHintsHttpsWhitelisted)) {
+    DISABLE_ON_WIN_MAC_CHROMESOS(ResourceLoadingHintsHttpsWhitelisted)) {
   GURL url = https_url();
 
   // Whitelist resource loading hints for https_hint_setup_url()'s' host.
@@ -419,7 +422,7 @@
 // not blocked.
 IN_PROC_BROWSER_TEST_F(
     ResourceLoadingHintsBrowserTest,
-    DISABLE_ON_WIN_MAC(ExperimentalHints_ExperimentIsNotEnabled)) {
+    DISABLE_ON_WIN_MAC_CHROMESOS(ExperimentalHints_ExperimentIsNotEnabled)) {
   GURL url = https_url();
 
   // Whitelist resource loading hints for https_hint_setup_url()'s' host.
@@ -447,7 +450,7 @@
 // Verifies that the hints are used, and the resource loading is blocked.
 IN_PROC_BROWSER_TEST_F(
     ResourceLoadingHintsBrowserTest,
-    DISABLE_ON_WIN_MAC(ExperimentalHints_ExperimentIsEnabled)) {
+    DISABLE_ON_WIN_MAC_CHROMESOS(ExperimentalHints_ExperimentIsEnabled)) {
   base::test::ScopedFeatureList scoped_list;
   scoped_list.InitAndEnableFeatureWithParameters(
       previews::features::kOptimizationHintsExperiments,
@@ -490,7 +493,7 @@
 // blocked.
 IN_PROC_BROWSER_TEST_F(
     ResourceLoadingHintsBrowserTest,
-    DISABLE_ON_WIN_MAC(MixExperimentalHints_ExperimentIsEnabled)) {
+    DISABLE_ON_WIN_MAC_CHROMESOS(MixExperimentalHints_ExperimentIsEnabled)) {
   base::test::ScopedFeatureList scoped_list;
   scoped_list.InitAndEnableFeatureWithParameters(
       previews::features::kOptimizationHintsExperiments,
@@ -534,7 +537,7 @@
 // used.
 IN_PROC_BROWSER_TEST_F(
     ResourceLoadingHintsBrowserTest,
-    DISABLE_ON_WIN_MAC(MixExperimentalHints_ExperimentIsNotEnabled)) {
+    DISABLE_ON_WIN_MAC_CHROMESOS(MixExperimentalHints_ExperimentIsNotEnabled)) {
   base::test::ScopedFeatureList scoped_list;
   scoped_list.InitAndEnableFeatureWithParameters(
       previews::features::kOptimizationHintsExperiments,
@@ -569,7 +572,8 @@
 
 IN_PROC_BROWSER_TEST_F(
     ResourceLoadingHintsBrowserTest,
-    DISABLE_ON_WIN_MAC(ResourceLoadingHintsHttpsWhitelistedRedirectToHttps)) {
+    DISABLE_ON_WIN_MAC_CHROMESOS(
+        ResourceLoadingHintsHttpsWhitelistedRedirectToHttps)) {
   GURL url = redirect_url();
 
   // Whitelist resource loading hints for https_hint_setup_url()'s' host.
@@ -604,7 +608,7 @@
 
 IN_PROC_BROWSER_TEST_F(
     ResourceLoadingHintsBrowserTest,
-    DISABLE_ON_WIN_MAC(ResourceLoadingHintsHttpsNoWhitelisted)) {
+    DISABLE_ON_WIN_MAC_CHROMESOS(ResourceLoadingHintsHttpsNoWhitelisted)) {
   GURL url = https_url();
 
   SetExpectedFooJpgRequest(true);
@@ -630,7 +634,7 @@
 }
 
 IN_PROC_BROWSER_TEST_F(ResourceLoadingHintsBrowserTest,
-                       DISABLE_ON_WIN_MAC(ResourceLoadingHintsHttp)) {
+                       DISABLE_ON_WIN_MAC_CHROMESOS(ResourceLoadingHintsHttp)) {
   GURL url = http_url();
 
   // Whitelist resource loading hints for http_hint_setup_url()'s' host.
@@ -655,9 +659,9 @@
   EXPECT_FALSE(resource_loading_hint_intervention_header_seen());
 }
 
-IN_PROC_BROWSER_TEST_F(
-    ResourceLoadingHintsBrowserTest,
-    DISABLE_ON_WIN_MAC(ResourceLoadingHintsHttpsWhitelistedNoTransform)) {
+IN_PROC_BROWSER_TEST_F(ResourceLoadingHintsBrowserTest,
+                       DISABLE_ON_WIN_MAC_CHROMESOS(
+                           ResourceLoadingHintsHttpsWhitelistedNoTransform)) {
   GURL url = https_no_transform_url();
 
   // Whitelist resource loading hints for http_hint_setup_url()'s' host.
diff --git a/chrome/browser/printing/print_preview_dialog_controller.cc b/chrome/browser/printing/print_preview_dialog_controller.cc
index e54fb51..b2ef67f 100644
--- a/chrome/browser/printing/print_preview_dialog_controller.cc
+++ b/chrome/browser/printing/print_preview_dialog_controller.cc
@@ -57,6 +57,12 @@
 
 namespace {
 
+PrintPreviewUI* GetPrintPreviewUIForDialog(WebContents* dialog) {
+  content::WebUI* web_ui = dialog->GetWebUI();
+  return web_ui ? static_cast<PrintPreviewUI*>(web_ui->GetController())
+                : nullptr;
+}
+
 // A ui::WebDialogDelegate that specifies the print preview dialog appearance.
 class PrintPreviewDialogDelegate : public ui::WebDialogDelegate,
                                    public content::WebContentsObserver {
@@ -85,8 +91,7 @@
 PrintPreviewDialogDelegate::PrintPreviewDialogDelegate(WebContents* initiator)
     : content::WebContentsObserver(initiator) {}
 
-PrintPreviewDialogDelegate::~PrintPreviewDialogDelegate() {
-}
+PrintPreviewDialogDelegate::~PrintPreviewDialogDelegate() = default;
 
 ui::ModalType PrintPreviewDialogDelegate::GetDialogModalType() const {
   // Not used, returning dummy value.
@@ -167,10 +172,7 @@
 
 }  // namespace
 
-PrintPreviewDialogController::PrintPreviewDialogController()
-    : waiting_for_new_preview_page_(false),
-      is_creating_print_preview_dialog_(false) {
-}
+PrintPreviewDialogController::PrintPreviewDialogController() = default;
 
 // static
 PrintPreviewDialogController* PrintPreviewDialogController::GetInstance() {
@@ -260,11 +262,8 @@
 
 void PrintPreviewDialogController::ForEachPreviewDialog(
     base::Callback<void(content::WebContents*)> callback) {
-  for (PrintPreviewDialogMap::const_iterator it = preview_dialog_map_.begin();
-       it != preview_dialog_map_.end();
-       ++it) {
-    callback.Run(it->first);
-  }
+  for (const auto& it : preview_dialog_map_)
+    callback.Run(it.first);
 }
 
 // static
@@ -283,7 +282,7 @@
   preview_dialog_map_[preview_dialog] = nullptr;
 }
 
-PrintPreviewDialogController::~PrintPreviewDialogController() {}
+PrintPreviewDialogController::~PrintPreviewDialogController() = default;
 
 void PrintPreviewDialogController::OnRendererProcessClosed(
     content::RenderProcessHost* rph) {
@@ -291,29 +290,24 @@
   // |preview_dialog_map_| because RemoveFoo() can change |preview_dialog_map_|.
   std::vector<WebContents*> closed_initiators;
   std::vector<WebContents*> closed_preview_dialogs;
-  for (auto iter = preview_dialog_map_.begin();
-       iter != preview_dialog_map_.end(); ++iter) {
-    WebContents* preview_dialog = iter->first;
-    WebContents* initiator = iter->second;
-    if (preview_dialog->GetMainFrame()->GetProcess() == rph) {
+  for (auto& it : preview_dialog_map_) {
+    WebContents* preview_dialog = it.first;
+    WebContents* initiator = it.second;
+    if (preview_dialog->GetMainFrame()->GetProcess() == rph)
       closed_preview_dialogs.push_back(preview_dialog);
-    } else if (initiator && initiator->GetMainFrame()->GetProcess() == rph) {
+    else if (initiator && initiator->GetMainFrame()->GetProcess() == rph)
       closed_initiators.push_back(initiator);
-    }
   }
 
-  for (size_t i = 0; i < closed_preview_dialogs.size(); ++i) {
-    RemovePreviewDialog(closed_preview_dialogs[i]);
-    if (content::WebUI* web_ui = closed_preview_dialogs[i]->GetWebUI()) {
-      PrintPreviewUI* print_preview_ui =
-          static_cast<PrintPreviewUI*>(web_ui->GetController());
-      if (print_preview_ui)
-        print_preview_ui->OnPrintPreviewDialogClosed();
-    }
+  for (WebContents* dialog : closed_preview_dialogs) {
+    RemovePreviewDialog(dialog);
+    auto* print_preview_ui = GetPrintPreviewUIForDialog(dialog);
+    if (print_preview_ui)
+      print_preview_ui->OnPrintPreviewDialogClosed();
   }
 
-  for (size_t i = 0; i < closed_initiators.size(); ++i)
-    RemoveInitiator(closed_initiators[i]);
+  for (WebContents* initiator : closed_initiators)
+    RemoveInitiator(initiator);
 }
 
 void PrintPreviewDialogController::OnWebContentsDestroyed(
@@ -430,12 +424,15 @@
 void PrintPreviewDialogController::SaveInitiatorTitle(
     WebContents* preview_dialog) {
   WebContents* initiator = GetInitiator(preview_dialog);
-  if (initiator && preview_dialog->GetWebUI()) {
-    PrintPreviewUI* print_preview_ui = static_cast<PrintPreviewUI*>(
-        preview_dialog->GetWebUI()->GetController());
-    print_preview_ui->SetInitiatorTitle(
-        PrintViewManager::FromWebContents(initiator)->RenderSourceName());
-  }
+  if (!initiator)
+    return;
+
+  auto* print_preview_ui = GetPrintPreviewUIForDialog(preview_dialog);
+  if (!print_preview_ui)
+    return;
+
+  print_preview_ui->SetInitiatorTitle(
+      PrintViewManager::FromWebContents(initiator)->RenderSourceName());
 }
 
 void PrintPreviewDialogController::AddObservers(WebContents* contents) {
@@ -501,13 +498,10 @@
 
   PrintViewManager::FromWebContents(initiator)->PrintPreviewDone();
 
-  // initiator is closed. Close the print preview dialog too.
-  if (content::WebUI* web_ui = preview_dialog->GetWebUI()) {
-    PrintPreviewUI* print_preview_ui =
-        static_cast<PrintPreviewUI*>(web_ui->GetController());
-    if (print_preview_ui)
-      print_preview_ui->OnInitiatorClosed();
-  }
+  // Initiator is closed. Close the print preview dialog too.
+  auto* print_preview_ui = GetPrintPreviewUIForDialog(preview_dialog);
+  if (print_preview_ui)
+    print_preview_ui->OnInitiatorClosed();
 }
 
 void PrintPreviewDialogController::RemovePreviewDialog(
diff --git a/chrome/browser/printing/print_preview_dialog_controller.h b/chrome/browser/printing/print_preview_dialog_controller.h
index a2cdbd1..fefafc870 100644
--- a/chrome/browser/printing/print_preview_dialog_controller.h
+++ b/chrome/browser/printing/print_preview_dialog_controller.h
@@ -130,11 +130,11 @@
 
   // True if the controller is waiting for a new preview dialog via
   // content::NAVIGATION_TYPE_NEW_PAGE.
-  bool waiting_for_new_preview_page_;
+  bool waiting_for_new_preview_page_ = false;
 
   // Whether the PrintPreviewDialogController is in the middle of creating a
   // print preview dialog.
-  bool is_creating_print_preview_dialog_;
+  bool is_creating_print_preview_dialog_ = false;
 
   // How many web contents (dialogs and initiators) are watching a given render
   // process host. Used to determine when a render process host's
diff --git a/chrome/browser/profile_resetter/profile_resetter_unittest.cc b/chrome/browser/profile_resetter/profile_resetter_unittest.cc
index a76350ec..d0c5971 100644
--- a/chrome/browser/profile_resetter/profile_resetter_unittest.cc
+++ b/chrome/browser/profile_resetter/profile_resetter_unittest.cc
@@ -6,6 +6,7 @@
 
 #include <stddef.h>
 
+#include <functional>
 #include <map>
 #include <memory>
 #include <string>
@@ -971,9 +972,8 @@
   EXPECT_CALL(capture, OnUpdatedList());
   ResettableSettingsSnapshot snapshot(profile());
   snapshot.RequestShortcuts(base::Bind(&FeedbackCapture::SetFeedback,
-                                       base::Unretained(&capture),
-                                       profile(),
-                                       base::ConstRef(snapshot)));
+                                       base::Unretained(&capture), profile(),
+                                       std::cref(snapshot)));
   // Let it enumerate shortcuts on a blockable task runner.
   content::RunAllTasksUntilIdle();
   EXPECT_TRUE(snapshot.shortcuts_determined());
diff --git a/chrome/browser/profiles/off_the_record_profile_impl.cc b/chrome/browser/profiles/off_the_record_profile_impl.cc
index 198a919..dda0836 100644
--- a/chrome/browser/profiles/off_the_record_profile_impl.cc
+++ b/chrome/browser/profiles/off_the_record_profile_impl.cc
@@ -15,6 +15,7 @@
 #include "base/strings/string_util.h"
 #include "base/task/post_task.h"
 #include "build/build_config.h"
+#include "chrome/browser/accessibility/accessibility_labels_service.h"
 #include "chrome/browser/background/background_contents_service_factory.h"
 #include "chrome/browser/background_fetch/background_fetch_delegate_factory.h"
 #include "chrome/browser/background_fetch/background_fetch_delegate_impl.h"
@@ -201,6 +202,9 @@
   // The DomDistillerViewerSource is not a normal WebUI so it must be registered
   // as a URLDataSource early.
   dom_distiller::RegisterViewerSource(this);
+
+  // AccessibilityLabelsService has a default prefs behavior in incognito.
+  AccessibilityLabelsService::InitOffTheRecordPrefs(this);
 }
 
 OffTheRecordProfileImpl::~OffTheRecordProfileImpl() {
diff --git a/chrome/browser/profiles/profile_attributes_storage_unittest.cc b/chrome/browser/profiles/profile_attributes_storage_unittest.cc
index 5079efb..39a598d1 100644
--- a/chrome/browser/profiles/profile_attributes_storage_unittest.cc
+++ b/chrome/browser/profiles/profile_attributes_storage_unittest.cc
@@ -590,7 +590,8 @@
   EXPECT_EQ(base::ASCIIToUTF16("OtherNewName"), first_entry->GetName());
 }
 
-TEST_F(ProfileAttributesStorageTest, ChooseAvatarIconIndexForNewProfile) {
+TEST_F(ProfileAttributesStorageTest,
+       DISABLED_ChooseAvatarIconIndexForNewProfile) {
   size_t total_icon_count = profiles::GetDefaultAvatarIconCount();
 #if defined(OS_CHROMEOS) || defined(OS_ANDROID)
   size_t generic_icon_count = profiles::GetGenericAvatarIconCount();
diff --git a/chrome/browser/resources/app_management/BUILD.gn b/chrome/browser/resources/app_management/BUILD.gn
index 98fe769..03e42ffd 100644
--- a/chrome/browser/resources/app_management/BUILD.gn
+++ b/chrome/browser/resources/app_management/BUILD.gn
@@ -116,7 +116,9 @@
   js_library("expandable_app_list") {
     deps = [
       ":app_item",
+      ":constants",
       ":store_client",
+      "//third_party/polymer/v1_0/components-chromium/iron-collapse:iron-collapse-extracted",
     ]
   }
 
diff --git a/chrome/browser/resources/app_management/browser_proxy.html b/chrome/browser/resources/app_management/browser_proxy.html
index aef2d819..018e777 100644
--- a/chrome/browser/resources/app_management/browser_proxy.html
+++ b/chrome/browser/resources/app_management/browser_proxy.html
@@ -1,7 +1,7 @@
 <link rel="import" href="chrome://resources/html/cr.html">
 <script src="chrome://resources/js/mojo_bindings_lite.js"></script>
+<script src="chrome://resources/js/big_buffer.mojom-lite.js"></script>
 <script src="chrome://resources/js/time.mojom-lite.js"></script>
-<script src="big_buffer.mojom-lite.js"></script>
 <script src="image_info.mojom-lite.js"></script>
 <script src="bitmap.mojom-lite.js"></script>
 <script src="image.mojom-lite.js"></script>
diff --git a/chrome/browser/resources/app_management/expandable_app_list.html b/chrome/browser/resources/app_management/expandable_app_list.html
index d1add13a..57e3e2e 100644
--- a/chrome/browser/resources/app_management/expandable_app_list.html
+++ b/chrome/browser/resources/app_management/expandable_app_list.html
@@ -1,6 +1,7 @@
 <link rel="import" href="chrome://resources/html/polymer.html">
 
 <link rel="import" href="app_item.html">
+<link rel="import" href="constants.html">
 <link rel="import" href="shared_style.html">
 <link rel="import" href="store_client.html">
 <link rel="import" href="permission_toggle.html">
@@ -10,68 +11,25 @@
 <dom-module id="app-management-expandable-app-list">
   <template>
     <style include="app-management-shared-css">
-      .app-management-item-arrow {
-        margin-inline-end: 8px;
-        padding: 12px;
-      }
-
       #app-list-title {
         padding: 16px 24px;
       }
 
-      app-management-permission-toggle {
-        margin-inline-end: 24px;
+      #collapse {
+        display: block;
+        min-height: var(--collapsed-height);
+        overflow: hidden;
       }
     </style>
-    <!-- TODO(ceciliani) Avoid using dom-if, and use slot by getting |items|
-    from dom-repeat -->
-    <!-- TODO(calamity) Make a more generic polymer element for expandable
-    list. -->
     <div class="card-container">
       <div id="app-list-title" class="header-text">[[listTitle]]</div>
-      <template is="dom-repeat" items="[[displayedApps]]">
-        <template is="dom-if" if="[[!notificationsViewSelected_()]]">
-          <app-management-app-item app="[[item]]">
-            <paper-icon-button-light slot="right-content"
-                class="subpage-arrow app-management-item-arrow" actionable>
-              <button></button>
-            </paper-icon-button-light>
-          </app-management-app-item>
-        </template>
-        <template is="dom-if" if="[[notificationsViewSelected_()]]">
-          <app-management-app-item app="[[item]]">
-            <app-management-permission-toggle slot="right-content"
-                app="[[item]]"
-                permission-type="CONTENT_SETTINGS_TYPE_NOTIFICATIONS">
-            </app-management-permission-toggle>
-          </app-management-app-item>
-        </template>
-      </template>
-
-      <iron-collapse opened="[[listExpanded_]]">
-        <template is="dom-repeat" items="[[collapsedApps]]">
-          <template is="dom-if" if="[[!notificationsViewSelected_()]]">
-            <app-management-app-item app="[[item]]">
-              <paper-icon-button-light slot="right-content"
-                  class="subpage-arrow app-management-item-arrow" actionable>
-                <button></button>
-              </paper-icon-button-light>
-            </app-management-app-item>
-          </template>
-          <template is="dom-if" if="[[notificationsViewSelected_()]]">
-            <app-management-app-item app="[[item]]">
-              <app-management-permission-toggle slot="right-content"
-                  app="[[item]]"
-                  permission-type="CONTENT_SETTINGS_TYPE_NOTIFICATIONS">
-              </app-management-permission-toggle>
-            </app-management-app-item>
-          </template>
-        </template>
+      <iron-collapse id="collapse">
+        <slot></slot>
       </iron-collapse>
 
       <div id="expander-row" class="expander-list-row"
           on-click="toggleListExpanded_">
-        <span>[[moreAppsString_(collapsedApps.length,listExpanded_)]]</span>
+        <span>[[moreAppsString_(apps.length, listExpanded_)]]</span>
         <paper-icon-button-light class="expand-button">
           <button>
             <iron-icon icon="[[getCollapsedIcon_(listExpanded_)]]">
diff --git a/chrome/browser/resources/app_management/expandable_app_list.js b/chrome/browser/resources/app_management/expandable_app_list.js
index b4dc9aa..787074d 100644
--- a/chrome/browser/resources/app_management/expandable_app_list.js
+++ b/chrome/browser/resources/app_management/expandable_app_list.js
@@ -2,79 +2,112 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+/**
+ * This is an expanding container for a list of apps that shows some items by
+ * default and can be expanded to show more.
+ *
+ * Note: The implementation assumes children are all the same height.
+ *
+ * Example usage:
+ *  <app-management-expandable-app-list apps="[[appsList]]">
+ *    <template is="dom-repeat" items="[[appsList]]" as="app" notify-dom-change>
+ *      <app-management-app-item app="[[app]]"></app-management-app-item>
+ *    </template>
+ *  </app-management-expandable-app-list>
+ */
 Polymer({
   is: 'app-management-expandable-app-list',
 
-  behaviors: [
-    app_management.StoreClient,
-  ],
-
   properties: {
     /**
-     * @private {Page}
-     */
-    currentPage_: {
-      type: Object,
-      observer: 'onViewChanged_',
-    },
-
-    /**
-     * List of apps displayed before expanding the app list.
-     * @type {Array<App>}
-     */
-    displayedApps: Array,
-
-    /**
      * Title of the expandable list.
      * @type {String}
      */
-    listTitle: String,
-
-    /**
-     * List of apps displayed after expanding app list.
-     * @type {Array<App>}
-     */
-    collapsedApps: {
-      type: Array,
-      observer: 'onAppsChanged_',
+    listTitle: {
+      type: String,
+      value: '',
+      observer: 'onListTitleChanged_',
     },
 
-    /**
-     * @private {boolean}
-     */
-    listExpanded_: Boolean,
+    /** The number of apps to collapse down to. */
+    collapsedSize: {
+      type: Number,
+      value: NUMBER_OF_APPS_DISPLAYED_DEFAULT,
+    },
+
+    /** @private {boolean} */
+    listExpanded_: {
+      type: Boolean,
+      observer: 'onListExpandedChanged_',
+    },
+  },
+
+  listeners: {
+    'dom-change': 'onDomChange_',
   },
 
   attached: function() {
-    this.watch('currentPage_', state => state.currentPage);
-
-    this.updateFromStore();
-  },
-
-  /**
-   * @private
-   */
-  onAppsChanged_: function() {
-    this.$['expander-row'].hidden = this.collapsedApps.length === 0;
-  },
-
-  /**
-   * Collapse the list when changing a page if it is open so that list is always
-   * collapsed when entering the page.
-   * @private
-   */
-  onViewChanged_: function() {
-    this.$['app-list-title'].hidden = !this.listTitle;
+    // Hide on reattach.
     this.listExpanded_ = false;
+    this.$.collapse.hide();
+
+    // Recalculate child heights on reattach.
+    this.onDomChange_();
   },
 
-  /**
-   * @private
-   */
+  /** @private */
+  onAppsChanged_: function(change) {},
+
+  /** @private */
+  onListTitleChanged_() {
+    this.$['app-list-title'].hidden = !this.listTitle;
+  },
+
+  /** @private */
+  onDomChange_: function() {
+    let collapsedHeight = 0;
+    let numChildren = 0;
+    for (const child of this.$.collapse.getContentChildren()) {
+      // Wait until we have an actual child element rather than just the
+      // dom-repeat.
+      if (child.tagName == 'DOM-REPEAT' || child.tagName == 'TEMPLATE') {
+        continue;
+      }
+
+      if (numChildren < this.collapsedSize) {
+        collapsedHeight += child.offsetHeight;
+      }
+
+      numChildren++;
+    }
+
+    this.style.setProperty(
+        '--collapsed-height', String(collapsedHeight) + 'px');
+    this.$['expander-row'].hidden = numChildren <= this.collapsedSize;
+  },
+
+  /** @private */
   toggleListExpanded_: function() {
     this.listExpanded_ = !this.listExpanded_;
   },
 
+  /** @private */
+  onListExpandedChanged_() {
+    // TODO(calamity): Hiding should display:none after the animation to prevent
+    // tabbing into hidden items.
+    const collapse = this.$.collapse;
+    // Since iron-collapse does not support a 'min-height' property, we force it
+    // to animate to the collapsed height.
+    if (this.listExpanded_) {
+      // Reset the opened state, or show won't work.
+      collapse.hide();
+      collapse.show();
+    } else {
+      // This technically leaves the collapse open.
+      collapse.updateSize('var(--collapsed-height)', true);
+    }
+  },
+
   /**
    * @param {boolean} listExpanded
    * @return {string}
@@ -91,11 +124,8 @@
    * @private
    */
   moreAppsString_: function(numApps, listExpanded) {
-    return listExpanded ? loadTimeData.getString('lessApps') :
-                          loadTimeData.getStringF('moreApps', numApps);
-  },
-
-  notificationsViewSelected_: function() {
-    return this.currentPage_.pageType === PageType.NOTIFICATIONS;
+    return listExpanded ?
+        loadTimeData.getString('lessApps') :
+        loadTimeData.getStringF('moreApps', numApps - this.collapsedSize);
   },
 });
diff --git a/chrome/browser/resources/app_management/fake_page_handler.js b/chrome/browser/resources/app_management/fake_page_handler.js
index 2481294..115aa6f7 100644
--- a/chrome/browser/resources/app_management/fake_page_handler.js
+++ b/chrome/browser/resources/app_management/fake_page_handler.js
@@ -107,6 +107,9 @@
           await this.page.$.flushForTesting();
         }
       };
+
+      /** @type {number} */
+      this.guid = 0;
     }
 
     async getApps() {
@@ -170,12 +173,16 @@
     openNativeSettings(appId) {}
 
     /**
-     * @param {string} id
+     * @param {string} optId
      * @param {Object=} optConfig
+     * @return {!Promise<!App>}
      */
-    async addApp(id, optConfig) {
-      this.page.onAppAdded(FakePageHandler.createApp(id, optConfig));
+    async addApp(optId, optConfig) {
+      optId = optId || String(this.guid++);
+      const app = FakePageHandler.createApp(optId, optConfig);
+      this.page.onAppAdded(app);
       await this.$.flushForTesting();
+      return app;
     }
 
     /**
diff --git a/chrome/browser/resources/app_management/main_view.html b/chrome/browser/resources/app_management/main_view.html
index 0492fb0..2b00ca8 100644
--- a/chrome/browser/resources/app_management/main_view.html
+++ b/chrome/browser/resources/app_management/main_view.html
@@ -38,11 +38,23 @@
         justify-content: space-between;
         padding: 0 24px;
       }
+
+      .app-management-item-arrow {
+        margin-inline-end: 8px;
+        padding: 12px;
+      }
     </style>
     <app-management-expandable-app-list
-        displayed-apps="[[displayedApps_]]"
-        collapsed-apps="[[collapsedApps_]]"
+        apps="[[appsList]]"
         list-title="$i18n{appListTitle}">
+      <template is="dom-repeat" items="[[appsList]]" as="app" notify-dom-change>
+        <app-management-app-item app="[[app]]">
+          <paper-icon-button-light slot="right-content"
+              class="subpage-arrow app-management-item-arrow" actionable>
+            <button></button>
+          </paper-icon-button-light>
+        </app-management-app-item>
+      </template>
     </app-management-expandable-app-list>
 
     <div class="card-container">
diff --git a/chrome/browser/resources/app_management/main_view.js b/chrome/browser/resources/app_management/main_view.js
index 82b87487..c2a2811 100644
--- a/chrome/browser/resources/app_management/main_view.js
+++ b/chrome/browser/resources/app_management/main_view.js
@@ -22,16 +22,7 @@
      * List of apps displayed before expanding the app list.
      * @private {Array<App>}
      */
-    displayedApps_: {
-      type: Array,
-      value: () => [],
-    },
-
-    /**
-     * List of apps displayed after expanding app list.
-     * @private {Array<App>}
-     */
-    collapsedApps_: {
+    appsList: {
       type: Array,
       value: () => [],
     },
@@ -56,10 +47,7 @@
    * @private
    */
   onAppsChanged_: function() {
-    const appList = Object.values(this.apps_);
-    this.displayedApps_ = appList.slice(0, NUMBER_OF_APPS_DISPLAYED_DEFAULT);
-    this.collapsedApps_ =
-        appList.slice(NUMBER_OF_APPS_DISPLAYED_DEFAULT, appList.length);
+    this.appsList = Object.values(this.apps_);
   },
 
   /** @private */
diff --git a/chrome/browser/resources/app_management/notifications_view.html b/chrome/browser/resources/app_management/notifications_view.html
index d3f8d96..bc2f84d8 100644
--- a/chrome/browser/resources/app_management/notifications_view.html
+++ b/chrome/browser/resources/app_management/notifications_view.html
@@ -12,6 +12,10 @@
         margin-inline-start: 0;
       }
 
+      app-management-permission-toggle {
+        margin-inline-end: 24px;
+      }
+
       #notification-view-header {
         align-items: center;
         display: flex;
@@ -35,8 +39,17 @@
       <div id="notification-title" class="page-title">$i18n{notifications}</div>
     </div>
     <app-management-expandable-app-list
-        displayed-apps="[[displayedApps_]]"
-        collapsed-apps="[[collapsedApps_]]">
+        apps="[[appsList_]]"
+        collapsed-size="[[getCollapsedSize_(appsList_)]]">
+      <template is="dom-repeat" items="[[appsList_]]"
+                as="app" notify-dom-change>
+        <app-management-app-item app="[[app]]">
+          <app-management-permission-toggle slot="right-content"
+              app="[[app]]"
+              permission-type="CONTENT_SETTINGS_TYPE_NOTIFICATIONS">
+          </app-management-permission-toggle>
+        </app-management-app-item>
+      </template>
     </app-management-expandable-app-list>
   </template>
  <script src="notifications_view.js"></script>
diff --git a/chrome/browser/resources/app_management/notifications_view.js b/chrome/browser/resources/app_management/notifications_view.js
index 2252b4c0..aae8cff 100644
--- a/chrome/browser/resources/app_management/notifications_view.js
+++ b/chrome/browser/resources/app_management/notifications_view.js
@@ -18,12 +18,18 @@
       observer: 'onAppsChanged_',
     },
 
+    /** @private {!Array<!App>} */
+    appsList_: {
+      type: Array,
+      computed: 'calculateAppsList_(allowed_.*, blocked_.*)',
+    },
+
     /**
      * List of apps with notification permission
      * displayed before expanding the app list.
      * @private {!Array<App>}
      */
-    displayedApps_: {
+    allowed_: {
       type: Array,
       value: () => [],
     },
@@ -33,18 +39,10 @@
      * displayed after expanding app list.
      * @private {!Array<App>}
      */
-    collapsedApps_: {
+    blocked_: {
       type: Array,
       value: () => [],
     },
-
-    /**
-     * @private {boolean}
-     */
-    listExpanded_: {
-      type: Boolean,
-      value: false,
-    },
   },
 
   attached: function() {
@@ -66,15 +64,10 @@
    */
   onViewLoaded_: function() {
     const state = this.getState();
-    this.displayedApps_ =
+    this.allowed_ =
         Array.from(state.notifications.allowedIds, id => state.apps[id]);
-    this.collapsedApps_ =
+    this.blocked_ =
         Array.from(state.notifications.blockedIds, id => state.apps[id]);
-
-    if (this.displayedApps_.length === 0) {
-      this.displayedApps_ = this.collapsedApps_;
-      this.collapsedApps_ = [];
-    }
   },
 
   /**
@@ -85,10 +78,8 @@
    */
   onAppsChanged_() {
     const unhandledAppIds = new Set(Object.keys(this.apps_));
-    this.displayedApps_ =
-        this.updateAppList_(this.displayedApps_, unhandledAppIds);
-    this.collapsedApps_ =
-        this.updateAppList_(this.collapsedApps_, unhandledAppIds);
+    this.allowed_ = this.updateAppList_(this.allowed_, unhandledAppIds);
+    this.blocked_ = this.updateAppList_(this.blocked_, unhandledAppIds);
 
     // If any new apps have been added, append them to the appropriate list.
     for (const appId of unhandledAppIds) {
@@ -100,14 +91,30 @@
       }
 
       if (allowed === OptionalBool.kTrue) {
-        this.displayedApps_.push(app);
+        this.push('allowed_', app);
       } else {
-        this.collapsedApps_.push(app);
+        this.push('blocked_', app);
       }
     }
   },
 
   /**
+   * @private
+   * @return {!Array<!App>}
+   */
+  calculateAppsList_() {
+    return this.allowed_.concat(this.blocked_);
+  },
+
+  /**
+   * @private
+   * @return {number}
+   */
+  getCollapsedSize_() {
+    return this.allowed_.length || this.blocked_.length;
+  },
+
+  /**
    * Creates a new list of apps with the same order as the original appList,
    * but using the updated apps from this.apps_. As each app is added to the
    * new list, it is also removed from the unhandledAppIds set.
@@ -129,7 +136,6 @@
 
   /** @private */
   onClickBackButton_: function() {
-    this.listExpanded_ = false;
     if (!window.history.state) {
       this.dispatch(app_management.actions.changePage(PageType.MAIN));
     } else {
diff --git a/chrome/browser/resources/app_management/permission_toggle.js b/chrome/browser/resources/app_management/permission_toggle.js
index b0099c3d..a8908cb 100644
--- a/chrome/browser/resources/app_management/permission_toggle.js
+++ b/chrome/browser/resources/app_management/permission_toggle.js
@@ -66,7 +66,6 @@
    * @private
    */
   getNewPermissionBoolean_: function(app, permissionType) {
-    /** @type {number} */
     let newPermissionValue;
 
     switch (app_management.util.getPermission(app, permissionType).value) {
@@ -80,9 +79,10 @@
         assertNotReached();
     }
 
+    assert(newPermissionValue !== undefined);
     return app_management.util.createPermission(
         app_management.util.permissionTypeHandle(app, permissionType),
-        PermissionValueType.kBool, assert(newPermissionValue));
+        PermissionValueType.kBool, newPermissionValue);
   },
 
   /**
@@ -112,8 +112,9 @@
         assertNotReached();
     }
 
+    assert(newPermissionValue !== undefined);
     return app_management.util.createPermission(
         app_management.util.permissionTypeHandle(app, permissionType),
-        PermissionValueType.kTriState, assert(newPermissionValue));
+        PermissionValueType.kTriState, newPermissionValue);
   },
 });
diff --git a/chrome/browser/resources/bookmarks/store.js b/chrome/browser/resources/bookmarks/store.js
index 3333c07..b1fb6a8 100644
--- a/chrome/browser/resources/bookmarks/store.js
+++ b/chrome/browser/resources/bookmarks/store.js
@@ -9,8 +9,8 @@
  */
 
 cr.define('bookmarks', function() {
+  /** @extends {cr.ui.Store<BookmarksPageState>} */
   class Store extends cr.ui.Store {
-    /** @extends {cr.ui.Store<BookmarksPageState>} */
     constructor() {
       super(bookmarks.util.createEmptyState(), bookmarks.reduceAction);
     }
diff --git a/chrome/browser/resources/component_extension_resources.grd b/chrome/browser/resources/component_extension_resources.grd
index b6054b8..110e3307 100644
--- a/chrome/browser/resources/component_extension_resources.grd
+++ b/chrome/browser/resources/component_extension_resources.grd
@@ -131,6 +131,8 @@
           <include name="IDR_PDF_VIEWER_INK_HOST_JS" file="pdf/elements/viewer-ink-host/viewer-ink-host.js" type="BINDATA" />
           <include name="IDR_PDF_VIEWER_PEN_OPTIONS_HTML" file="pdf/elements/viewer-pen-options/viewer-pen-options.html" type="BINDATA" />
           <include name="IDR_PDF_VIEWER_PEN_OPTIONS_JS" file="pdf/elements/viewer-pen-options/viewer-pen-options.js" type="BINDATA" />
+          <include name="IDR_PDF_VIEWER_FORM_WARNING_HTML" file="pdf/elements/viewer-form-warning/viewer-form-warning.html" type="BINDATA" />
+          <include name="IDR_PDF_VIEWER_FORM_WARNING_JS" file="pdf/elements/viewer-form-warning/viewer-form-warning.js" type="BINDATA" />
         </if>
         <include name="IDR_PDF_VIEWER_PAGE_INDICATOR_HTML" file="pdf/elements/viewer-page-indicator/viewer-page-indicator.html" type="BINDATA" />
         <include name="IDR_PDF_VIEWER_PAGE_INDICATOR_JS" file="pdf/elements/viewer-page-indicator/viewer-page-indicator.js" type="BINDATA" flattenhtml="true" />
diff --git a/chrome/browser/resources/local_ntp/animations.css b/chrome/browser/resources/local_ntp/animations.css
index 107e358..cb2efa4 100644
--- a/chrome/browser/resources/local_ntp/animations.css
+++ b/chrome/browser/resources/local_ntp/animations.css
@@ -47,7 +47,6 @@
 button.paper {
   border: none;
   border-radius: 4px;
-  font-family: 'Roboto', arial, sans-serif;
   font-size: 12px;
   font-weight: 500;
   height: 32px;
diff --git a/chrome/browser/resources/local_ntp/custom_backgrounds.css b/chrome/browser/resources/local_ntp/custom_backgrounds.css
index 4f26b91..3d52ab6 100644
--- a/chrome/browser/resources/local_ntp/custom_backgrounds.css
+++ b/chrome/browser/resources/local_ntp/custom_backgrounds.css
@@ -59,6 +59,10 @@
   right: auto;
 }
 
+.non-google-page #edit-bg {
+  display: none;
+}
+
 #edit-bg-icon {
   -webkit-mask-image: url(icons/icon_pencil.svg);
   -webkit-mask-position-x: center;
@@ -133,7 +137,6 @@
 
 #edit-bg-title {
   color: rgb(var(--GG900-rgb));
-  font-family: 'Roboto', arial, sans-serif;
   font-size: 15px;
   height: 30px;
   padding: 16px 0 0 16px;
@@ -153,7 +156,6 @@
 
 .bg-option {
   color: rgb(var(--GG800-rgb));
-  font-family: 'Roboto', arial, sans-serif;
   font-size: 13px;
   height: 40px;
   line-height: 40px;
@@ -251,7 +253,6 @@
   bottom: 0;
   box-shadow: 0 1px 3px 0 rgba(var(--GG800-rgb), 0.3),
       0 4px 8px 3px rgba(var(--GG800-rgb), 0.15);
-  font-family: 'Roboto', arial, sans-serif;
   height: 400px;
   padding: 0 0 0 0;
   position: fixed;
@@ -635,7 +636,6 @@
   border-radius: 8px;
   bottom: 16px;
   color: white;
-  font-family: 'Roboto', arial, sans-serif;
   font-weight: 500;
   left: 16px;
   padding: 8px 8px 8px 8px;
diff --git a/chrome/browser/resources/local_ntp/custom_links_edit.css b/chrome/browser/resources/local_ntp/custom_links_edit.css
index 8a27be6..d4c2cad 100644
--- a/chrome/browser/resources/local_ntp/custom_links_edit.css
+++ b/chrome/browser/resources/local_ntp/custom_links_edit.css
@@ -17,7 +17,6 @@
   bottom: 0;
   box-shadow: 0 1px 3px 0 rgba(var(--GG800-rgb), 0.3),
       0 4px 8px 3px rgba(var(--GG800-rgb), 0.15);
-  font-family: 'Roboto', arial, sans-serif;
   margin: auto;
   min-width: 320px;
   padding: 16px;
@@ -41,7 +40,6 @@
 
 #dialog-title {
   color: rgb(var(--GG900-rgb));
-  font-family: 'Roboto', arial, sans-serif;
   font-size: 15px;
   line-height: 24px;
   margin-bottom: 16px;
@@ -76,7 +74,6 @@
   border-radius: 4px;
   caret-color: rgb(var(--GB600-rgb));
   color: rgb(var(--GG900-rgb));
-  font-family: 'Roboto', arial, sans-serif;
   font-size: 13px;
   height: 32px;
   line-height: 24px;
diff --git a/chrome/browser/resources/local_ntp/custom_links_edit.html b/chrome/browser/resources/local_ntp/custom_links_edit.html
index 724ad97..a9022a1 100644
--- a/chrome/browser/resources/local_ntp/custom_links_edit.html
+++ b/chrome/browser/resources/local_ntp/custom_links_edit.html
@@ -7,7 +7,7 @@
   <base target="_top">
   <meta charset="utf-8">
   <link rel="stylesheet" type="text/css" href="animations.css">
-  <link rel="stylesheet" type="text/css" href="constants.css">
+  <link rel="stylesheet" type="text/css" href="local-ntp-common.css">
   <link rel="stylesheet" type="text/css" href="edit.css">
   <script src="utils.js"></script>
   <script src="animations.js"></script>
diff --git a/chrome/browser/resources/local_ntp/custom_links_edit.js b/chrome/browser/resources/local_ntp/custom_links_edit.js
index d512f33..541b7ce3 100644
--- a/chrome/browser/resources/local_ntp/custom_links_edit.js
+++ b/chrome/browser/resources/local_ntp/custom_links_edit.js
@@ -334,6 +334,8 @@
   $(IDS.URL_FIELD).addEventListener('input',
       () => $(IDS.DONE).disabled = ($(IDS.URL_FIELD).value.trim() === ''));
 
+  utils.setPlatformClass(document.body);
+
   $(IDS.EDIT_DIALOG).showModal();
 
   window.addEventListener('message', handlePostMessage);
diff --git a/chrome/browser/resources/local_ntp/local_ntp.css b/chrome/browser/resources/local_ntp/local_ntp.css
index 61a9af7..b448fe3 100644
--- a/chrome/browser/resources/local_ntp/local_ntp.css
+++ b/chrome/browser/resources/local_ntp/local_ntp.css
@@ -65,13 +65,10 @@
   }
 }
 
-/* TODO: Need to discuss with NTP folks before we remove font-family from the
- * body tag. */
 body {
   background-attachment: fixed !important;
   cursor: default;
   display: none;
-  font-family: arial, sans-serif;
   font-size: small;
   margin: 0;
   min-height: 100%;
@@ -183,7 +180,6 @@
 #fakebox-text {
   bottom: 4px;
   color: rgb(var(--GG600-rgb));
-  font-family: 'Roboto', arial, sans-serif;
   font-size: 14px;
   left: 0;
   margin-top: 1px;
@@ -395,7 +391,6 @@
   border: 1px solid rgb(var(--GG300-rgb));
   /* Necessary for a "pill" shape. Using 50% creates an oval. */
   border-radius: 16px;
-  font-family: 'Roboto', arial, sans-serif;
   font-weight: normal;
   height: fit-content;
   margin: 0 auto;
@@ -650,7 +645,6 @@
   border-radius: 16px;
   color: rgb(var(--GR600-rgb));
   display: flex;
-  font-family: 'Roboto', arial, sans-serif;
   font-size: 12px;
   font-weight: normal;
   height: fit-content;
@@ -756,7 +750,6 @@
   box-sizing: border-box;
   color: rgb(var(--GG700-rgb));
   display: inline-block;
-  font-family: 'Roboto', arial, sans-serif;
   font-size: 12px;
   line-height: 32px;
   margin-bottom: 0;
diff --git a/chrome/browser/resources/local_ntp/local_ntp.html b/chrome/browser/resources/local_ntp/local_ntp.html
index d5ab8c9..3e6252b 100644
--- a/chrome/browser/resources/local_ntp/local_ntp.html
+++ b/chrome/browser/resources/local_ntp/local_ntp.html
@@ -5,7 +5,7 @@
      found in the LICENSE file. -->
 <head>
   <link rel="stylesheet" href="chrome-search://local-ntp/animations.css"></link>
-  <link rel="stylesheet" href="chrome-search://local-ntp/constants.css"></link>
+  <link rel="stylesheet" href="chrome-search://local-ntp/local-ntp-common.css"></link>
   <link rel="stylesheet" href="chrome-search://local-ntp/custom-backgrounds.css"></link>
   <link rel="stylesheet" href="chrome-search://local-ntp/doodles.css"></link>
   <link rel="stylesheet" href="chrome-search://local-ntp/local-ntp.css"></link>
diff --git a/chrome/browser/resources/local_ntp/local_ntp.js b/chrome/browser/resources/local_ntp/local_ntp.js
index 5918581e..d852377f 100644
--- a/chrome/browser/resources/local_ntp/local_ntp.js
+++ b/chrome/browser/resources/local_ntp/local_ntp.js
@@ -97,7 +97,7 @@
   NON_GOOGLE_PAGE: 'non-google-page',
   NON_WHITE_BG: 'non-white-bg',
   REMOVE_FAKEBOX: 'remove-fakebox',  // Hides the fakebox from the page.
-  RTL: 'rtl',  // Right-to-left language text.
+  RTL: 'rtl',                        // Right-to-left language text.
   // Applied when the doodle notifier should be shown instead of the doodle.
   USE_NOTIFIER: 'use-notifier',
 };
@@ -1210,6 +1210,8 @@
     createIframes();
   }
 
+  utils.setPlatformClass(document.body);
+
   document.body.classList.add(CLASSES.INITED);
 }
 
diff --git a/chrome/browser/resources/local_ntp/constants.css b/chrome/browser/resources/local_ntp/local_ntp_common.css
similarity index 80%
rename from chrome/browser/resources/local_ntp/constants.css
rename to chrome/browser/resources/local_ntp/local_ntp_common.css
index 72e81b87..7c5d144b 100644
--- a/chrome/browser/resources/local_ntp/constants.css
+++ b/chrome/browser/resources/local_ntp/local_ntp_common.css
@@ -48,3 +48,18 @@
   --GR600-dark-rgb: 211, 59, 48;
   --GR800-dark-rgb: 180, 27, 26;
 }
+
+body {
+  /* Default font families for Linux and ChromeOS. */
+  font-family: 'Roboto', arial, sans-serif;
+}
+
+.win {
+  /* Default font families for Windows */
+  font-family: 'Segoe UI', 'Roboto', arial, sans-serif;
+}
+
+.mac {
+  /* Default font families for MacOS */
+  font-family: system-ui, BlinkMacSystemFont, 'Roboto', arial, sans-serif;
+}
diff --git a/chrome/browser/resources/local_ntp/local_ntp_resources.grd b/chrome/browser/resources/local_ntp/local_ntp_resources.grd
index f4da3f37..68609e7 100644
--- a/chrome/browser/resources/local_ntp/local_ntp_resources.grd
+++ b/chrome/browser/resources/local_ntp/local_ntp_resources.grd
@@ -15,7 +15,7 @@
       <include name="IDR_CUSTOM_LINKS_EDIT_CSS" file="custom_links_edit.css" type="BINDATA" />
       <include name="IDR_CUSTOM_LINKS_EDIT_JS" file="custom_links_edit.js" type="BINDATA" />
       <include name="IDR_CUSTOM_LINKS_EDIT_MENU_SVG" file="icons\edit_menu.svg" type="BINDATA" />
-      <include name="IDR_LOCAL_NTP_CONSTANTS_CSS" file="constants.css" type="BINDATA" />
+      <include name="IDR_LOCAL_NTP_COMMON_CSS" file="local_ntp_common.css" type="BINDATA" />
       <include name="IDR_LOCAL_NTP_ANIMATIONS_CSS" file="animations.css" flattenhtml="true" type="BINDATA" />
       <include name="IDR_LOCAL_NTP_ANIMATIONS_JS" file="animations.js" flattenhtml="true" type="BINDATA" />
       <include name="IDR_LOCAL_NTP_CSS" file="local_ntp.css" flattenhtml="true" type="BINDATA" />
diff --git a/chrome/browser/resources/local_ntp/most_visited_single.css b/chrome/browser/resources/local_ntp/most_visited_single.css
index a224468..bc412c5 100644
--- a/chrome/browser/resources/local_ntp/most_visited_single.css
+++ b/chrome/browser/resources/local_ntp/most_visited_single.css
@@ -204,7 +204,6 @@
   background-color: rgb(var(--GG600-rgb));
   border-radius: 50%;
   color: white;
-  font-family: 'Segoe UI', 'Roboto', arial, sans-serif;
   font-size: var(--md-fallback-letter-size);
   height: var(--md-favicon-size);
   line-height: var(--md-favicon-size);
@@ -212,9 +211,12 @@
   width: var(--md-favicon-size);
 }
 
+.win .md-fallback-letter {
+  font-weight: 600;
+}
+
 .md-title {
   color: rgb(var(--GG800-rgb));
-  font-family: 'Roboto', arial, sans-serif;
   font-size: var(--md-title-font-size);
   font-weight: 500;
   max-height: var(--md-title-max-height);
diff --git a/chrome/browser/resources/local_ntp/most_visited_single.html b/chrome/browser/resources/local_ntp/most_visited_single.html
index 92bf1458..2f7f9ae 100644
--- a/chrome/browser/resources/local_ntp/most_visited_single.html
+++ b/chrome/browser/resources/local_ntp/most_visited_single.html
@@ -6,7 +6,7 @@
 <head>
   <base target="_top">
   <meta charset="utf-8">
-  <link rel="stylesheet" type="text/css" href="constants.css">
+  <link rel="stylesheet" type="text/css" href="local-ntp-common.css">
   <link rel="stylesheet" type="text/css" href="single.css">
   <script src="utils.js"></script>
   <script src="single.js"></script>
diff --git a/chrome/browser/resources/local_ntp/most_visited_single.js b/chrome/browser/resources/local_ntp/most_visited_single.js
index bb17abd..8dfe125 100644
--- a/chrome/browser/resources/local_ntp/most_visited_single.js
+++ b/chrome/browser/resources/local_ntp/most_visited_single.js
@@ -44,7 +44,8 @@
 const CLASSES = {
   FAILED_FAVICON: 'failed-favicon',  // Applied when the favicon fails to load.
   REORDER: 'reorder',  // Applied to the tile being moved while reordering.
-  REORDERING: 'reordering',  // Applied while we are reordering.
+  REORDERING: 'reordering',      // Applied while we are reordering.
+  MAC_CHROMEOS: 'mac-chromeos',  // Reduces font weight for MacOS and ChromeOS.
   // Material Design classes.
   MD_EMPTY_TILE: 'md-empty-tile',
   MD_ICON_BACKGROUND: 'md-icon-background',
@@ -331,6 +332,7 @@
  */
 var showTiles = function(info) {
   logEvent(LOG_TYPE.NTP_ALL_TILES_RECEIVED);
+  utils.setPlatformClass(document.body);
   countLoad();
 };
 
@@ -346,10 +348,11 @@
   document.documentElement.setAttribute('darkmode', info.isDarkMode);
 
   // Reduce font weight on the default(white) background for Mac and CrOS.
-  document.body.classList.toggle('mac-chromeos',
+  document.body.classList.toggle(
+      CLASSES.MAC_CHROMEOS,
       !info.isThemeDark && !info.isUsingTheme &&
-      (navigator.userAgent.indexOf('Mac') > -1 ||
-      navigator.userAgent.indexOf('CrOS') > -1));
+          (navigator.userAgent.indexOf('Mac') > -1 ||
+           navigator.userAgent.indexOf('CrOS') > -1));
 };
 
 
@@ -752,9 +755,6 @@
       let fallbackLetter = document.createElement('div');
       fallbackLetter.className = CLASSES.MD_FALLBACK_LETTER;
       fallbackLetter.textContent = data.title.charAt(0).toUpperCase();
-      if (navigator.userAgent.indexOf('Windows') > -1) {
-        fallbackLetter.style.fontWeight = 600;
-      }
       mdIcon.classList.add(CLASSES.FAILED_FAVICON);
 
       fallbackBackground.appendChild(fallbackLetter);
@@ -784,10 +784,6 @@
   let mdTitleTextwrap = document.createElement('span');
   mdTitleTextwrap.innerText = data.title;
   mdTitle.style.direction = data.direction || 'ltr';
-  // Windows font family fallback to Segoe
-  if (navigator.userAgent.indexOf('Windows') > -1) {
-    mdTitle.style.fontFamily = 'Segoe UI';
-  }
   mdTitleContainer.appendChild(mdTitle);
   mdTileInner.appendChild(mdTitleContainer);
   mdTile.appendChild(mdTileInner);
diff --git a/chrome/browser/resources/local_ntp/utils.js b/chrome/browser/resources/local_ntp/utils.js
index 8c12cf4..0959d22 100644
--- a/chrome/browser/resources/local_ntp/utils.js
+++ b/chrome/browser/resources/local_ntp/utils.js
@@ -2,6 +2,15 @@
  * Use of this source code is governed by a BSD-style license that can be
  * found in the LICENSE file. */
 
+/**
+ * Enum for classnames.
+ * @enum {string}
+ * @const
+ */
+const CLASSES = {
+  MAC: 'mac',  // Applies MacOS specific properties.
+  WIN: 'win',  // Applies Windows specific properties.
+};
 
 /**
  * Alias for document.getElementById.
@@ -42,3 +51,15 @@
   return url.startsWith('http://') || url.startsWith('https://') ||
       url.startsWith('ftp://') || url.startsWith('chrome-extension://');
 };
+
+
+/**
+ * Sets CSS class for |element| corresponding to the current platform.
+ * @param {Element} element The element to set the current platform.
+ */
+utils.setPlatformClass = function(element) {
+  element.classList.toggle(
+      CLASSES.WIN, navigator.userAgent.indexOf('Windows') > -1);
+  element.classList.toggle(
+      CLASSES.MAC, navigator.userAgent.indexOf('Mac') > -1);
+};
diff --git a/chrome/browser/resources/management/management_ui.html b/chrome/browser/resources/management/management_ui.html
index 5bec4a36..6a7f8ad 100644
--- a/chrome/browser/resources/management/management_ui.html
+++ b/chrome/browser/resources/management/management_ui.html
@@ -193,9 +193,9 @@
                 <div class="content-indented browser-report">
                   <iron-icon icon="[[item.icon]]"></iron-icon>
                   <ul>
-                    <template is="dom-repeat" items="[[item.messages]]"
-                        as="message">
-                      <li>[[message]]</li>
+                    <template is="dom-repeat" items="[[item.messageIds]]"
+                        as="messageId">
+                      <li inner-h-t-m-l="[[i18nAdvanced(messageId)]]"></li>
                     </template>
                   </ul>
                 </div>
diff --git a/chrome/browser/resources/management/management_ui.js b/chrome/browser/resources/management/management_ui.js
index 65be622..a669283 100644
--- a/chrome/browser/resources/management/management_ui.js
+++ b/chrome/browser/resources/management/management_ui.js
@@ -5,7 +5,7 @@
 
 /**
  * @typedef {{
- *    messages: !Array<string>,
+ *    messageIds: !Array<string>,
  *    icon: string,
  * }}
  */
@@ -93,10 +93,9 @@
     const reportingInfoMap = reportingInfo.reduce((info, response) => {
       info[response.reportingType] = info[response.reportingType] || {
         icon: this.getIconForReportingType_(response.reportingType),
-        messages: []
+        messageIds: []
       };
-      info[response.reportingType].messages.push(
-          loadTimeData.getString(response.messageId));
+      info[response.reportingType].messageIds.push(response.messageId);
       return info;
     }, {});
 
diff --git a/chrome/browser/resources/pdf/BUILD.gn b/chrome/browser/resources/pdf/BUILD.gn
index 214f03a..1483ff4 100644
--- a/chrome/browser/resources/pdf/BUILD.gn
+++ b/chrome/browser/resources/pdf/BUILD.gn
@@ -10,6 +10,7 @@
     ":pdf_resources",
     "elements/viewer-bookmark:closure_compile",
     "elements/viewer-error-screen:closure_compile",
+    "elements/viewer-form-warning:closure_compile",
     "elements/viewer-page-indicator:closure_compile",
     "elements/viewer-page-selector:closure_compile",
     "elements/viewer-password-screen:closure_compile",
diff --git a/chrome/browser/resources/pdf/elements/viewer-form-warning/BUILD.gn b/chrome/browser/resources/pdf/elements/viewer-form-warning/BUILD.gn
new file mode 100644
index 0000000..e27ead67
--- /dev/null
+++ b/chrome/browser/resources/pdf/elements/viewer-form-warning/BUILD.gn
@@ -0,0 +1,18 @@
+# Copyright 2019 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import("//third_party/closure_compiler/compile_js.gni")
+
+js_type_check("closure_compile") {
+  deps = [
+    ":viewer-form-warning",
+  ]
+}
+
+js_library("viewer-form-warning") {
+  deps = [
+    "//ui/webui/resources/cr_elements/cr_dialog:cr_dialog",
+    "//ui/webui/resources/js:promise_resolver",
+  ]
+}
diff --git a/chrome/browser/resources/pdf/elements/viewer-form-warning/viewer-form-warning.html b/chrome/browser/resources/pdf/elements/viewer-form-warning/viewer-form-warning.html
new file mode 100644
index 0000000..e86b791
--- /dev/null
+++ b/chrome/browser/resources/pdf/elements/viewer-form-warning/viewer-form-warning.html
@@ -0,0 +1,25 @@
+<link rel="import" href="chrome://resources/html/polymer.html">
+
+<link rel="import" href="chrome://resources/cr_elements/cr_dialog/cr_dialog.html">
+<link rel="import" href="chrome://resources/cr_elements/hidden_style_css.html">
+<link rel="import" href="chrome://resources/cr_elements/paper_button_style_css.html">
+<link rel="import" href="chrome://resources/polymer/v1_0/paper-button/paper-button.html">
+
+<dom-module id="viewer-form-warning">
+  <template>
+    <style include="paper-button-style cr-hidden-style"></style>
+    <cr-dialog id="dialog" no-cancel>
+      <div slot="title">[[strings.annotationFormWarningTitle]]</div>
+      <div slot="body">[[strings.annotationFormWarningDetail]]</div>
+      <div slot="button-container">
+        <paper-button class="cancel-button" on-click="onCancel">
+          [[strings.annotationFormWarningKeepEditing]]
+        </paper-button>
+        <paper-button class="action-button" on-click="onAction">
+          [[strings.annotationFormWarningDiscard]]
+        </paper-button>
+      </div>
+    </cr-dialog>
+  </template>
+  <script src="viewer-form-warning.js"></script>
+</dom-module>
diff --git a/chrome/browser/resources/pdf/elements/viewer-form-warning/viewer-form-warning.js b/chrome/browser/resources/pdf/elements/viewer-form-warning/viewer-form-warning.js
new file mode 100644
index 0000000..539eddd6
--- /dev/null
+++ b/chrome/browser/resources/pdf/elements/viewer-form-warning/viewer-form-warning.js
@@ -0,0 +1,29 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+Polymer({
+  is: 'viewer-form-warning',
+  properties: {
+    strings: Object,
+  },
+
+  /** @private {PromiseResolver} */
+  resolver_: null,
+
+  show: function() {
+    this.resolver_ = new PromiseResolver();
+    /** @type {!CrDialogElement} */ (this.$.dialog).showModal();
+    return this.resolver_.promise;
+  },
+
+  onCancel: function() {
+    this.resolver_.reject();
+    this.$.dialog.cancel();
+  },
+
+  onAction: function() {
+    this.resolver_.resolve();
+    this.$.dialog.close();
+  },
+});
diff --git a/chrome/browser/resources/pdf/elements/viewer-pdf-toolbar/viewer-pdf-toolbar.js b/chrome/browser/resources/pdf/elements/viewer-pdf-toolbar/viewer-pdf-toolbar.js
index 82408d8..c9fc910 100644
--- a/chrome/browser/resources/pdf/elements/viewer-pdf-toolbar/viewer-pdf-toolbar.js
+++ b/chrome/browser/resources/pdf/elements/viewer-pdf-toolbar/viewer-pdf-toolbar.js
@@ -190,6 +190,11 @@
       // Select pen tool when entering annotation mode.
       this.updateAnnotationTool_(this.$.pen);
     }
+    this.dispatchEvent(new CustomEvent('annotation-mode-toggled', {
+      detail: {
+        value: this.annotationMode,
+      },
+    }));
   },
 
   /** @param {Event} e */
diff --git a/chrome/browser/resources/pdf/elements/viewer-pen-options/viewer-pen-options.html b/chrome/browser/resources/pdf/elements/viewer-pen-options/viewer-pen-options.html
index d7acee52..3151736c 100644
--- a/chrome/browser/resources/pdf/elements/viewer-pen-options/viewer-pen-options.html
+++ b/chrome/browser/resources/pdf/elements/viewer-pen-options/viewer-pen-options.html
@@ -73,8 +73,8 @@
       <paper-icon-button id="expand" icon="cr:expand-more"
           tabindex="3"
           on-click="toggleExpanded_"
-          aria-label$="[[strings.tooltipExpand]]"
-          title$="[[strings.tooltipExpand]]">
+          aria-label$="[[strings.annotationExpand]]"
+          title$="[[strings.annotationExpand]]">
       </paper-icon-button>
     </div>
     <div id="separator"></div>
diff --git a/chrome/browser/resources/pdf/index.html b/chrome/browser/resources/pdf/index.html
index 9d79f99..71b5d84 100644
--- a/chrome/browser/resources/pdf/index.html
+++ b/chrome/browser/resources/pdf/index.html
@@ -9,9 +9,11 @@
   <link rel="import" href="elements/viewer-pdf-toolbar/viewer-pdf-toolbar.html">
   <link rel="import" href="elements/viewer-zoom-toolbar/viewer-zoom-toolbar.html">
   <link rel="import" href="elements/shared-vars.html">
+  <link rel="import" href="chrome://resources/cr_elements/cr_dialog/cr_dialog.html">
 
 <if expr="chromeos">
   <link rel="import" href="elements/viewer-ink-host/viewer-ink-host.html">
+  <link rel="import" href="elements/viewer-form-warning/viewer-form-warning.html">
 </if>
 
   <link rel="stylesheet" href="chrome://resources/css/text_defaults_md.css">
@@ -30,6 +32,10 @@
 
 <viewer-error-screen id="error-screen"></viewer-error-screen>
 
+<if expr="chromeos">
+<viewer-form-warning id="form-warning"></viewer-form-warning>
+</if>
+
 <div id="content"></div>
 
 </body>
diff --git a/chrome/browser/resources/pdf/ink/ink_api.js b/chrome/browser/resources/pdf/ink/ink_api.js
index c530d57..4fd916d 100644
--- a/chrome/browser/resources/pdf/ink/ink_api.js
+++ b/chrome/browser/resources/pdf/ink/ink_api.js
@@ -58,7 +58,7 @@
     const shape = {
       eraser: 'MAGIC_ERASE',
       pen: 'INKPEN',
-      highlighter: 'HIGHLIGHTER',
+      highlighter: 'SMART_HIGHLIGHTER_TOOL',
     }[tool.tool];
     this.brush_.setShape(shape);
     if (tool.tool != 'eraser') {
diff --git a/chrome/browser/resources/pdf/pdf_viewer.js b/chrome/browser/resources/pdf/pdf_viewer.js
index b85e690..26b71ba 100644
--- a/chrome/browser/resources/pdf/pdf_viewer.js
+++ b/chrome/browser/resources/pdf/pdf_viewer.js
@@ -64,7 +64,8 @@
   }
 
   return (
-      activeElement.isContentEditable || activeElement.tagName == 'INPUT' ||
+      activeElement.isContentEditable ||
+      (activeElement.tagName == 'INPUT' && activeElement.type != 'radio') ||
       activeElement.tagName == 'TEXTAREA');
 }
 
@@ -256,7 +257,7 @@
     this.toolbar_.addEventListener(
         'rotate-right', () => this.rotateClockwise());
     this.toolbar_.addEventListener(
-        'annotation-mode-changed', e => this.annotationModeChanged_(e));
+        'annotation-mode-toggled', e => this.annotationModeToggled_(e));
     this.toolbar_.addEventListener(
         'annotation-tool-changed',
         e => this.inkController_.setAnnotationTool(e.detail.value));
@@ -500,17 +501,28 @@
    * @param {!CustomEvent<{value: boolean}>} e
    * @private
    */
-  annotationModeChanged_: async function(e) {
+  annotationModeToggled_: async function(e) {
     const annotationMode = e.detail.value;
     if (annotationMode) {
       // Enter annotation mode.
-      PDFMetrics.record(PDFMetrics.UserAction.ENTER_ANNOTATION_MODE);
-      this.hasEnteredAnnotationMode_ = true;
       assert(this.currentController_ == this.pluginController_);
       // TODO(dstockwell): set plugin read-only, begin transition
       this.updateProgress(0);
       // TODO(dstockwell): handle save failure
       const result = await this.pluginController_.save(true);
+      if (result.hasUnsavedChanges) {
+        assert(!loadTimeData.getBoolean('pdfFormSaveEnabled'));
+        try {
+          await $('form-warning').show();
+        } catch (e) {
+          // The user aborted entering annotation mode. Revert to the plugin.
+          this.toolbar_.annotationMode = false;
+          this.updateProgress(100);
+          return;
+        }
+      }
+      PDFMetrics.record(PDFMetrics.UserAction.ENTER_ANNOTATION_MODE);
+      this.hasEnteredAnnotationMode_ = true;
       // TODO(dstockwell): feed real progress data from the Ink component
       this.updateProgress(50);
       await this.inkController_.load(result.fileName, result.dataToSave);
@@ -737,6 +749,9 @@
     $('zoom-toolbar').strings = strings;
     $('password-screen').strings = strings;
     $('error-screen').strings = strings;
+    if ($('form-warning')) {
+      $('form-warning').strings = strings;
+    }
   },
 
   /**
diff --git a/chrome/browser/resources/policy/policy.css b/chrome/browser/resources/policy/policy.css
index 17241e8..025065d 100644
--- a/chrome/browser/resources/policy/policy.css
+++ b/chrome/browser/resources/policy/policy.css
@@ -89,11 +89,13 @@
   white-space: pre;
 }
 
+<if expr="not android">
 .messages.row .name,
 .conflict.row .name,
 .value.row .name {
   text-align: end;
 }
+</if>
 
 #status-box-container {
   display: flex;
@@ -178,11 +180,22 @@
 }
 
 <if expr="android">
+body > main {
+  padding: 0;
+}
+
+.value,
+.level,
+.messages,
+.scope {
+  display: none;
+}
+
 .name {
   flex: 0 0 50%;
 }
 
-.messages {
+.toggle {
   flex: 0 0 25%;
 }
 
@@ -202,6 +215,6 @@
 .messages.row .value,
 .value.row .value {
   display: block;
-  flex: 0 0 25%;
+  flex: 1;
 }
 </if>
\ No newline at end of file
diff --git a/chrome/browser/resources/policy/policy.html b/chrome/browser/resources/policy/policy.html
index e2e8d4e..743e944 100644
--- a/chrome/browser/resources/policy/policy.html
+++ b/chrome/browser/resources/policy/policy.html
@@ -102,6 +102,10 @@
         <div class="refresh-interval"></div>
       </div>
       <div class="status-entry">
+        <div class="label">$i18n{labelPoliciesPush}</div>
+        <div class="policy-push"></div>
+      </div>
+      <div class="status-entry">
         <div class="label">$i18n{labelStatus}</div>
         <div class="status"></div>
       </div>
diff --git a/chrome/browser/resources/policy/policy_base.js b/chrome/browser/resources/policy/policy_base.js
index 4179b68..06e59cc 100644
--- a/chrome/browser/resources/policy/policy_base.js
+++ b/chrome/browser/resources/policy/policy_base.js
@@ -149,6 +149,11 @@
           '.time-since-last-refresh', status.timeSinceLastRefresh, false);
       this.setLabelAndShow_('.refresh-interval', status.refreshInterval, false);
       this.setLabelAndShow_('.status', status.status, false);
+      this.setLabelAndShow_(
+          '.policy-push',
+          loadTimeData.getString(
+              status.policiesPushAvailable ? 'policiesPushOn' :
+                                             'policiesPushOff'));
     },
   };
 
@@ -274,16 +279,6 @@
             messagesNotice || conflictsNotice || loadTimeData.getString('ok');
         messagesDisplay.textContent = notice;
 
-        // <if expr="android">
-        const valueRowDisplay = this.querySelector('.value.row');
-        valueRowDisplay.hidden = false;
-        valueDisplay.hidden = true;
-        levelDisplay.hidden = true;
-        scopeDisplay.hidden = true;
-        this.querySelector('.toggle').hidden = true;
-        row.querySelectorAll('.policy-conflict-data')
-            .forEach(row => row.hidden = false);
-        // </if>
 
         if (policy.conflicts) {
           policy.conflicts.forEach(conflict => {
diff --git a/chrome/browser/resources/print_preview/data/destination.js b/chrome/browser/resources/print_preview/data/destination.js
index 1d38102..bc8fba10 100644
--- a/chrome/browser/resources/print_preview/data/destination.js
+++ b/chrome/browser/resources/print_preview/data/destination.js
@@ -146,8 +146,8 @@
  *   dpi: ({
  *     option: !Array<{
  *       vendor_id: (string|undefined),
- *       height_microns: number,
- *       width_microns: number,
+ *       horizontal_dpi: number,
+ *       vertical_dpi: number,
  *       is_default: (boolean|undefined)
  *     }>
  *   }|undefined)
diff --git a/chrome/browser/resources/print_preview/new/color_settings.html b/chrome/browser/resources/print_preview/new/color_settings.html
index c46d036..9d3b121 100644
--- a/chrome/browser/resources/print_preview/new/color_settings.html
+++ b/chrome/browser/resources/print_preview/new/color_settings.html
@@ -3,6 +3,7 @@
 <link rel="import" href="chrome://resources/html/md_select_css.html">
 <link rel="import" href="print_preview_shared_css.html">
 <link rel="import" href="select_behavior.html">
+<link rel="import" href="settings_behavior.html">
 <link rel="import" href="settings_section.html">
 
 <dom-module id="print-preview-color-settings">
diff --git a/chrome/browser/resources/print_preview/new/margins_settings.html b/chrome/browser/resources/print_preview/new/margins_settings.html
index a18a3d6..49f34fe 100644
--- a/chrome/browser/resources/print_preview/new/margins_settings.html
+++ b/chrome/browser/resources/print_preview/new/margins_settings.html
@@ -1,6 +1,7 @@
 <link rel="import" href="chrome://resources/html/polymer.html">
 
 <link rel="import" href="chrome://resources/html/md_select_css.html">
+<link rel="import" href="../data/margins.html">
 <link rel="import" href="print_preview_shared_css.html">
 <link rel="import" href="select_behavior.html">
 <link rel="import" href="settings_behavior.html">
@@ -20,10 +21,14 @@
           <!-- The order of these options must match the natural order of their
           values, which come from
           print_preview.ticket_items.MarginsTypeValue. -->
-          <option value="0" selected>$i18n{defaultMargins}</option>
-          <option value="1">$i18n{noMargins}</option>
-          <option value="2">$i18n{minimumMargins}</option>
-          <option value="3">$i18n{customMargins}</option>
+          <option value="[[MarginsValue.DEFAULT]]" selected>
+            $i18n{defaultMargins}
+          </option>
+          <option value="[[MarginsValue.NO_MARGINS]]">$i18n{noMargins}</option>
+          <option value="[[MarginsValue.MINIMUM]]">
+            $i18n{minimumMargins}
+          </option>
+          <option value="[[MarginsValue.CUSTOM]]">$i18n{customMargins}</option>
         </select>
       </div>
     </print-preview-settings-section>
diff --git a/chrome/browser/resources/print_preview/new/margins_settings.js b/chrome/browser/resources/print_preview/new/margins_settings.js
index 2623a2ba..5dfad94 100644
--- a/chrome/browser/resources/print_preview/new/margins_settings.js
+++ b/chrome/browser/resources/print_preview/new/margins_settings.js
@@ -9,10 +9,18 @@
 
   properties: {
     disabled: Boolean,
+
+    /** Mirroring the enum so that it can be used from HTML bindings. */
+    MarginsValue: Object,
   },
 
   observers: ['onMarginsSettingChange_(settings.margins.value)'],
 
+  /** @override */
+  ready: function() {
+    this.MarginsValue = print_preview.ticket_items.MarginsTypeValue;
+  },
+
   /**
    * @param {*} newValue The new value of the margins setting.
    * @private
diff --git a/chrome/browser/resources/print_preview/new/model.js b/chrome/browser/resources/print_preview/new/model.js
index 63f6c89b..c8d90bc 100644
--- a/chrome/browser/resources/print_preview/new/model.js
+++ b/chrome/browser/resources/print_preview/new/model.js
@@ -110,6 +110,9 @@
     /**
      * Object containing current settings of Print Preview, for use by Polymer
      * controls.
+     * Initialize settings that are only available on some printers to
+     * unavailable, and settings that are provided by PDF generation to
+     * available.
      * @type {!print_preview_new.Settings}
      */
     settings: {
@@ -153,18 +156,18 @@
             value: true, /* color */
             unavailableValue: false,
             valid: true,
-            available: true,
+            available: false,
             setByPolicy: false,
             key: 'isColorEnabled',
           },
           mediaSize: {
-            value: {
+            value: {},
+            unavailableValue: {
               width_microns: 215900,
               height_microns: 279400,
             },
-            unavailableValue: {},
             valid: true,
-            available: true,
+            available: false,
             setByPolicy: false,
             key: 'mediaSize',
           },
@@ -189,7 +192,7 @@
             value: {},
             unavailableValue: {},
             valid: true,
-            available: true,
+            available: false,
             setByPolicy: false,
             key: 'dpi',
           },
@@ -221,7 +224,7 @@
             value: true,
             unavailableValue: false,
             valid: true,
-            available: true,
+            available: false,
             setByPolicy: false,
             key: 'isDuplexEnabled',
           },
@@ -261,7 +264,7 @@
             value: {},
             unavailableValue: {},
             valid: true,
-            available: true,
+            available: false,
             setByPolicy: false,
             key: 'vendorOptions',
           },
@@ -549,19 +552,40 @@
    */
   updateSettingsValues_: function(caps) {
     if (this.settings.mediaSize.available) {
-      const defaultOption = caps.media_size.option.find(o => !!o.is_default);
-      this.setSetting('mediaSize', defaultOption);
+      const defaultOption = caps.media_size.option.find(o => !!o.is_default) ||
+          caps.media_size.option[0];
+      let matchingOption = null;
+      // If the setting does not have a valid value, the UI has just started so
+      // do not try to get a matching value; just set the printer default in
+      // case the user doesn't have sticky settings.
+      if (this.settings.mediaSize.value.height_microns !== undefined) {
+        const currentMediaSize = this.getSettingValue('mediaSize');
+        matchingOption = caps.media_size.option.find(o => {
+          return o.height_microns === currentMediaSize.height_microns &&
+              o.width_microns === currentMediaSize.width_microns;
+        });
+      }
+      this.setSetting('mediaSize', matchingOption || defaultOption);
     }
 
     if (this.settings.dpi.available) {
-      const defaultOption = caps.dpi.option.find(o => !!o.is_default);
-      this.setSetting('dpi', defaultOption);
+      const defaultOption =
+          caps.dpi.option.find(o => !!o.is_default) || caps.dpi.option[0];
+      let matchingOption = null;
+      if (this.settings.dpi.value.horizontal_dpi !== undefined) {
+        const currentDpi = this.getSettingValue('dpi');
+        matchingOption = caps.dpi.option.find(o => {
+          return o.horizontal_dpi === currentDpi.horizontal_dpi &&
+              o.vertical_dpi === currentDpi.vertical_dpi;
+        });
+      }
+      this.setSetting('dpi', matchingOption || defaultOption);
     } else if (
         caps && caps.dpi && caps.dpi.option && caps.dpi.option.length > 0) {
       this.set('settings.dpi.unavailableValue', caps.dpi.option[0]);
     }
 
-    if (this.settings.color.available) {
+    if (!this.initialized_ && this.settings.color.available) {
       const defaultOption = this.destination.defaultColorOption;
       if (defaultOption) {
         this.setSetting(
@@ -570,22 +594,24 @@
                 defaultOption.type));
       }
     } else if (
-        this.destination.id ===
-            print_preview.Destination.GooglePromotedId.DOCS ||
-        this.destination.type === print_preview.DestinationType.MOBILE) {
+        !this.settings.color.available &&
+        (this.destination.id ===
+             print_preview.Destination.GooglePromotedId.DOCS ||
+         this.destination.type === print_preview.DestinationType.MOBILE)) {
       this.set('settings.color.unavailableValue', true);
     } else if (
-        caps && caps.color && caps.color.option &&
-        caps.color.option.length > 0) {
+        !this.settings.color.available && caps && caps.color &&
+        caps.color.option && caps.color.option.length > 0) {
       this.set(
           'settings.color.unavailableValue',
           !['STANDARD_MONOCHROME', 'CUSTOM_MONOCHROME'].includes(
               caps.color.option[0].type));
-    } else {  // if no color capability is reported, assume black and white.
+    } else if (!this.settings.color.available) {
+      // if no color capability is reported, assume black and white.
       this.set('settings.color.unavailableValue', false);
     }
 
-    if (this.settings.duplex.available) {
+    if (!this.initialized_ && this.settings.duplex.available) {
       const defaultOption = caps.duplex.option.find(o => !!o.is_default);
       this.setSetting(
           'duplex',
@@ -593,13 +619,15 @@
               defaultOption.type == print_preview_new.DuplexType.LONG_EDGE :
               false);
     } else if (
-        caps && caps.duplex && caps.duplex.option &&
+        !this.settings.duplex.available && caps && caps.duplex &&
+        caps.duplex.option &&
         !caps.duplex.option.some(
             o => o.type != print_preview_new.DuplexType.LONG_EDGE)) {
       // If the only option available is long edge, the value should always be
       // true.
       this.set('settings.duplex.unavailableValue', true);
-    } else {  // If no duplex capability is reported, assume false.
+    } else if (!this.settings.duplex.available) {
+      // If no duplex capability is reported, assume false.
       this.set('settings.duplex.unavailableValue', false);
     }
 
diff --git a/chrome/browser/resources/safe_browsing/README.md b/chrome/browser/resources/safe_browsing/README.md
index 7a95496..63b5ab4e 100644
--- a/chrome/browser/resources/safe_browsing/README.md
+++ b/chrome/browser/resources/safe_browsing/README.md
@@ -25,6 +25,42 @@
     * The Component Updater system will notice those files and push them to
       users withing ~6 hours. If not, contact `waffles@.`
 
+## Procedure for rollback
+While Omaha allows rollback through the release manager, the Chrome client will
+reject updates with lower version numbers. (This is important for running new
+versions on Canary/Dev channel). Rolling back a bad version is best achieved by:
+  * **Reverting** the changes on the Chromium source tree.
+  * **Submitting** a new CL incrementing the version number.
+  * **Push** the newest version, as above.
+
+## Procedure for incremental rollout
+  * Open the Omaha Release manager at:
+    https://omaharelease.corp.google.com/product/1436/cohorts
+  * **Disable** the automatic push by changing the _Push Scheduler_ from
+    `LATEST_TO_AUTO` to `NONE`. Then commit the changes by clicking _Commit
+    Automation Changes_.
+  * **Upload** the new version of the file types.
+    * In a synced checkout, run the following to generate protos for all
+      platforms and push them to GCS. Replace the arg with your build directory:
+        * % `chrome/browser/resources/safe_browsing/push_file_type_proto.py -d
+          out-gn/Debug`
+  * Create a new cohort.
+    * Under _Cohorts_, click _Manage_, then _Create Subcohort_ of Auto.
+    * Select a name and percentage for the new cohort.
+    * Return to the product page.
+  * **Push** to the new cohort.
+    * Select _Edit Schedule_ for the new cohort, and select the newest file
+      group and a time to roll out.
+  * After the incremental rollout is complete, remember to set the _Push
+    Scheduler_ back to `LATEST_TO_AUTO`
+
+Note: Everything after disabling automation could be scripted through the
+[ReleaseServiceManager](http://google3/java/com/google/installer/releasemanager/proto/releasemanager_stubby.proto).
+An example script using this API is
+[here](http://google3/googleclient/installer/tools/release/keystone/create_release_testing_cohorts.py).
+If we regularly need incremental rollouts, it may be worth creating our own
+scripts to do so reliably.
+
 
 ## Guidelines for a DownloadFileType entry:
 See `download_file_types.proto` for all fields.
@@ -45,8 +81,8 @@
 
     * `SAMPLED_PING`: Don't send a full Safe Browsing ping, but
        send a no-PII "light-ping" for a random sample of SBER users.
-       This should be the default for unknown types. The verdict won't
-       be used.
+       This should be used for known safe types. The verdict won't be used.
+
     * `NO_PING`:  Don’t send any pings. This file is whitelisted. All
       NOT_DANGEROUS files should normally use this.
     * `FULL_PING`: Send full pings and use the verdict. All dangerous
@@ -138,5 +174,5 @@
 
   * `default_file_type`: Settings used if a downloaded file is not in
     the above list. `extension` is ignored, but other settings are used.
-    The ping_setting should be SAMPLED_PING for all platforms.
-
+    The ping_setting should be FULL_PING for all platforms, so that
+    unknown file types generate pings.
diff --git a/chrome/browser/resources/settings/autofill_page/payments_section.html b/chrome/browser/resources/settings/autofill_page/payments_section.html
index 57fbb20..2cb349b 100644
--- a/chrome/browser/resources/settings/autofill_page/payments_section.html
+++ b/chrome/browser/resources/settings/autofill_page/payments_section.html
@@ -59,72 +59,31 @@
       <span>$i18nRaw{manageCreditCardsLabel}</span>
     </div>
 
-    <!--The version of settings that contains all the credit cards.-->
-    <template is="dom-if" if="[[!splitCreditCardList_]]">
-      <div class="settings-box continuation">
-        <h2 class="start">$i18n{creditCards}</h2>
-        <paper-button id="addCreditCard"
-            class="secondary-button header-aligned-button"
-            on-click="onAddCreditCardTap_"
-            hidden$="[[!prefs.autofill.credit_card_enabled.value]]">
-          $i18n{add}
-        </paper-button>
+    <div class="settings-box continuation">
+      <h2 class="start">$i18n{creditCards}</h2>
+      <paper-button id="addCreditCard"
+          class="secondary-button header-aligned-button"
+          on-click="onAddCreditCardTap_"
+          hidden$="[[!prefs.autofill.credit_card_enabled.value]]">
+        $i18n{add}
+      </paper-button>
+    </div>
+    <div class="settings-box two-line" id="migrateCreditCards"
+        hidden$="[[!checkIfMigratable_(syncStatus, creditCards,
+            prefs.autofill.credit_card_enabled.value)]]"
+        on-click="onMigrateCreditCardsClick_" actionable>
+      <div class="start">
+        $i18n{migrateCreditCardsLabel}
+        <div class="secondary">[[migratableCreditCardsInfo_]]</div>
       </div>
-      <div class="settings-box two-line" id="migrateCreditCards"
-          hidden$="[[!checkIfMigratable_(syncStatus, creditCards,
-              prefs.autofill.credit_card_enabled.value)]]"
-          on-click="onMigrateCreditCardsClick_" actionable>
-        <div class="start">
-          $i18n{migrateCreditCardsLabel}
-          <div class="secondary">[[migratableCreditCardsInfo_]]</div>
-        </div>
-        <paper-icon-button-light id="migrateCreditCardsButton"
-            class="subpage-arrow">
-          <button aria-label="[[migrateCreditCardsLabel_]]"></button>
-        </paper-icon-button-light>
-      </div>
-      <settings-credit-card-list id="creditCardList" class="list-frame"
-          credit-cards="[[creditCards]]">
-      </settings-credit-card-list>
-    </template>
-
-    <!--The version of settings that have split credit card sections.-->
-    <template is="dom-if" if="[[splitCreditCardList_]]">
-      <!--The section that contains the local credit cards.-->
-      <div class="settings-box continuation">
-        <h2 class="start">$i18n{localCreditCardsSectionTitle}</h2>
-        <paper-button id="addCreditCard"
-            class="secondary-button header-aligned-button"
-            on-click="onAddCreditCardTap_"
-            hidden$="[[!prefs.autofill.credit_card_enabled.value]]">
-          $i18n{add}
-        </paper-button>
-      </div>
-      <div class="settings-box two-line" id="migrateCreditCards"
-          hidden$="[[!checkIfMigratable_(syncStatus, localCreditCards,
-              prefs.autofill.credit_card_enabled.value)]]"
-          on-click="onMigrateCreditCardsClick_" actionable>
-        <div class="start">
-          $i18n{migrateCreditCardsLabel}
-          <div class="secondary">[[migratableCreditCardsInfo_]]</div>
-        </div>
-        <paper-icon-button-light id="migrateCreditCardsButton"
-            class="subpage-arrow">
-          <button aria-label="[[migrateCreditCardsLabel_]]"></button>
-        </paper-icon-button-light>
-      </div>
-      <settings-credit-card-list id="localCreditCardList" class="list-frame"
-          credit-cards="[[localCreditCards]]">
-      </settings-credit-card-list>
-
-      <!--The section that contains the server credit cards.-->
-      <div class="settings-box continuation">
-        <h2 class="start">$i18n{serverCreditCardsSectionTitle}</h2>
-      </div>
-      <settings-credit-card-list id="serverCreditCardList" class="list-frame"
-          credit-cards="[[serverCreditCards]]">
-      </settings-credit-card-list>
-    </template>
+      <paper-icon-button-light id="migrateCreditCardsButton"
+          class="subpage-arrow">
+        <button aria-label="[[migrateCreditCardsLabel_]]"></button>
+      </paper-icon-button-light>
+    </div>
+    <settings-credit-card-list id="creditCardList" class="list-frame"
+        credit-cards="[[creditCards]]">
+    </settings-credit-card-list>
 
     <cr-action-menu id="creditCardSharedMenu">
       <button id="menuEditCreditCard" class="dropdown-item"
diff --git a/chrome/browser/resources/settings/autofill_page/payments_section.js b/chrome/browser/resources/settings/autofill_page/payments_section.js
index ff88ab7..0eca2bcf 100644
--- a/chrome/browser/resources/settings/autofill_page/payments_section.js
+++ b/chrome/browser/resources/settings/autofill_page/payments_section.js
@@ -19,53 +19,17 @@
   addCreditCardListChangedListener(listener) {}
 
   /**
-   * Add an observer to the list of local credit cards.
-   * @param {function(!Array<!PaymentsManager.CreditCardEntry>):void} listener
-   */
-  addLocalCreditCardListChangedListener(listener) {}
-
-  /**
-   * Add an observer to the list of server credit cards.
-   * @param {function(!Array<!PaymentsManager.CreditCardEntry>):void} listener
-   */
-  addServerCreditCardListChangedListener(listener) {}
-
-  /**
    * Remove an observer from the list of credit cards.
    * @param {function(!Array<!PaymentsManager.CreditCardEntry>):void} listener
    */
   removeCreditCardListChangedListener(listener) {}
 
   /**
-   * Remove an observer from the list of local credit cards.
-   * @param {function(!Array<!PaymentsManager.CreditCardEntry>):void} listener
-   */
-  removeLocalCreditCardListChangedListener(listener) {}
-
-  /**
-   * Remove an observer from the list of server credit cards.
-   * @param {function(!Array<!PaymentsManager.CreditCardEntry>):void} listener
-   */
-  removeServerCreditCardListChangedListener(listener) {}
-
-  /**
    * Request the list of credit cards.
    * @param {function(!Array<!PaymentsManager.CreditCardEntry>):void} callback
    */
   getCreditCardList(callback) {}
 
-  /**
-   * Request the list of local credit cards.
-   * @param {function(!Array<!PaymentsManager.CreditCardEntry>):void} callback
-   */
-  getLocalCreditCardList(callback) {}
-
-  /**
-   * Request the list of server credit cards.
-   * @param {function(!Array<!PaymentsManager.CreditCardEntry>):void} callback
-   */
-  getServerCreditCardList(callback) {}
-
   /** @param {string} guid The GUID of the credit card to remove.  */
   removeCreditCard(guid) {}
 
@@ -103,48 +67,16 @@
   }
 
   /** @override */
-  addLocalCreditCardListChangedListener(listener) {
-    chrome.autofillPrivate.onLocalCreditCardListChanged.addListener(listener);
-  }
-
-  /** @override */
-  addServerCreditCardListChangedListener(listener) {
-    chrome.autofillPrivate.onServerCreditCardListChanged.addListener(listener);
-  }
-
-  /** @override */
   removeCreditCardListChangedListener(listener) {
     chrome.autofillPrivate.onCreditCardListChanged.removeListener(listener);
   }
 
   /** @override */
-  removeLocalCreditCardListChangedListener(listener) {
-    chrome.autofillPrivate.onLocalCreditCardListChanged.removeListener(
-        listener);
-  }
-
-  /** @override */
-  removeServerCreditCardListChangedListener(listener) {
-    chrome.autofillPrivate.onServerCreditCardListChanged.removeListener(
-        listener);
-  }
-
-  /** @override */
   getCreditCardList(callback) {
     chrome.autofillPrivate.getCreditCardList(callback);
   }
 
   /** @override */
-  getLocalCreditCardList(callback) {
-    chrome.autofillPrivate.getLocalCreditCardList(callback);
-  }
-
-  /** @override */
-  getServerCreditCardList(callback) {
-    chrome.autofillPrivate.getServerCreditCardList(callback);
-  }
-
-  /** @override */
   removeCreditCard(guid) {
     chrome.autofillPrivate.removeEntry(assert(guid));
   }
@@ -191,18 +123,6 @@
     creditCards: Array,
 
     /**
-     * An array of saved locl credit cards.
-     * @type {!Array<!PaymentsManager.CreditCardEntry>}
-     */
-    localCreditCards: Array,
-
-    /**
-     * An array of saved server credit cards.
-     * @type {!Array<!PaymentsManager.CreditCardEntry>}
-     */
-    serverCreditCards: Array,
-
-    /**
      * The model for any credit card related action menus or dialogs.
      * @private {?chrome.autofillPrivate.CreditCardEntry}
      */
@@ -291,18 +211,6 @@
       },
       readOnly: true,
     },
-
-    /**
-     * True when the cards list should be split in two..
-     * @private {boolean}
-     */
-    splitCreditCardList_: {
-      type: Boolean,
-      value: function() {
-        return loadTimeData.getBoolean('splitCreditCardList');
-      },
-      readOnly: true,
-    },
   },
 
   listeners: {
@@ -330,18 +238,6 @@
    */
   setCreditCardsListener_: null,
 
-  /**
-   * @type {?function(!Array<!PaymentsManager.CreditCardEntry>)}
-   * @private
-   */
-  setLocalCreditCardsListener_: null,
-
-  /**
-   * @type {?function(!Array<!PaymentsManager.CreditCardEntry>)}
-   * @private
-   */
-  setServerCreditCardsListener_: null,
-
   /** @private {?settings.SyncBrowserProxy} */
   syncBrowserProxy_: null,
 
@@ -352,35 +248,19 @@
     const setCreditCardsListener = list => {
       this.creditCards = list;
     };
-    /** @type {function(!Array<!PaymentsManager.CreditCardEntry>)} */
-    const setLocalCreditCardsListener = list => {
-      this.localCreditCards = list;
-    };
-    /** @type {function(!Array<!PaymentsManager.CreditCardEntry>)} */
-    const setServerCreditCardsListener = list => {
-      this.serverCreditCards = list;
-    };
 
     // Remember the bound reference in order to detach.
     this.setCreditCardsListener_ = setCreditCardsListener;
-    this.setLocalCreditCardsListener_ = setLocalCreditCardsListener;
-    this.setServerCreditCardsListener_ = setServerCreditCardsListener;
 
     // Set the managers. These can be overridden by tests.
     this.paymentsManager_ = PaymentsManagerImpl.getInstance();
 
     // Request initial data.
     this.paymentsManager_.getCreditCardList(setCreditCardsListener);
-    this.paymentsManager_.getLocalCreditCardList(setLocalCreditCardsListener);
-    this.paymentsManager_.getServerCreditCardList(setServerCreditCardsListener);
 
     // Listen for changes.
     this.paymentsManager_.addCreditCardListChangedListener(
         setCreditCardsListener);
-    this.paymentsManager_.addLocalCreditCardListChangedListener(
-        setLocalCreditCardsListener);
-    this.paymentsManager_.addServerCreditCardListChangedListener(
-        setServerCreditCardsListener);
 
     this.syncBrowserProxy_ = settings.SyncBrowserProxyImpl.getInstance();
     this.syncBrowserProxy_.getSyncStatus().then(
@@ -397,12 +277,6 @@
     this.paymentsManager_.removeCreditCardListChangedListener(
         /** @type {function(!Array<!PaymentsManager.CreditCardEntry>)} */ (
             this.setCreditCardsListener_));
-    this.paymentsManager_.removeLocalCreditCardListChangedListener(
-        /** @type {function(!Array<!PaymentsManager.CreditCardEntry>)} */ (
-            this.setLocalCreditCardsListener_));
-    this.paymentsManager_.removeServerCreditCardListChangedListener(
-        /** @type {function(!Array<!PaymentsManager.CreditCardEntry>)} */ (
-            this.setServerCreditCardsListener_));
   },
 
   /**
diff --git a/chrome/browser/resources/settings/people_page/people_page.html b/chrome/browser/resources/settings/people_page/people_page.html
index bb5681f4..0234eae 100644
--- a/chrome/browser/resources/settings/people_page/people_page.html
+++ b/chrome/browser/resources/settings/people_page/people_page.html
@@ -327,8 +327,9 @@
       </template>
 
       <template is="dom-if" if="[[unifiedConsentEnabled_]]">
-        <template is="dom-if" route-path="/syncSetup/advanced" no-search>
+        <template is="dom-if" route-path="/syncSetup/advanced">
           <settings-subpage page-title="$i18n{syncAdvancedPageTitle}"
+              associated-control="[[$$('#sync-setup')]]"
               learn-more-url="$i18n{syncAndGoogleServicesLearnMoreURL}">
             <settings-sync-controls sync-status="[[syncStatus]]">
             </settings-sync-controls>
diff --git a/chrome/browser/resources/settings/people_page/sync_controls.html b/chrome/browser/resources/settings/people_page/sync_controls.html
index b809d8e..cf1c3ca8 100644
--- a/chrome/browser/resources/settings/people_page/sync_controls.html
+++ b/chrome/browser/resources/settings/people_page/sync_controls.html
@@ -24,7 +24,6 @@
         flex: 1;
       }
     </style>
-
     <div class="settings-box first">
       <div id="syncEverythingCheckboxLabel" class="start">
         $i18n{syncEverythingCheckboxLabel}
diff --git a/chrome/browser/resources/settings/people_page/sync_controls.js b/chrome/browser/resources/settings/people_page/sync_controls.js
index c3642e2..452cc5e 100644
--- a/chrome/browser/resources/settings/people_page/sync_controls.js
+++ b/chrome/browser/resources/settings/people_page/sync_controls.js
@@ -32,6 +32,14 @@
   behaviors: [WebUIListenerBehavior],
 
   properties: {
+    hidden: {
+      type: Boolean,
+      value: false,
+      computed: 'syncControlsHidden_(' +
+          'syncStatus.signedIn, syncStatus.disabled, syncStatus.hasError)',
+      reflectToAttribute: true,
+    },
+
     /**
      * The current sync preferences, supplied by SyncBrowserProxy.
      * @type {settings.SyncPrefs|undefined}
@@ -161,21 +169,29 @@
   },
 
   /** @private */
-  syncStatusChanged_: function(syncStatus) {
-    // When the sync controls are embedded, the parent has to take care of
-    // showing/hiding them.
-    if (settings.getCurrentRoute() != settings.routes.SYNC_ADVANCED ||
-        !syncStatus) {
-      return;
-    }
-
-    // Navigate to main sync page when the sync controls page should *not* be
-    // available.
-    if (!syncStatus.signedIn || syncStatus.disabled ||
-        (syncStatus.hasError &&
-         syncStatus.statusAction !== settings.StatusAction.ENTER_PASSPHRASE)) {
+  syncStatusChanged_: function() {
+    if (settings.getCurrentRoute() == settings.routes.SYNC_ADVANCED &&
+        this.syncControlsHidden_()) {
       settings.navigateTo(settings.routes.SYNC);
     }
   },
+
+  /**
+   * @return {boolean} Whether the sync controls are hidden.
+   * @private
+   */
+  syncControlsHidden_: function() {
+    if (!this.syncStatus) {
+      // Show sync controls by default.
+      return false;
+    }
+
+    if (!this.syncStatus.signedIn || this.syncStatus.disabled) {
+      return true;
+    }
+
+    return !!this.syncStatus.hasError &&
+        this.syncStatus.statusAction !== settings.StatusAction.ENTER_PASSPHRASE;
+  },
 });
 })();
diff --git a/chrome/browser/resources/settings/people_page/sync_page.html b/chrome/browser/resources/settings/people_page/sync_page.html
index ff7cddb4..413b7467 100644
--- a/chrome/browser/resources/settings/people_page/sync_page.html
+++ b/chrome/browser/resources/settings/people_page/sync_page.html
@@ -197,7 +197,7 @@
         </template>
 
         <template is="dom-if" if="[[!unifiedConsentEnabled]]">
-          <settings-sync-controls sync-status="syncStatus">
+          <settings-sync-controls sync-status="[[syncStatus]]">
           </settings-sync-controls>
         </template>
 
diff --git a/chrome/browser/resources/settings/site_settings/site_details.html b/chrome/browser/resources/settings/site_settings/site_details.html
index 4333d45..944392a 100644
--- a/chrome/browser/resources/settings/site_settings/site_details.html
+++ b/chrome/browser/resources/settings/site_settings/site_details.html
@@ -2,6 +2,8 @@
 
 <link rel="import" href="chrome://resources/html/action_link.html">
 <link rel="import" href="chrome://resources/html/action_link_css.html">
+<link rel="import" href="chrome://resources/html/assert.html">
+<link rel="import" href="chrome://resources/html/cr/ui/focus_without_ink.html">
 <link rel="import" href="chrome://resources/html/i18n_behavior.html">
 <link rel="import" href="chrome://resources/cr_elements/cr_dialog/cr_dialog.html">
 <link rel="import" href="chrome://resources/cr_elements/icons.html">
@@ -33,12 +35,13 @@
         padding-inline-start: var(--settings-box-row-padding);
       }
 
-      div#resetSettingsButton {
+      #resetSettingsButton {
         margin-top: 24px;
       }
     </style>
     <!-- Confirm reset settings dialog. -->
-    <cr-dialog id="confirmResetSettings" close-text="$i18n{close}">
+    <cr-dialog id="confirmResetSettings" close-text="$i18n{close}"
+        on-close="onResetSettingsDialogClosed_">
       <div slot="body">
         [[i18n('siteSettingsSiteResetConfirmation', pageTitle)]]
       </div>
@@ -53,7 +56,8 @@
     </cr-dialog>
 
     <!-- Confirm clear storage dialog. -->
-    <cr-dialog id="confirmClearStorage" close-text="$i18n{close}">
+    <cr-dialog id="confirmClearStorage" close-text="$i18n{close}"
+        on-close="onClearStorageDialogClosed_">
       <div slot="title">
         $i18n{siteSettingsSiteClearStorageDialogTitle}
       </div>
@@ -82,8 +86,9 @@
           </div>
           <div class="list-item" id="storage" hidden$="[[!storedData_]]">
             <div class="start">[[storedData_]]</div>
-            <paper-button class="secondary-button" role="button"
-                aria-disabled="false" on-click="onConfirmClearStorage_"
+            <paper-button class="secondary-button" id="clearStorage" 
+                role="button" aria-disabled="false"
+                on-click="onConfirmClearStorage_"
                 aria-label="$i18n{siteSettingsDelete}">
               $i18n{siteSettingsDelete}
             </paper-button>
diff --git a/chrome/browser/resources/settings/site_settings/site_details.js b/chrome/browser/resources/settings/site_settings/site_details.js
index 622cbab..8f92a6a 100644
--- a/chrome/browser/resources/settings/site_settings/site_details.js
+++ b/chrome/browser/resources/settings/site_settings/site_details.js
@@ -258,4 +258,13 @@
     return storage != '';
   },
 
+  /** @private */
+  onResetSettingsDialogClosed_: function() {
+    cr.ui.focusWithoutInk(assert(this.$$('#resetSettingsButton')));
+  },
+
+  /** @private */
+  onClearStorageDialogClosed_: function() {
+    cr.ui.focusWithoutInk(assert(this.$$('#clearStorage')));
+  },
 });
diff --git a/chrome/browser/resources/welcome/onboarding_welcome/ntp_background/ntp_background_proxy.js b/chrome/browser/resources/welcome/onboarding_welcome/ntp_background/ntp_background_proxy.js
index 7623098..3c86b2a 100644
--- a/chrome/browser/resources/welcome/onboarding_welcome/ntp_background/ntp_background_proxy.js
+++ b/chrome/browser/resources/welcome/onboarding_welcome/ntp_background/ntp_background_proxy.js
@@ -15,6 +15,8 @@
 
   /** @interface */
   class NtpBackgroundProxy {
+    clearBackground() {}
+
     /** @return {!Promise<!Array<!nux.NtpBackgroundData>>} */
     getBackgrounds() {}
 
@@ -31,6 +33,11 @@
   /** @implements {nux.NtpBackgroundProxy} */
   class NtpBackgroundProxyImpl {
     /** @override */
+    clearBackground() {
+      return cr.sendWithPromise('clearBackground');
+    }
+
+    /** @override */
     getBackgrounds() {
       return cr.sendWithPromise('getBackgrounds');
     }
diff --git a/chrome/browser/resources/welcome/onboarding_welcome/ntp_background/nux_ntp_background.html b/chrome/browser/resources/welcome/onboarding_welcome/ntp_background/nux_ntp_background.html
index 2ad633d..f20e0eab 100644
--- a/chrome/browser/resources/welcome/onboarding_welcome/ntp_background/nux_ntp_background.html
+++ b/chrome/browser/resources/welcome/onboarding_welcome/ntp_background/nux_ntp_background.html
@@ -207,7 +207,6 @@
         </div>
         <step-indicator model="[[indicatorModel]]"></step-indicator>
         <paper-button class="action-button"
-            disabled$="[[!hasValidSelectedBackground_(selectedBackground_)]]"
             on-click="onNextClicked_">
           $i18n{next}
           <iron-icon icon="cr:chevron-right"></iron-icon>
diff --git a/chrome/browser/resources/welcome/onboarding_welcome/ntp_background/nux_ntp_background.js b/chrome/browser/resources/welcome/onboarding_welcome/ntp_background/nux_ntp_background.js
index a9bf6b8..ad37645c 100644
--- a/chrome/browser/resources/welcome/onboarding_welcome/ntp_background/nux_ntp_background.js
+++ b/chrome/browser/resources/welcome/onboarding_welcome/ntp_background/nux_ntp_background.js
@@ -168,6 +168,8 @@
   onNextClicked_: function() {
     if (this.selectedBackground_ && this.selectedBackground_.id > -1) {
       this.ntpBackgroundProxy_.setBackground(this.selectedBackground_.id);
+    } else {
+      this.ntpBackgroundProxy_.clearBackground();
     }
     welcome.navigateToNextStep();
   },
diff --git a/chrome/browser/safe_browsing/advanced_protection_status_manager.h b/chrome/browser/safe_browsing/advanced_protection_status_manager.h
index aeaeda3..91e16a0 100644
--- a/chrome/browser/safe_browsing/advanced_protection_status_manager.h
+++ b/chrome/browser/safe_browsing/advanced_protection_status_manager.h
@@ -68,6 +68,10 @@
                            AlreadySignedInAndUnderAPIncognito);
   FRIEND_TEST_ALL_PREFIXES(AdvancedProtectionStatusManagerTest,
                            AlreadySignedInAndNotUnderAPIncognito);
+  FRIEND_TEST_ALL_PREFIXES(AdvancedProtectionStatusManagerTest,
+                           AdvancedProtectionDisabledAfterSignin);
+  FRIEND_TEST_ALL_PREFIXES(AdvancedProtectionStatusManagerTest,
+                           StartupAfterLongWaitRefreshesImmediately);
 
   void Initialize();
 
diff --git a/chrome/browser/safe_browsing/advanced_protection_status_manager_unittest.cc b/chrome/browser/safe_browsing/advanced_protection_status_manager_unittest.cc
index a0a3808..7433f2cf 100644
--- a/chrome/browser/safe_browsing/advanced_protection_status_manager_unittest.cc
+++ b/chrome/browser/safe_browsing/advanced_protection_status_manager_unittest.cc
@@ -4,6 +4,8 @@
 
 #include "chrome/browser/safe_browsing/advanced_protection_status_manager.h"
 
+#include <string>
+
 #include "base/bind.h"
 #include "base/test/metrics/histogram_tester.h"
 #include "chrome/browser/safe_browsing/advanced_protection_status_manager_factory.h"
@@ -51,9 +53,8 @@
     AccountInfo account_info = identity_test_env()->MakeAccountAvailable(email);
 
     account_info.is_under_advanced_protection = is_under_advanced_protection;
-    identity_test_env()->UpdateAccountInfoForAccount(account_info);
-
     identity_test_env()->SetPrimaryAccount(account_info.email);
+    identity_test_env()->UpdateAccountInfoForAccount(account_info);
 
     return account_info.account_id;
   }
@@ -360,4 +361,58 @@
   aps_manager.UnsubscribeFromSigninEvents();
 }
 
+TEST_F(AdvancedProtectionStatusManagerTest,
+       AdvancedProtectionDisabledAfterSignin) {
+  AdvancedProtectionStatusManager aps_manager(
+      testing_profile_.get(), base::TimeDelta() /*no min delay*/);
+  // There is no account, so the timer should not run at startup.
+  EXPECT_FALSE(aps_manager.IsRefreshScheduled());
+
+  std::string account_id =
+      SignIn("test@test.com", /* is_under_advanced_protection = */ true);
+  base::RunLoop().RunUntilIdle();
+
+  // Now that we've signed into Advanced Protection, we should have a scheduled
+  // refresh.
+  EXPECT_TRUE(aps_manager.is_under_advanced_protection());
+  EXPECT_TRUE(aps_manager.IsRefreshScheduled());
+
+  // Skip the 24 hour wait, and try to refresh the token now.
+  aps_manager.timer_.FireNow();
+  MakeOAuthTokenFetchSucceed(account_id,
+                             /* is_under_advanced_protection = */ false);
+
+  EXPECT_FALSE(aps_manager.is_under_advanced_protection());
+  EXPECT_FALSE(aps_manager.IsRefreshScheduled());
+
+  aps_manager.UnsubscribeFromSigninEvents();
+}
+
+TEST_F(AdvancedProtectionStatusManagerTest,
+       StartupAfterLongWaitRefreshesImmediately) {
+  std::string account_id =
+      SignIn("test@test.com", /* is_under_advanced_protection = */ true);
+  base::RunLoop().RunUntilIdle();
+
+  base::Time last_refresh_time =
+      base::Time::Now() - base::TimeDelta::FromDays(1);
+  testing_profile_->GetPrefs()->SetInt64(
+      prefs::kAdvancedProtectionLastRefreshInUs,
+      last_refresh_time.ToDeltaSinceWindowsEpoch().InMicroseconds());
+
+  AdvancedProtectionStatusManager aps_manager(
+      testing_profile_.get(), base::TimeDelta() /*no min delay*/);
+  ASSERT_FALSE(aps_manager.GetPrimaryAccountId().empty());
+  ASSERT_TRUE(aps_manager.is_under_advanced_protection());
+  EXPECT_TRUE(aps_manager.IsRefreshScheduled());
+
+  MakeOAuthTokenFetchSucceed(account_id,
+                             /* is_under_advanced_protection = */ false);
+
+  EXPECT_FALSE(aps_manager.is_under_advanced_protection());
+  EXPECT_FALSE(aps_manager.IsRefreshScheduled());
+
+  aps_manager.UnsubscribeFromSigninEvents();
+}
+
 }  // namespace safe_browsing
diff --git a/chrome/browser/search/instant_service.cc b/chrome/browser/search/instant_service.cc
index 3179fb4..970a996 100644
--- a/chrome/browser/search/instant_service.cc
+++ b/chrome/browser/search/instant_service.cc
@@ -5,6 +5,7 @@
 #include "chrome/browser/search/instant_service.h"
 
 #include <stddef.h>
+#include <string>
 
 #include "base/bind.h"
 #include "base/files/file_util.h"
@@ -328,11 +329,6 @@
 }
 
 void InstantService::UpdateThemeInfo() {
-  // Initialize |theme_info_| if necessary.
-  if (!theme_info_) {
-    BuildThemeInfo();
-  }
-
   ApplyOrResetCustomBackgroundThemeInfo();
 
   NotifyAboutThemeInfo();
@@ -407,6 +403,12 @@
                      weak_ptr_factory_.GetWeakPtr()));
 }
 
+ThemeBackgroundInfo* InstantService::GetInitializedThemeInfo() {
+  if (!theme_info_)
+    BuildThemeInfo();
+  return theme_info_.get();
+}
+
 void InstantService::SetDarkModeThemeForTesting(ui::NativeTheme* theme) {
   CreateDarkModeObserver(theme);
 }
@@ -632,7 +634,7 @@
     std::string time_string = std::to_string(base::Time::Now().ToTimeT());
     std::string local_string(chrome::kChromeSearchLocalNtpBackgroundUrl);
     GURL timestamped_url(local_string + "?ts=" + time_string);
-    theme_info_->custom_background_url = timestamped_url;
+    GetInitializedThemeInfo()->custom_background_url = timestamped_url;
     return;
   }
 
@@ -660,16 +662,16 @@
       background_info->FindKey(kNtpCustomBackgroundAttributionLine2);
   const base::Value* attribution_action_url =
       background_info->FindKey(kNtpCustomBackgroundAttributionActionURL);
-
-  theme_info_->custom_background_url = custom_background_url;
+  ThemeBackgroundInfo* theme_info = GetInitializedThemeInfo();
+  theme_info->custom_background_url = custom_background_url;
 
   if (attribution_line_1) {
-    theme_info_->custom_background_attribution_line_1 =
+    theme_info->custom_background_attribution_line_1 =
         background_info->FindKey(kNtpCustomBackgroundAttributionLine1)
             ->GetString();
   }
   if (attribution_line_2) {
-    theme_info_->custom_background_attribution_line_2 =
+    theme_info->custom_background_attribution_line_2 =
         background_info->FindKey(kNtpCustomBackgroundAttributionLine2)
             ->GetString();
   }
@@ -679,9 +681,9 @@
             ->GetString());
 
     if (!action_url.SchemeIsCryptographic()) {
-      theme_info_->custom_background_attribution_action_url = GURL();
+      theme_info->custom_background_attribution_action_url = GURL();
     } else {
-      theme_info_->custom_background_attribution_action_url = action_url;
+      theme_info->custom_background_attribution_action_url = action_url;
     }
   }
 }
@@ -694,10 +696,11 @@
 }
 
 void InstantService::FallbackToDefaultThemeInfo() {
-  theme_info_->custom_background_url = GURL();
-  theme_info_->custom_background_attribution_line_1 = std::string();
-  theme_info_->custom_background_attribution_line_2 = std::string();
-  theme_info_->custom_background_attribution_action_url = GURL();
+  ThemeBackgroundInfo* theme_info = GetInitializedThemeInfo();
+  theme_info->custom_background_url = GURL();
+  theme_info->custom_background_attribution_line_1 = std::string();
+  theme_info->custom_background_attribution_line_2 = std::string();
+  theme_info->custom_background_attribution_action_url = GURL();
 }
 
 bool InstantService::IsCustomBackgroundSet() {
diff --git a/chrome/browser/search/instant_service.h b/chrome/browser/search/instant_service.h
index 7bf4bdd..0359f74 100644
--- a/chrome/browser/search/instant_service.h
+++ b/chrome/browser/search/instant_service.h
@@ -134,8 +134,8 @@
   // Invoked when a user selected the "Upload an image" option on the NTP.
   void SelectLocalBackgroundImage(const base::FilePath& path);
 
-  // Used for testing.
-  ThemeBackgroundInfo* GetThemeInfoForTesting() { return theme_info_.get(); }
+  // Getter for |theme_info_| that will also initialize it if necessary.
+  ThemeBackgroundInfo* GetInitializedThemeInfo();
 
   // Used for testing.
   void SetDarkModeThemeForTesting(ui::NativeTheme* theme);
@@ -155,6 +155,7 @@
   FRIEND_TEST_ALL_PREFIXES(InstantExtendedTest, ProcessIsolation);
   FRIEND_TEST_ALL_PREFIXES(InstantServiceTest, DeleteThumbnailDataIfExists);
   FRIEND_TEST_ALL_PREFIXES(InstantServiceTest, GetNTPTileSuggestion);
+  FRIEND_TEST_ALL_PREFIXES(InstantServiceTest, TestNoThemeInfo);
 
   // KeyedService:
   void Shutdown() override;
diff --git a/chrome/browser/search/instant_service_unittest.cc b/chrome/browser/search/instant_service_unittest.cc
index 5f2074f8..0543ceb 100644
--- a/chrome/browser/search/instant_service_unittest.cc
+++ b/chrome/browser/search/instant_service_unittest.cc
@@ -121,7 +121,7 @@
   instant_service_->AddValidBackdropUrlForTesting(kUrl);
   instant_service_->SetCustomBackgroundURL(kUrl);
 
-  ThemeBackgroundInfo* theme_info = instant_service_->GetThemeInfoForTesting();
+  ThemeBackgroundInfo* theme_info = instant_service_->GetInitializedThemeInfo();
   EXPECT_EQ(kUrl, theme_info->custom_background_url);
   EXPECT_TRUE(instant_service_->IsCustomBackgroundSet());
 }
@@ -133,12 +133,12 @@
   instant_service_->AddValidBackdropUrlForTesting(kValidUrl);
   instant_service_->SetCustomBackgroundURL(kValidUrl);
 
-  ThemeBackgroundInfo* theme_info = instant_service_->GetThemeInfoForTesting();
+  ThemeBackgroundInfo* theme_info = instant_service_->GetInitializedThemeInfo();
   EXPECT_EQ(kValidUrl.spec(), theme_info->custom_background_url.spec());
 
   instant_service_->SetCustomBackgroundURL(kInvalidUrl);
 
-  theme_info = instant_service_->GetThemeInfoForTesting();
+  theme_info = instant_service_->GetInitializedThemeInfo();
   EXPECT_EQ(std::string(), theme_info->custom_background_url.spec());
   EXPECT_FALSE(instant_service_->IsCustomBackgroundSet());
 }
@@ -153,7 +153,7 @@
   instant_service_->SetCustomBackgroundURLWithAttributions(
       kUrl, kAttributionLine1, kAttributionLine2, kActionUrl);
 
-  ThemeBackgroundInfo* theme_info = instant_service_->GetThemeInfoForTesting();
+  ThemeBackgroundInfo* theme_info = instant_service_->GetInitializedThemeInfo();
   EXPECT_EQ(kUrl, theme_info->custom_background_url);
   EXPECT_EQ(kAttributionLine1,
             theme_info->custom_background_attribution_line_1);
@@ -175,7 +175,7 @@
   instant_service_->SetCustomBackgroundURLWithAttributions(
       kUrl, kAttributionLine1, kAttributionLine2, kActionUrl);
 
-  ThemeBackgroundInfo* theme_info = instant_service_->GetThemeInfoForTesting();
+  ThemeBackgroundInfo* theme_info = instant_service_->GetInitializedThemeInfo();
   EXPECT_EQ(kUrl, theme_info->custom_background_url);
   EXPECT_EQ(kAttributionLine1,
             theme_info->custom_background_attribution_line_1);
@@ -187,7 +187,7 @@
   SetUserSelectedDefaultSearchProvider("https://www.search.com");
   instant_service_->UpdateThemeInfo();
 
-  theme_info = instant_service_->GetThemeInfoForTesting();
+  theme_info = instant_service_->GetInitializedThemeInfo();
   EXPECT_EQ(GURL(), theme_info->custom_background_url);
   EXPECT_EQ(std::string(), theme_info->custom_background_attribution_line_1);
   EXPECT_EQ(std::string(), theme_info->custom_background_attribution_line_2);
@@ -197,7 +197,7 @@
   SetUserSelectedDefaultSearchProvider("{google:baseURL}");
   instant_service_->UpdateThemeInfo();
 
-  theme_info = instant_service_->GetThemeInfoForTesting();
+  theme_info = instant_service_->GetInitializedThemeInfo();
   EXPECT_EQ(GURL(), theme_info->custom_background_url);
   EXPECT_EQ(std::string(), theme_info->custom_background_attribution_line_1);
   EXPECT_EQ(std::string(), theme_info->custom_background_attribution_line_2);
@@ -287,7 +287,7 @@
   instant_service_->SetCustomBackgroundURLWithAttributions(
       kUrl, kAttributionLine1, kAttributionLine2, kHttpsActionUrl);
 
-  ThemeBackgroundInfo* theme_info = instant_service_->GetThemeInfoForTesting();
+  ThemeBackgroundInfo* theme_info = instant_service_->GetInitializedThemeInfo();
   EXPECT_EQ(kHttpsActionUrl,
             theme_info->custom_background_attribution_action_url);
   EXPECT_TRUE(instant_service_->IsCustomBackgroundSet());
@@ -295,14 +295,14 @@
   instant_service_->SetCustomBackgroundURLWithAttributions(
       kUrl, kAttributionLine1, kAttributionLine2, kHttpActionUrl);
 
-  theme_info = instant_service_->GetThemeInfoForTesting();
+  theme_info = instant_service_->GetInitializedThemeInfo();
   EXPECT_EQ(GURL(), theme_info->custom_background_attribution_action_url);
   EXPECT_TRUE(instant_service_->IsCustomBackgroundSet());
 
   instant_service_->SetCustomBackgroundURLWithAttributions(
       kUrl, kAttributionLine1, kAttributionLine2, kHttpsActionUrl);
 
-  theme_info = instant_service_->GetThemeInfoForTesting();
+  theme_info = instant_service_->GetInitializedThemeInfo();
   EXPECT_EQ(kHttpsActionUrl,
             theme_info->custom_background_attribution_action_url);
   EXPECT_TRUE(instant_service_->IsCustomBackgroundSet());
@@ -310,7 +310,7 @@
   instant_service_->SetCustomBackgroundURLWithAttributions(
       kUrl, kAttributionLine1, kAttributionLine2, GURL());
 
-  theme_info = instant_service_->GetThemeInfoForTesting();
+  theme_info = instant_service_->GetInitializedThemeInfo();
   EXPECT_EQ(GURL(), theme_info->custom_background_attribution_action_url);
   EXPECT_TRUE(instant_service_->IsCustomBackgroundSet());
 }
@@ -326,7 +326,7 @@
       prefs::kNtpCustomBackgroundDict,
       std::make_unique<base::Value>(GetBackgroundInfoAsDict(kUrlFoo)));
 
-  ThemeBackgroundInfo* theme_info = instant_service_->GetThemeInfoForTesting();
+  ThemeBackgroundInfo* theme_info = instant_service_->GetInitializedThemeInfo();
   EXPECT_EQ(kUrlFoo, theme_info->custom_background_url);
   EXPECT_TRUE(instant_service_->IsCustomBackgroundSet());
 
@@ -334,7 +334,7 @@
       prefs::kNtpCustomBackgroundDict,
       std::make_unique<base::Value>(GetBackgroundInfoAsDict(kUrlBar)));
 
-  theme_info = instant_service_->GetThemeInfoForTesting();
+  theme_info = instant_service_->GetInitializedThemeInfo();
   EXPECT_EQ(kUrlBar, theme_info->custom_background_url);
   EXPECT_EQ(false,
             pref_service->GetBoolean(prefs::kNtpCustomBackgroundLocalToDevice));
@@ -357,7 +357,7 @@
   instant_service_->SelectLocalBackgroundImage(path);
   thread_bundle()->RunUntilIdle();
 
-  ThemeBackgroundInfo* theme_info = instant_service_->GetThemeInfoForTesting();
+  ThemeBackgroundInfo* theme_info = instant_service_->GetInitializedThemeInfo();
   EXPECT_TRUE(base::StartsWith(theme_info->custom_background_url.spec(),
                                chrome::kChromeSearchLocalNtpBackgroundUrl,
                                base::CompareCase::SENSITIVE));
@@ -391,7 +391,7 @@
       std::make_unique<base::Value>(GetBackgroundInfoAsDict(kUrl)));
   thread_bundle()->RunUntilIdle();
 
-  ThemeBackgroundInfo* theme_info = instant_service_->GetThemeInfoForTesting();
+  ThemeBackgroundInfo* theme_info = instant_service_->GetInitializedThemeInfo();
   EXPECT_EQ(kUrl, theme_info->custom_background_url);
   EXPECT_FALSE(
       pref_service->GetBoolean(prefs::kNtpCustomBackgroundLocalToDevice));
@@ -409,26 +409,37 @@
   instant_service_->AddValidBackdropUrlForTesting(kBackdropUrl2);
 
   instant_service_->SetCustomBackgroundURL(kBackdropUrl1);
-  ThemeBackgroundInfo* theme_info = instant_service_->GetThemeInfoForTesting();
+  ThemeBackgroundInfo* theme_info = instant_service_->GetInitializedThemeInfo();
   EXPECT_EQ(kBackdropUrl1, theme_info->custom_background_url);
   EXPECT_TRUE(instant_service_->IsCustomBackgroundSet());
 
   instant_service_->SetCustomBackgroundURL(kNonBackdropUrl1);
-  theme_info = instant_service_->GetThemeInfoForTesting();
+  theme_info = instant_service_->GetInitializedThemeInfo();
   EXPECT_EQ(GURL(), theme_info->custom_background_url);
   EXPECT_FALSE(instant_service_->IsCustomBackgroundSet());
 
   instant_service_->SetCustomBackgroundURL(kBackdropUrl2);
-  theme_info = instant_service_->GetThemeInfoForTesting();
+  theme_info = instant_service_->GetInitializedThemeInfo();
   EXPECT_EQ(kBackdropUrl2, theme_info->custom_background_url);
   EXPECT_TRUE(instant_service_->IsCustomBackgroundSet());
 
   instant_service_->SetCustomBackgroundURL(kNonBackdropUrl2);
-  theme_info = instant_service_->GetThemeInfoForTesting();
+  theme_info = instant_service_->GetInitializedThemeInfo();
   EXPECT_EQ(GURL(), theme_info->custom_background_url);
   EXPECT_FALSE(instant_service_->IsCustomBackgroundSet());
 }
 
+TEST_F(InstantServiceTest, TestNoThemeInfo) {
+  instant_service_->theme_info_ = nullptr;
+  EXPECT_NE(nullptr, instant_service_->GetInitializedThemeInfo());
+
+  instant_service_->theme_info_ = nullptr;
+  // As |FallbackToDefaultThemeInfo| uses |theme_info_| it should initialize it
+  // otherwise the test should crash.
+  instant_service_->FallbackToDefaultThemeInfo();
+  EXPECT_NE(nullptr, instant_service_->theme_info_);
+}
+
 class InstantServiceThemeTest : public InstantServiceTest {
  public:
   InstantServiceThemeTest() {}
diff --git a/chrome/browser/search/local_ntp_source.cc b/chrome/browser/search/local_ntp_source.cc
index 70906543..cc26a4a 100644
--- a/chrome/browser/search/local_ntp_source.cc
+++ b/chrome/browser/search/local_ntp_source.cc
@@ -109,7 +109,7 @@
 } kResources[] = {
     {"animations.css", IDR_LOCAL_NTP_ANIMATIONS_CSS, "text/css"},
     {"animations.js", IDR_LOCAL_NTP_ANIMATIONS_JS, "application/javascript"},
-    {"constants.css", IDR_LOCAL_NTP_CONSTANTS_CSS, "text/css"},
+    {"local-ntp-common.css", IDR_LOCAL_NTP_COMMON_CSS, "text/css"},
     {"custom-backgrounds.css", IDR_LOCAL_NTP_CUSTOM_BACKGROUNDS_CSS,
      "text/css"},
     {"custom-backgrounds.js", IDR_LOCAL_NTP_CUSTOM_BACKGROUNDS_JS,
diff --git a/chrome/browser/search/most_visited_iframe_source.cc b/chrome/browser/search/most_visited_iframe_source.cc
index eda19c26..0e118472 100644
--- a/chrome/browser/search/most_visited_iframe_source.cc
+++ b/chrome/browser/search/most_visited_iframe_source.cc
@@ -39,7 +39,7 @@
 // Used in the single-iframe version and the edit custom links dialog iframe.
 const char kAnimationsCSSPath[] = "/animations.css";
 const char kAnimationsJSPath[] = "/animations.js";
-const char kConstantsCSSPath[] = "/constants.css";
+const char kLocalNTPCommonCSSPath[] = "/local-ntp-common.css";
 const char kLocalNTPUtilsJSPath[] = "/utils.js";
 
 }  // namespace
@@ -105,8 +105,8 @@
     SendResource(IDR_CUSTOM_LINKS_ADD_WHITE_SVG, callback);
   } else if (path == kEditMenuSvgPath) {
     SendResource(IDR_CUSTOM_LINKS_EDIT_MENU_SVG, callback);
-  } else if (path == kConstantsCSSPath) {
-    SendResource(IDR_LOCAL_NTP_CONSTANTS_CSS, callback);
+  } else if (path == kLocalNTPCommonCSSPath) {
+    SendResource(IDR_LOCAL_NTP_COMMON_CSS, callback);
   } else if (path == kAnimationsCSSPath) {
     SendResource(IDR_LOCAL_NTP_ANIMATIONS_CSS, callback);
   } else if (path == kAnimationsJSPath) {
@@ -125,6 +125,6 @@
          path == kCommonCSSPath || path == kEditHTMLPath ||
          path == kEditCSSPath || path == kEditJSPath || path == kAddSvgPath ||
          path == kAddWhiteSvgPath || path == kEditMenuSvgPath ||
-         path == kConstantsCSSPath || path == kAnimationsCSSPath ||
+         path == kLocalNTPCommonCSSPath || path == kAnimationsCSSPath ||
          path == kAnimationsJSPath || path == kLocalNTPUtilsJSPath;
 }
diff --git a/chrome/browser/send_tab_to_self/desktop_notification_handler.cc b/chrome/browser/send_tab_to_self/desktop_notification_handler.cc
new file mode 100644
index 0000000..20d6cb3
--- /dev/null
+++ b/chrome/browser/send_tab_to_self/desktop_notification_handler.cc
@@ -0,0 +1,98 @@
+// 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/send_tab_to_self/desktop_notification_handler.h"
+
+#include <string>
+#include <utility>
+
+#include "base/strings/utf_string_conversions.h"
+#include "build/build_config.h"
+#include "chrome/browser/notifications/notification_display_service.h"
+#include "chrome/browser/notifications/notification_display_service_factory.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/sync/send_tab_to_self_sync_service_factory.h"
+#include "chrome/browser/ui/browser_navigator.h"
+#include "chrome/browser/ui/browser_navigator_params.h"
+#include "components/send_tab_to_self/send_tab_to_self_entry.h"
+#include "components/send_tab_to_self/send_tab_to_self_model.h"
+#include "components/send_tab_to_self/send_tab_to_self_sync_service.h"
+#include "ui/base/l10n/l10n_util.h"
+#include "ui/base/page_transition_types.h"
+#include "ui/base/resource/resource_bundle.h"
+#include "ui/gfx/image/image.h"
+#include "ui/message_center/public/cpp/notification.h"
+#include "ui/strings/grit/ui_strings.h"
+
+namespace send_tab_to_self {
+
+DesktopNotificationHandler::DesktopNotificationHandler(Profile* profile)
+    : profile_(profile) {}
+
+DesktopNotificationHandler::~DesktopNotificationHandler() = default;
+
+void DesktopNotificationHandler::DisplayNewEntry(
+    const SendTabToSelfEntry* entry) {
+  const base::string16& device_info =
+      l10n_util::GetStringUTF16(
+          IDS_MESSAGE_NOTIFICATION_SEND_TAB_TO_SELF_DEVICE_INFO_PREFIX) +
+      base::UTF8ToUTF16(" " + entry->GetDeviceName());
+  const gfx::Image notification_icon = gfx::Image();
+  const GURL& url = entry->GetURL();
+  message_center::RichNotificationData optional_fields;
+  optional_fields.settings_button_handler =
+      message_center::SettingsButtonHandler::NONE;
+
+  // Declare a notification
+  message_center::Notification notification(
+      message_center::NOTIFICATION_TYPE_SIMPLE, entry->GetGUID(), device_info,
+      base::UTF8ToUTF16(entry->GetTitle()), notification_icon,
+      base::UTF8ToUTF16(url.host()), url, message_center::NotifierId(url),
+      optional_fields, nullptr);
+
+  NotificationDisplayServiceFactory::GetForProfile(profile_)->Display(
+      NotificationHandler::Type::SEND_TAB_TO_SELF, notification);
+}
+
+void DesktopNotificationHandler::DismissEntry(const SendTabToSelfEntry* entry) {
+  const std::string& guid = entry->GetGUID();
+  NotificationDisplayServiceFactory::GetForProfile(profile_)->Close(
+      NotificationHandler::Type::SEND_TAB_TO_SELF, guid);
+}
+
+void DesktopNotificationHandler::OnClose(Profile* profile,
+                                         const GURL& origin,
+                                         const std::string& notification_id,
+                                         bool by_user,
+                                         base::OnceClosure completed_closure) {
+  SendTabToSelfSyncServiceFactory::GetForProfile(profile)
+      ->GetSendTabToSelfModel()
+      ->DismissEntry(notification_id);
+  std::move(completed_closure).Run();
+}
+
+void DesktopNotificationHandler::OnClick(
+    Profile* profile,
+    const GURL& origin,
+    const std::string& notification_id,
+    const base::Optional<int>& action_index,
+    const base::Optional<base::string16>& reply,
+    base::OnceClosure completed_closure) {
+  // Launch a new tab for the notification's |origin|,
+  // and close the activated notification.
+  NavigateParams params(profile, origin, ui::PAGE_TRANSITION_LINK);
+  params.disposition = WindowOpenDisposition::NEW_FOREGROUND_TAB;
+  params.window_action = NavigateParams::SHOW_WINDOW;
+  Navigate(&params);
+  NotificationDisplayServiceFactory::GetForProfile(profile)->Close(
+      NotificationHandler::Type::SEND_TAB_TO_SELF, notification_id);
+
+  SendTabToSelfSyncServiceFactory::GetForProfile(profile)
+      ->GetSendTabToSelfModel()
+      ->DeleteEntry(notification_id);
+
+  std::move(completed_closure).Run();
+}
+
+}  // namespace send_tab_to_self
diff --git a/chrome/browser/send_tab_to_self/desktop_notification_handler.h b/chrome/browser/send_tab_to_self/desktop_notification_handler.h
new file mode 100644
index 0000000..40f4449
--- /dev/null
+++ b/chrome/browser/send_tab_to_self/desktop_notification_handler.h
@@ -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.
+
+#ifndef CHROME_BROWSER_SEND_TAB_TO_SELF_DESKTOP_NOTIFICATION_HANDLER_H_
+#define CHROME_BROWSER_SEND_TAB_TO_SELF_DESKTOP_NOTIFICATION_HANDLER_H_
+
+#include <string>
+
+#include "chrome/browser/notifications/notification_handler.h"
+#include "chrome/browser/send_tab_to_self/receiving_ui_handler.h"
+
+class Profile;
+
+namespace send_tab_to_self {
+
+class SendTabToSelfEntry;
+
+// Handler for desktop notifications shown by SendTabToSelf.
+// Will only be used on desktop platform.
+// Will be created and owned by the NativeNotificationDisplayService.
+class DesktopNotificationHandler : public NotificationHandler,
+                                   public ReceivingUiHandler {
+ public:
+  explicit DesktopNotificationHandler(Profile* profile);
+  ~DesktopNotificationHandler() override;
+
+  // ReceivingUiHandler implementation.
+  void DisplayNewEntry(const SendTabToSelfEntry* entry) override;
+  void DismissEntry(const SendTabToSelfEntry* entry) override;
+
+  // NotificationHandler implementation.
+  void OnClose(Profile* profile,
+               const GURL& origin,
+               const std::string& notification_id,
+               bool by_user,
+               base::OnceClosure completed_closure) override;
+
+  void OnClick(Profile* profile,
+               const GURL& origin,
+               const std::string& notification_id,
+               const base::Optional<int>& action_index,
+               const base::Optional<base::string16>& reply,
+               base::OnceClosure completed_closure) override;
+
+ protected:
+  Profile* profile_;
+  DISALLOW_COPY_AND_ASSIGN(DesktopNotificationHandler);
+};
+
+}  // namespace send_tab_to_self
+
+#endif  // CHROME_BROWSER_SEND_TAB_TO_SELF_DESKTOP_NOTIFICATION_HANDLER_H_
diff --git a/chrome/browser/send_tab_to_self/receiving_ui_handler_registry.cc b/chrome/browser/send_tab_to_self/receiving_ui_handler_registry.cc
index 3317e579..a37a717 100644
--- a/chrome/browser/send_tab_to_self/receiving_ui_handler_registry.cc
+++ b/chrome/browser/send_tab_to_self/receiving_ui_handler_registry.cc
@@ -11,6 +11,10 @@
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/send_tab_to_self/receiving_ui_handler.h"
 
+#if defined(OS_LINUX) || defined(OS_MACOSX)
+#include "chrome/browser/send_tab_to_self/desktop_notification_handler.h"
+#endif
+
 namespace send_tab_to_self {
 
 ReceivingUiHandlerRegistry::ReceivingUiHandlerRegistry() {}
@@ -24,6 +28,10 @@
 // Instantiates all the handlers relevant to this platform.
 void ReceivingUiHandlerRegistry::InstantiatePlatformSpecificHandlers(
     Profile* profile) {
+#if defined(OS_LINUX) || defined(OS_MACOSX)
+  applicable_handlers_.push_back(
+      std::make_unique<send_tab_to_self::DesktopNotificationHandler>(profile));
+#endif
 }
 
 std::vector<std::unique_ptr<ReceivingUiHandler>>&
diff --git a/chrome/browser/ssl/chrome_ssl_host_state_delegate.cc b/chrome/browser/ssl/chrome_ssl_host_state_delegate.cc
index 353eb1e..6706659 100644
--- a/chrome/browser/ssl/chrome_ssl_host_state_delegate.cc
+++ b/chrome/browser/ssl/chrome_ssl_host_state_delegate.cc
@@ -6,6 +6,7 @@
 
 #include <stdint.h>
 
+#include <functional>
 #include <set>
 #include <string>
 #include <utility>
@@ -320,7 +321,7 @@
   HostContentSettingsMap::PatternSourcePredicate pattern_filter;
   if (!host_filter.is_null()) {
     pattern_filter =
-        base::Bind(&HostFilterToPatternFilter, base::ConstRef(host_filter));
+        base::Bind(&HostFilterToPatternFilter, std::cref(host_filter));
   }
 
   HostContentSettingsMapFactory::GetForProfile(profile_)
diff --git a/chrome/browser/supervised_user/child_accounts/child_account_service.cc b/chrome/browser/supervised_user/child_accounts/child_account_service.cc
index f5db39f..7adc57d 100644
--- a/chrome/browser/supervised_user/child_accounts/child_account_service.cc
+++ b/chrome/browser/supervised_user/child_accounts/child_account_service.cc
@@ -101,8 +101,12 @@
 
   // If we're already signed in, check the account immediately just to be sure.
   // (We might have missed an update before registering as an observer.)
-  if (identity_manager_->HasPrimaryAccount())
-    OnExtendedAccountInfoUpdated(identity_manager_->GetPrimaryAccountInfo());
+  base::Optional<AccountInfo> primary_account_info =
+      identity_manager_->FindExtendedAccountInfoForAccount(
+          identity_manager_->GetPrimaryAccountInfo());
+
+  if (primary_account_info.has_value())
+    OnExtendedAccountInfoUpdated(primary_account_info.value());
 }
 
 bool ChildAccountService::IsChildAccountStatusKnown() {
diff --git a/chrome/browser/sync/test/integration/bookmarks_helper.cc b/chrome/browser/sync/test/integration/bookmarks_helper.cc
index 92b5a1a4..2d8dad4 100644
--- a/chrome/browser/sync/test/integration/bookmarks_helper.cc
+++ b/chrome/browser/sync/test/integration/bookmarks_helper.cc
@@ -6,6 +6,7 @@
 
 #include <stddef.h>
 
+#include <functional>
 #include <memory>
 #include <set>
 #include <vector>
@@ -1089,6 +1090,6 @@
                                          int expected_count)
     : AwaitMatchStatusChangeChecker(base::Bind(BookmarkCountsByUrlMatch,
                                                profile,
-                                               base::ConstRef(url),
+                                               std::cref(url),
                                                expected_count),
                                     "Bookmark URL counts match.") {}
diff --git a/chrome/browser/sync/test/integration/sync_auth_test.cc b/chrome/browser/sync/test/integration/sync_auth_test.cc
index 95afee4..f7eff6e 100644
--- a/chrome/browser/sync/test/integration/sync_auth_test.cc
+++ b/chrome/browser/sync/test/integration/sync_auth_test.cc
@@ -329,13 +329,12 @@
   // Enter the "Sync paused" state.
   GetClient(0)->EnterSyncPausedStateForPrimaryAccount();
   ASSERT_TRUE(GetSyncService(0)->GetAuthError().IsPersistentError());
-  ASSERT_TRUE(AttemptToTriggerAuthError());
 
-  // While Sync itself is still considered active, the active data types should
-  // now be empty.
-  EXPECT_TRUE(GetSyncService(0)->IsSyncFeatureActive());
+  // Sync should have shut itself down.
   EXPECT_EQ(GetSyncService(0)->GetTransportState(),
-            syncer::SyncService::TransportState::ACTIVE);
+            syncer::SyncService::TransportState::DISABLED);
+  EXPECT_TRUE(GetSyncService(0)->HasDisableReason(
+      syncer::SyncService::DISABLE_REASON_PAUSED));
   EXPECT_TRUE(GetSyncService(0)->GetActiveDataTypes().Empty());
 
   // Clear the "Sync paused" state again.
@@ -344,8 +343,10 @@
   // access token again, so wait for that to happen.
   NoAuthErrorChecker(GetSyncService(0)).Wait();
   ASSERT_FALSE(GetSyncService(0)->GetAuthError().IsPersistentError());
+  // Once the auth error is gone, wait for Sync to start up again.
+  GetClient(0)->AwaitSyncSetupCompletion();
 
-  // Now the active data types should be back.
+  // Now Sync should be active again.
   EXPECT_TRUE(GetSyncService(0)->IsSyncFeatureActive());
   EXPECT_EQ(GetSyncService(0)->GetTransportState(),
             syncer::SyncService::TransportState::ACTIVE);
diff --git a/chrome/browser/ui/BUILD.gn b/chrome/browser/ui/BUILD.gn
index 39288f3..17f8b7b 100644
--- a/chrome/browser/ui/BUILD.gn
+++ b/chrome/browser/ui/BUILD.gn
@@ -454,6 +454,7 @@
     "//components/renderer_context_menu",
     "//components/resources",
     "//components/safe_browsing:features",
+    "//components/safe_browsing/common:common",
     "//components/safe_browsing/common:safe_browsing_prefs",
     "//components/safe_browsing/db:database_manager",
     "//components/safe_browsing/db:util",
diff --git a/chrome/browser/ui/app_list/app_launch_event_logger.cc b/chrome/browser/ui/app_list/app_launch_event_logger.cc
index bf58860..de92e2a4 100644
--- a/chrome/browser/ui/app_list/app_launch_event_logger.cc
+++ b/chrome/browser/ui/app_list/app_launch_event_logger.cc
@@ -128,13 +128,22 @@
 
 AppLaunchEventLogger::~AppLaunchEventLogger() {}
 
-void AppLaunchEventLogger::OnSuggestionChipClicked(const std::string& id,
-                                                   int suggestion_index) {
+void AppLaunchEventLogger::OnSuggestionChipOrSearchBoxClicked(
+    const std::string& id,
+    int suggestion_index,
+    int launched_from) {
   if (!base::FeatureList::IsEnabled(kUkmAppLaunchEventLogging)) {
     return;
   }
   AppLaunchEvent event;
-  event.set_launched_from(AppLaunchEvent_LaunchedFrom_SUGGESTED);
+  AppLaunchEvent_LaunchedFrom from(
+      static_cast<AppLaunchEvent_LaunchedFrom>(launched_from));
+  if (from == AppLaunchEvent_LaunchedFrom_SUGGESTION_CHIP ||
+      from == AppLaunchEvent_LaunchedFrom_SEARCH_BOX) {
+    event.set_launched_from(from);
+  } else {
+    return;
+  }
   event.set_app_id(RemoveScheme(id));
   event.set_index(suggestion_index);
   EnforceLoggingPolicy();
@@ -487,7 +496,9 @@
   all_clicks_last_24_hours_->Log(duration);
 
   if (app_launch_event.launched_from() ==
-      AppLaunchEvent_LaunchedFrom_SUGGESTED) {
+          AppLaunchEvent_LaunchedFrom_SUGGESTION_CHIP ||
+      app_launch_event.launched_from() ==
+          AppLaunchEvent_LaunchedFrom_SEARCH_BOX) {
     app_launch.SetPositionIndex(app_launch_event.index());
   }
   app_launch.SetAppType(app->second.app_type())
diff --git a/chrome/browser/ui/app_list/app_launch_event_logger.h b/chrome/browser/ui/app_list/app_launch_event_logger.h
index 15589693..abfae3b9 100644
--- a/chrome/browser/ui/app_list/app_launch_event_logger.h
+++ b/chrome/browser/ui/app_list/app_launch_event_logger.h
@@ -47,10 +47,12 @@
  public:
   AppLaunchEventLogger();
   ~AppLaunchEventLogger();
-  // Processes a click on an app in the suggestion chip and logs the resulting
-  // metrics in UKM. This method calls EnforceLoggingPolicy() to ensure the
-  // logging policy is complied with.
-  void OnSuggestionChipClicked(const std::string& id, int suggestion_index);
+  // Processes a click on an app in the suggestion chip or search box and logs
+  // the resulting metrics in UKM. This method calls EnforceLoggingPolicy() to
+  // ensure the logging policy is complied with.
+  void OnSuggestionChipOrSearchBoxClicked(const std::string& id,
+                                          int suggestion_index,
+                                          int launched_from);
   // Processes a click on an app located in the grid of apps in the launcher and
   // logs the resulting metrics in UKM. This method calls EnforceLoggingPolicy()
   // to ensure the logging policy is complied with.
diff --git a/chrome/browser/ui/app_list/app_launch_event_logger.proto b/chrome/browser/ui/app_list/app_launch_event_logger.proto
index 8efd18d..878315c 100644
--- a/chrome/browser/ui/app_list/app_launch_event_logger.proto
+++ b/chrome/browser/ui/app_list/app_launch_event_logger.proto
@@ -11,8 +11,9 @@
 message AppLaunchEvent {
   enum LaunchedFrom {
     GRID = 1;
-    SUGGESTED = 2;
+    SUGGESTION_CHIP = 2;
     SHELF = 3;
+    SEARCH_BOX = 4;
   }
   // The component that the user launched the app from.
   optional LaunchedFrom launched_from = 1;
diff --git a/chrome/browser/ui/app_list/app_launch_event_logger_unittest.cc b/chrome/browser/ui/app_list/app_launch_event_logger_unittest.cc
index 3419e29f..5bc3796 100644
--- a/chrome/browser/ui/app_list/app_launch_event_logger_unittest.cc
+++ b/chrome/browser/ui/app_list/app_launch_event_logger_unittest.cc
@@ -169,7 +169,8 @@
                                                 packages.get());
   app_launch_event_logger_.OnGridClicked(kPhotosPWAApp);
   app_launch_event_logger_.OnGridClicked(kMapsArcApp);
-  app_launch_event_logger_.OnSuggestionChipClicked(kPhotosPWAApp, 2);
+  app_launch_event_logger_.OnSuggestionChipOrSearchBoxClicked(kPhotosPWAApp, 3,
+                                                              2);
   app_launch_event_logger_.OnGridClicked(kPhotosPWAApp);
 
   scoped_task_environment_.RunUntilIdle();
@@ -226,7 +227,8 @@
 
   AppLaunchEventLogger app_launch_event_logger_;
   app_launch_event_logger_.SetAppDataForTesting(&registry, nullptr, nullptr);
-  app_launch_event_logger_.OnSuggestionChipClicked(kPhotosPWAApp, 2);
+  app_launch_event_logger_.OnSuggestionChipOrSearchBoxClicked(kPhotosPWAApp, 3,
+                                                              2);
 
   scoped_task_environment_.RunUntilIdle();
 
@@ -234,7 +236,31 @@
   ASSERT_EQ(1ul, entries.size());
   const auto* entry = entries.back();
   test_ukm_recorder_.ExpectEntrySourceHasUrl(entry, url);
-  test_ukm_recorder_.ExpectEntryMetric(entry, "PositionIndex", 2);
+  test_ukm_recorder_.ExpectEntryMetric(entry, "PositionIndex", 3);
+  test_ukm_recorder_.ExpectEntryMetric(entry, "LaunchedFrom", 2);
+}
+
+TEST_F(AppLaunchEventLoggerTest, CheckUkmCodeSearchBox) {
+  extensions::ExtensionRegistry registry(nullptr);
+  scoped_refptr<const extensions::Extension> extension =
+      extensions::ExtensionBuilder("test").SetID(kPhotosPWAApp).Build();
+  registry.AddEnabled(extension);
+
+  GURL url("https://photos.google.com/");
+
+  AppLaunchEventLogger app_launch_event_logger_;
+  app_launch_event_logger_.SetAppDataForTesting(&registry, nullptr, nullptr);
+  app_launch_event_logger_.OnSuggestionChipOrSearchBoxClicked(kPhotosPWAApp, 3,
+                                                              4);
+
+  scoped_task_environment_.RunUntilIdle();
+
+  const auto entries = test_ukm_recorder_.GetEntriesByName("AppListAppLaunch");
+  ASSERT_EQ(1ul, entries.size());
+  const auto* entry = entries.back();
+  test_ukm_recorder_.ExpectEntrySourceHasUrl(entry, url);
+  test_ukm_recorder_.ExpectEntryMetric(entry, "PositionIndex", 3);
+  test_ukm_recorder_.ExpectEntryMetric(entry, "LaunchedFrom", 4);
 }
 
 }  // namespace app_list
diff --git a/chrome/browser/ui/app_list/app_list_client_impl.cc b/chrome/browser/ui/app_list/app_list_client_impl.cc
index 25dffa6..67b6020 100644
--- a/chrome/browser/ui/app_list/app_list_client_impl.cc
+++ b/chrome/browser/ui/app_list/app_list_client_impl.cc
@@ -106,9 +106,12 @@
   }
 }
 
-void AppListClientImpl::LogSearchClick(const std::string& result_id,
-                                       int suggestion_index) {
-  app_launch_event_logger_.OnSuggestionChipClicked(result_id, suggestion_index);
+void AppListClientImpl::LogSearchClick(
+    const std::string& result_id,
+    int suggestion_index,
+    ash::mojom::AppListLaunchedFrom launched_from) {
+  app_launch_event_logger_.OnSuggestionChipOrSearchBoxClicked(
+      result_id, suggestion_index, static_cast<int>(launched_from));
 }
 
 void AppListClientImpl::InvokeSearchResultAction(const std::string& result_id,
diff --git a/chrome/browser/ui/app_list/app_list_client_impl.h b/chrome/browser/ui/app_list/app_list_client_impl.h
index 60078c1..03b9891d 100644
--- a/chrome/browser/ui/app_list/app_list_client_impl.h
+++ b/chrome/browser/ui/app_list/app_list_client_impl.h
@@ -50,7 +50,8 @@
   void StartSearch(const base::string16& trimmed_query) override;
   void OpenSearchResult(const std::string& result_id, int event_flags) override;
   void LogSearchClick(const std::string& result_id,
-                      int suggestion_index) override;
+                      int suggestion_index,
+                      ash::mojom::AppListLaunchedFrom launched_from) override;
   void InvokeSearchResultAction(const std::string& result_id,
                                 int action_index,
                                 int event_flags) override;
diff --git a/chrome/browser/ui/app_list/arc/arc_app_icon_loader.cc b/chrome/browser/ui/app_list/arc/arc_app_icon_loader.cc
index 09f691d..1388c44 100644
--- a/chrome/browser/ui/app_list/arc/arc_app_icon_loader.cc
+++ b/chrome/browser/ui/app_list/arc/arc_app_icon_loader.cc
@@ -58,7 +58,7 @@
       arc_prefs_->GetApp(app_id);
   if (app_info && app_info->suspended) {
     image =
-        gfx::ImageSkiaOperations::CreateHSLShiftedImage(image, {-1, 0, 0.75});
+        gfx::ImageSkiaOperations::CreateHSLShiftedImage(image, {-1, 0, 0.6});
   }
 
   delegate()->OnAppImageUpdated(app_id, image);
diff --git a/chrome/browser/ui/app_list/search/app_search_provider.cc b/chrome/browser/ui/app_list/search/app_search_provider.cc
index 1ecda2e..a81fa96 100644
--- a/chrome/browser/ui/app_list/search/app_search_provider.cc
+++ b/chrome/browser/ui/app_list/search/app_search_provider.cc
@@ -128,6 +128,20 @@
   return min + score * (max - min);
 }
 
+// Normalizes app IDs by removing any scheme prefix and trailing slash:
+// "arc://[id]/" to "[id]". This is necessary because apps launched from
+// different parts of the launcher have differently formatted IDs.
+std::string NormalizeID(const std::string& id) {
+  std::string app_id(id);
+  // No existing scheme names include the delimiter string "://".
+  std::size_t delimiter_index = app_id.find("://");
+  if (delimiter_index != std::string::npos)
+    app_id.erase(0, delimiter_index + 3);
+  if (!app_id.empty() && app_id.back() == '/')
+    app_id.pop_back();
+  return app_id;
+}
+
 }  // namespace
 
 namespace app_list {
@@ -676,7 +690,7 @@
 
 void AppSearchProvider::Train(const std::string& id, RankingItemType type) {
   if (type == RankingItemType::kApp)
-    ranker_->Train(id);
+    ranker_->Train(NormalizeID(id));
 }
 
 void AppSearchProvider::RefreshAppsAndUpdateResults() {
@@ -865,4 +879,8 @@
   }
 }
 
+std::string AppSearchProvider::NormalizeIDForTest(const std::string& id) {
+  return NormalizeID(id);
+}
+
 }  // namespace app_list
diff --git a/chrome/browser/ui/app_list/search/app_search_provider.h b/chrome/browser/ui/app_list/search/app_search_provider.h
index 550e3b4..e560e98 100644
--- a/chrome/browser/ui/app_list/search/app_search_provider.h
+++ b/chrome/browser/ui/app_list/search/app_search_provider.h
@@ -68,6 +68,8 @@
     return open_tabs_ui_delegate_for_testing_;
   }
 
+  static std::string NormalizeIDForTest(const std::string& id);
+
  private:
   void UpdateResults();
   void UpdateRecommendedResults(
diff --git a/chrome/browser/ui/app_list/search/arc/arc_app_reinstall_app_result.cc b/chrome/browser/ui/app_list/search/arc/arc_app_reinstall_app_result.cc
index 82b830f..8c8b425 100644
--- a/chrome/browser/ui/app_list/search/arc/arc_app_reinstall_app_result.cc
+++ b/chrome/browser/ui/app_list/search/arc/arc_app_reinstall_app_result.cc
@@ -25,20 +25,23 @@
 
 ArcAppReinstallAppResult::ArcAppReinstallAppResult(
     const arc::mojom::AppReinstallCandidatePtr& mojom_data,
-
-    const gfx::ImageSkia& skia_icon) {
+    const gfx::ImageSkia& skia_icon,
+    Observer* observer)
+    : observer_(observer) {
+  DCHECK(observer_);
   set_id(kPlayStoreAppUrlPrefix + mojom_data->package_name);
   SetResultType(ash::SearchResultType::kPlayStoreReinstallApp);
   SetTitle(base::UTF8ToUTF16(mojom_data->title));
   SetDisplayType(ash::SearchResultDisplayType::kRecommendation);
   set_relevance(kAppReinstallRelevance);
-
+  SetNotifyVisibilityChange(true);
   SetIcon(skia_icon);
   SetChipIcon(skia_icon);
   SetNotifyVisibilityChange(true);
 
-  if (mojom_data->star_rating != 0.0f)
+  if (mojom_data->star_rating != 0.0f) {
     SetRating(mojom_data->star_rating);
+  }
 }
 
 ArcAppReinstallAppResult::~ArcAppReinstallAppResult() = default;
@@ -46,6 +49,12 @@
 void ArcAppReinstallAppResult::Open(int /*event_flags*/) {
   RecordAction(base::UserMetricsAction("ArcAppReinstall_Clicked"));
   arc::LaunchPlayStoreWithUrl(id());
+  observer_->OnOpened(id());
+}
+
+void ArcAppReinstallAppResult::OnVisibilityChanged(bool visibility) {
+  ChromeSearchResult::OnVisibilityChanged(visibility);
+  observer_->OnVisibilityChanged(id(), visibility);
 }
 
 }  // namespace app_list
diff --git a/chrome/browser/ui/app_list/search/arc/arc_app_reinstall_app_result.h b/chrome/browser/ui/app_list/search/arc/arc_app_reinstall_app_result.h
index 40c58867..14ce37e 100644
--- a/chrome/browser/ui/app_list/search/arc/arc_app_reinstall_app_result.h
+++ b/chrome/browser/ui/app_list/search/arc/arc_app_reinstall_app_result.h
@@ -5,6 +5,8 @@
 #ifndef CHROME_BROWSER_UI_APP_LIST_SEARCH_ARC_ARC_APP_REINSTALL_APP_RESULT_H_
 #define CHROME_BROWSER_UI_APP_LIST_SEARCH_ARC_ARC_APP_REINSTALL_APP_RESULT_H_
 
+#include <string>
+
 #include "base/macros.h"
 #include "chrome/browser/ui/app_list/search/chrome_search_result.h"
 #include "components/arc/common/app.mojom.h"
@@ -17,15 +19,30 @@
 // store when Open is called.
 class ArcAppReinstallAppResult : public ChromeSearchResult {
  public:
+  class Observer {
+   public:
+    virtual void OnOpened(const std::string& package_id) = 0;
+    virtual void OnVisibilityChanged(const std::string& package_id,
+                                     bool visibility) = 0;
+
+   protected:
+    virtual ~Observer() = default;
+  };
+
   ArcAppReinstallAppResult(
       const arc::mojom::AppReinstallCandidatePtr& mojom_data,
-      const gfx::ImageSkia& skia_icon);
+      const gfx::ImageSkia& skia_icon,
+      ArcAppReinstallAppResult::Observer* observer);
   ~ArcAppReinstallAppResult() override;
 
-  // ArcAppResult:
+  // ChromeSearchResult:
   void Open(int event_flags) override;
+  void OnVisibilityChanged(bool visibility) override;
 
  private:
+  // Observer passed in constructor. not owned.
+  Observer* const observer_;
+
   DISALLOW_COPY_AND_ASSIGN(ArcAppReinstallAppResult);
 };
 
diff --git a/chrome/browser/ui/app_list/search/arc/arc_app_reinstall_search_provider.cc b/chrome/browser/ui/app_list/search/arc/arc_app_reinstall_search_provider.cc
index 11849675..1ba095b6 100644
--- a/chrome/browser/ui/app_list/search/arc/arc_app_reinstall_search_provider.cc
+++ b/chrome/browser/ui/app_list/search/arc/arc_app_reinstall_search_provider.cc
@@ -5,14 +5,12 @@
 #include "chrome/browser/ui/app_list/search/arc/arc_app_reinstall_search_provider.h"
 
 #include <algorithm>
-#include <memory>
-#include <string>
-#include <unordered_map>
 #include <unordered_set>
 #include <utility>
-#include <vector>
 
 #include "ash/public/cpp/app_list/app_list_config.h"
+#include "ash/public/cpp/app_list/app_list_features.h"
+#include "base/metrics/field_trial_params.h"
 #include "base/metrics/histogram_macros.h"
 #include "base/strings/strcat.h"
 #include "base/strings/string16.h"
@@ -25,14 +23,162 @@
 #include "chrome/browser/ui/app_list/search/common/url_icon_source.h"
 #include "components/arc/arc_bridge_service.h"
 #include "components/arc/arc_service_manager.h"
+#include "components/pref_registry/pref_registry_syncable.h"
+#include "components/prefs/pref_registry_simple.h"
+#include "components/prefs/scoped_user_pref_update.h"
 #include "extensions/grit/extensions_browser_resources.h"
 
 namespace {
 // Seconds in between refreshes;
-constexpr int64_t kRefreshSeconds = 1800;
+constexpr base::TimeDelta kRefresh = base::TimeDelta::FromMinutes(30);
+
 constexpr char kAppListLatency[] = "Apps.AppListRecommendedResponse.Latency";
 constexpr char kAppListCounts[] = "Apps.AppListRecommendedResponse.Count";
 
+// Fields for working with pref syncable state.
+
+// Overall dictionary to use for all arc app reinstall states.
+constexpr char kAppState[] = "arc_app_reinstall_state";
+
+// field name for install start time, as milliseconds since epoch
+constexpr char kInstallStartTime[] = "install_start_time";
+
+// field name for install time, as milliseconds since epoch
+constexpr char kInstallTime[] = "install_time";
+// field name for install opened time, as milliseconds since epoch
+constexpr char kOpenTime[] = "open_time";
+// field name for uninstalltime, as milliseconds since epoch.
+constexpr char kUninstallTime[] = "uninstall_time";
+
+// field name for latest impressiontime, as milliseconds since epoch.
+constexpr char kImpressionTime[] = "impression_time";
+// Number of impressions.
+constexpr char kImpressionCount[] = "impression_count";
+
+// If uninstalled in this time, do not recommend.
+constexpr base::FeatureParam<int> kUninstallGrace(
+    &app_list_features::kEnableAppReinstallZeroState,
+    "uninstall_time_hours",
+    24 * 90);
+
+// If install start triggered within this many days, do not recommend.
+constexpr base::FeatureParam<int> kInstallStartGrace(
+    &app_list_features::kEnableAppReinstallZeroState,
+    "install_start_hours",
+    24);
+
+// If install impression older than this age, reset impressions.
+constexpr base::FeatureParam<int> kResetImpressionGrace(
+    &app_list_features::kEnableAppReinstallZeroState,
+    "reset_impression_hours",
+    30 * 24);
+
+// Count an impression as new if it's more than this much away from the
+// previous.
+constexpr base::FeatureParam<int> kNewImpressionTime(
+    &app_list_features::kEnableAppReinstallZeroState,
+    "new_impression_seconds",
+    30);
+
+// Maximum number of impressions to show an item.
+constexpr base::FeatureParam<int> kImpressionLimit(
+    &app_list_features::kEnableAppReinstallZeroState,
+    "impression_count_limit",
+    5);
+
+// If a user has meaningfully interacted with this feature within this grace
+// period, do not show anything. If set to 0, ignored.
+constexpr base::FeatureParam<int> kInteractionGrace(
+    &app_list_features::kEnableAppReinstallZeroState,
+    "interaction_grace_hours",
+    0);
+
+void SetStateInt64(Profile* profile,
+                   const std::string& package_name,
+                   const std::string& key,
+                   const int64_t value) {
+  const std::string int64_str = base::NumberToString(value);
+  DictionaryPrefUpdate update(profile->GetPrefs(), kAppState);
+  base::DictionaryValue* const dictionary = update.Get();
+  base::Value* package_item =
+      dictionary->FindKeyOfType(package_name, base::Value::Type::DICTIONARY);
+  if (!package_item) {
+    package_item = dictionary->SetKey(
+        package_name, base::Value(base::Value::Type::DICTIONARY));
+  }
+
+  package_item->SetKey(key, base::Value(int64_str));
+}
+
+void UpdateStateRemoveKey(Profile* profile,
+                          const std::string& package_name,
+                          const std::string& key) {
+  DictionaryPrefUpdate update(profile->GetPrefs(), kAppState);
+  base::DictionaryValue* const dictionary = update.Get();
+  base::Value* package_item =
+      dictionary->FindKeyOfType(package_name, base::Value::Type::DICTIONARY);
+  if (!package_item) {
+    return;
+  }
+  package_item->RemoveKey(key);
+}
+
+void UpdateStateTime(Profile* profile,
+                     const std::string& package_name,
+                     const std::string& key) {
+  const int64_t timestamp =
+      base::Time::Now().ToDeltaSinceWindowsEpoch().InMilliseconds();
+  SetStateInt64(profile, package_name, key, timestamp);
+}
+
+bool GetStateInt64(Profile* profile,
+                   const std::string& package_name,
+                   const std::string& key,
+                   int64_t* value) {
+  const base::DictionaryValue* dictionary =
+      profile->GetPrefs()->GetDictionary(kAppState);
+  if (!dictionary)
+    return false;
+  const base::Value* package_item =
+      dictionary->FindKeyOfType(package_name, base::Value::Type::DICTIONARY);
+  if (!package_item)
+    return false;
+  const std::string* value_str = package_item->FindStringKey(key);
+  if (!value_str)
+    return false;
+
+  if (!base::StringToInt64(*value_str, value)) {
+    LOG(ERROR) << "Failed conversion " << *value_str;
+    return false;
+  }
+
+  return true;
+}
+
+bool GetStateTime(Profile* profile,
+                  const std::string& package_name,
+                  const std::string& key,
+                  base::TimeDelta* time_delta) {
+  int64_t value;
+  if (!GetStateInt64(profile, package_name, key, &value))
+    return false;
+
+  *time_delta = base::TimeDelta::FromMilliseconds(value);
+  return true;
+}
+
+bool GetKnownPackageNames(Profile* profile,
+                          std::unordered_set<std::string>* package_names) {
+  const base::DictionaryValue* dictionary =
+      profile->GetPrefs()->GetDictionary(kAppState);
+  for (const auto& it : dictionary->DictItems()) {
+    if (it.second.is_dict()) {
+      package_names->insert(it.first);
+    }
+  }
+  return true;
+}
+
 void RecordUmaResponseParseResult(arc::mojom::AppReinstallState result) {
   UMA_HISTOGRAM_ENUMERATION("Apps.AppListRecommendedResponse", result);
 }
@@ -72,8 +218,7 @@
   if (app_fetch_timer_->IsRunning())
     return;
 
-  app_fetch_timer_->Start(FROM_HERE,
-                          base::TimeDelta::FromSeconds(kRefreshSeconds), this,
+  app_fetch_timer_->Start(FROM_HERE, kRefresh, this,
                           &ArcAppReinstallSearchProvider::StartFetch);
   StartFetch();
 }
@@ -131,6 +276,25 @@
       loaded_value_.push_back(candidate.Clone());
     }
   }
+
+  // Update the dictionary to reset old impression counts.
+  const base::TimeDelta now = base::Time::Now().ToDeltaSinceWindowsEpoch();
+  // Remove stale impressions from state.
+  std::unordered_set<std::string> package_names;
+  GetKnownPackageNames(profile_, &package_names);
+  for (const std::string& package_name : package_names) {
+    base::TimeDelta latest_impression;
+    if (!GetStateTime(profile_, package_name, kImpressionTime,
+                      &latest_impression)) {
+      continue;
+    }
+    if (now - latest_impression >
+        base::TimeDelta::FromHours(kResetImpressionGrace.Get())) {
+      SetStateInt64(profile_, package_name, kImpressionCount, 0);
+      UpdateStateRemoveKey(profile_, package_name, kImpressionTime);
+    }
+  }
+
   UpdateResults();
 }
 
@@ -151,38 +315,45 @@
   std::unordered_set<std::string> used_icon_urls;
 
   // Lock over the whole list.
-  for (size_t i = 0, processed = 0;
-       i < loaded_value_.size() && processed < max_result_count_; ++i) {
-    // Any packages that are installing or installed and not in sync with the
-    // server are removed with IsUnknownPackage.
-    if (!prefs->IsUnknownPackage(loaded_value_[i]->package_name))
-      continue;
+  if (ShouldShowAnything()) {
+    for (size_t i = 0, processed = 0;
+         i < loaded_value_.size() && processed < max_result_count_; ++i) {
+      // Any packages that are installing or installed and not in sync with the
+      // server are removed with IsUnknownPackage.
+      if (!prefs->IsUnknownPackage(loaded_value_[i]->package_name))
+        continue;
+      // Should we filter this ?
+      if (!ShouldShowPackage(loaded_value_[i]->package_name)) {
+        continue;
+      }
+      processed++;
 
-    processed++;
+      // From this point, we believe that this item should be in the result
+      // list. We try to find this icon, and if it is not available, we load it.
+      const std::string& icon_url = loaded_value_[i]->icon_url.value();
+      // All the icons we are showing.
+      used_icon_urls.insert(icon_url);
 
-    // From this point, we believe that this item should be in the result list.
-    // We try to find this icon, and if it is not available, we load it.
-    const std::string& icon_url = loaded_value_[i]->icon_url.value();
-    // All the icons we are showing.
-    used_icon_urls.insert(icon_url);
-
-    const auto icon_it = icon_urls_.find(icon_url);
-    const auto loading_icon_it = loading_icon_urls_.find(icon_url);
-    if (icon_it == icon_urls_.end() &&
-        loading_icon_it == loading_icon_urls_.end()) {
-      // this icon is not loaded, nor is it in the loading set. Add it.
-      loading_icon_urls_[icon_url] = gfx::ImageSkia(
-          std::make_unique<app_list::UrlIconSource>(
-              base::BindRepeating(&ArcAppReinstallSearchProvider::OnIconLoaded,
-                                  weak_ptr_factory_.GetWeakPtr(), icon_url),
-              profile_, GURL(LimitIconSizeWithFife(icon_url, icon_dimension_)),
-              icon_dimension_, IDR_APP_DEFAULT_ICON),
-          gfx::Size(icon_dimension_, icon_dimension_));
-      loading_icon_urls_[icon_url].GetRepresentation(1.0f);
-    } else if (icon_it != icon_urls_.end()) {
-      // Icon is loaded, add it to the results.
-      new_results.emplace_back(std::make_unique<ArcAppReinstallAppResult>(
-          loaded_value_[i], icon_it->second));
+      const auto icon_it = icon_urls_.find(icon_url);
+      const auto loading_icon_it = loading_icon_urls_.find(icon_url);
+      if (icon_it == icon_urls_.end() &&
+          loading_icon_it == loading_icon_urls_.end()) {
+        // this icon is not loaded, nor is it in the loading set. Add it.
+        loading_icon_urls_[icon_url] = gfx::ImageSkia(
+            std::make_unique<app_list::UrlIconSource>(
+                base::BindRepeating(
+                    &ArcAppReinstallSearchProvider::OnIconLoaded,
+                    weak_ptr_factory_.GetWeakPtr(), icon_url),
+                profile_,
+                GURL(LimitIconSizeWithFife(icon_url, icon_dimension_)),
+                icon_dimension_, IDR_APP_DEFAULT_ICON),
+            gfx::Size(icon_dimension_, icon_dimension_));
+        loading_icon_urls_[icon_url].GetRepresentation(1.0f);
+      } else if (icon_it != icon_urls_.end()) {
+        // Icon is loaded, add it to the results.
+        new_results.emplace_back(std::make_unique<ArcAppReinstallAppResult>(
+            loaded_value_[i], icon_it->second, this));
+      }
     }
   }
 
@@ -237,11 +408,16 @@
 
 void ArcAppReinstallSearchProvider::OnInstallationStarted(
     const std::string& package_name) {
+  UpdateStateTime(profile_, package_name, kInstallStartTime);
   UpdateResults();
 }
+
 void ArcAppReinstallSearchProvider::OnInstallationFinished(
     const std::string& package_name,
     bool success) {
+  if (success) {
+    UpdateStateTime(profile_, package_name, kInstallTime);
+  }
   UpdateResults();
 }
 
@@ -253,6 +429,11 @@
 void ArcAppReinstallSearchProvider::OnPackageRemoved(
     const std::string& package_name,
     bool uninstalled) {
+  // If we uninstalled this, update the timestamp before updating results.
+  // Otherwise, it's just an app no longer available.
+  if (uninstalled) {
+    UpdateStateTime(profile_, package_name, kUninstallTime);
+  }
   UpdateResults();
 }
 
@@ -261,10 +442,43 @@
   app_fetch_timer_ = std::move(timer);
 }
 
+void ArcAppReinstallSearchProvider::OnOpened(const std::string& id) {
+  UpdateStateTime(profile_, id, kOpenTime);
+  UpdateResults();
+}
+
+void ArcAppReinstallSearchProvider::OnVisibilityChanged(const std::string& id,
+                                                        bool visibility) {
+  if (!visibility) {
+    // do not update state when showing, update when we hide.
+    return;
+  }
+
+  // If never shown before, or shown more than |kNewImpressionTime| ago,
+  // increment the count here.
+  const base::TimeDelta now = base::Time::Now().ToDeltaSinceWindowsEpoch();
+  base::TimeDelta latest_impression;
+  int64_t impression_count;
+  // Get impression count and time. If neither is set, set them.
+  // If they're set, update if appropriate.
+  if (!GetStateTime(profile_, id, kImpressionTime, &latest_impression) ||
+      !GetStateInt64(profile_, id, kImpressionCount, &impression_count) ||
+      impression_count == 0 ||
+      (now - latest_impression >
+       base::TimeDelta::FromSeconds(kNewImpressionTime.Get()))) {
+    UpdateStateTime(profile_, id, kImpressionTime);
+    SetStateInt64(profile_, id, kImpressionCount, impression_count + 1);
+  }
+}
+
+void ArcAppReinstallSearchProvider::RegisterProfilePrefs(
+    PrefRegistrySimple* registry) {
+  registry->RegisterDictionaryPref(kAppState);
+}
+
 // For icon load callback, in OnGetAppReinstallCandidates
 void ArcAppReinstallSearchProvider::OnIconLoaded(const std::string& icon_url) {
   auto skia_ptr = loading_icon_urls_.find(icon_url);
-
   DCHECK(skia_ptr != loading_icon_urls_.end());
   if (skia_ptr == loading_icon_urls_.end()) {
     return;
@@ -282,6 +496,80 @@
   UpdateResults();
 }
 
+bool ArcAppReinstallSearchProvider::ShouldShowPackage(
+    const std::string& package_id) const {
+  base::TimeDelta timestamp;
+  const base::TimeDelta now = base::Time::Now().ToDeltaSinceWindowsEpoch();
+  if (GetStateTime(profile_, package_id, kUninstallTime, &timestamp)) {
+    const auto delta = now - timestamp;
+    if (delta < base::TimeDelta::FromHours(kUninstallGrace.Get())) {
+      // We uninstalled this recently, don't show.
+      return false;
+    }
+  }
+  if (GetStateTime(profile_, package_id, kInstallStartTime, &timestamp)) {
+    const auto delta = now - timestamp;
+    if (delta < base::TimeDelta::FromHours(kInstallStartGrace.Get())) {
+      // We started install on this recently, don't show.
+      return false;
+    }
+  }
+  int64_t value;
+  if (GetStateInt64(profile_, package_id, kImpressionCount, &value)) {
+    if (value > kImpressionLimit.Get()) {
+      // Shown too many times, ignore.
+      return false;
+    }
+  }
+  return true;
+}
+
+bool ArcAppReinstallSearchProvider::ShouldShowAnything() const {
+  if (!kInteractionGrace.Get()) {
+    return true;
+  }
+  const base::TimeDelta grace_period =
+      base::TimeDelta::FromHours(kInteractionGrace.Get());
+  const base::TimeDelta now = base::Time::Now().ToDeltaSinceWindowsEpoch();
+  std::unordered_set<std::string> package_names;
+  GetKnownPackageNames(profile_, &package_names);
+
+  for (const std::string& package_name : package_names) {
+    base::TimeDelta install_time;
+    if (GetStateTime(profile_, package_name, kInstallTime, &install_time)) {
+      if (now - install_time < grace_period) {
+        // installed in grace, do not show anything.
+        return false;
+      }
+    }
+
+    base::TimeDelta result_open;
+    if (GetStateTime(profile_, package_name, kOpenTime, &result_open)) {
+      if (now - result_open < grace_period) {
+        // Shown in grace, do not show anything.
+        return false;
+      }
+    }
+
+    int64_t impression_count;
+    if (GetStateInt64(profile_, package_name, kImpressionCount,
+                      &impression_count)) {
+      if (impression_count >= kImpressionLimit.Get()) {
+        base::TimeDelta impression_time;
+        if (GetStateTime(profile_, package_name, kImpressionTime,
+                         &impression_time)) {
+          if (now - impression_time < grace_period) {
+            // We showed a too-many-shown result recently, within grace, don't
+            // show anything.
+            return false;
+          }
+        }
+      }
+    }
+  }
+  return true;
+}
+
 bool ArcAppReinstallSearchProvider::ResultsIdentical(
     const std::vector<std::unique_ptr<ChromeSearchResult>>& old_results,
     const std::vector<std::unique_ptr<ChromeSearchResult>>& new_results) {
diff --git a/chrome/browser/ui/app_list/search/arc/arc_app_reinstall_search_provider.h b/chrome/browser/ui/app_list/search/arc/arc_app_reinstall_search_provider.h
index 2ea9f9d..2534e7a 100644
--- a/chrome/browser/ui/app_list/search/arc/arc_app_reinstall_search_provider.h
+++ b/chrome/browser/ui/app_list/search/arc/arc_app_reinstall_search_provider.h
@@ -5,7 +5,6 @@
 #ifndef CHROME_BROWSER_UI_APP_LIST_SEARCH_ARC_ARC_APP_REINSTALL_SEARCH_PROVIDER_H_
 #define CHROME_BROWSER_UI_APP_LIST_SEARCH_ARC_ARC_APP_REINSTALL_SEARCH_PROVIDER_H_
 
-#include <map>
 #include <memory>
 #include <string>
 #include <unordered_map>
@@ -15,9 +14,11 @@
 #include "base/memory/weak_ptr.h"
 #include "base/timer/timer.h"
 #include "chrome/browser/ui/app_list/arc/arc_app_list_prefs.h"
+#include "chrome/browser/ui/app_list/search/arc/arc_app_reinstall_app_result.h"
 #include "chrome/browser/ui/app_list/search/search_provider.h"
 #include "ui/gfx/image/image_skia.h"
 
+class PrefRegistrySimple;
 class Profile;
 FORWARD_DECLARE_TEST(ArcAppReinstallSearchProviderTest,
                      TestResultsWithSearchChanged);
@@ -37,8 +38,10 @@
 //
 // For users who do not have ARC++ enabled, we do not make a call through to the
 // Play Store, but rather populate with empty results.
-class ArcAppReinstallSearchProvider : public SearchProvider,
-                                      public ArcAppListPrefs::Observer {
+class ArcAppReinstallSearchProvider
+    : public SearchProvider,
+      public ArcAppListPrefs::Observer,
+      public ArcAppReinstallAppResult::Observer {
  public:
   // Constructor receives the Profile in order to
   // instantiate App results. Ownership is not taken.
@@ -56,6 +59,13 @@
   // Used by unit tests. SearchProvider takes ownership of pointer.
   void SetTimerForTesting(std::unique_ptr<base::RepeatingTimer> timer);
 
+  // ArcAppReinstallAppResult::Observer:
+  void OnOpened(const std::string& id) override;
+
+  void OnVisibilityChanged(const std::string& id, bool visibility) override;
+
+  static void RegisterProfilePrefs(PrefRegistrySimple* registry);
+
  private:
   FRIEND_TEST_ALL_PREFIXES(::ArcAppReinstallSearchProviderTest,
                            TestResultsWithSearchChanged);
@@ -101,6 +111,11 @@
   // For icon load callback, in OnGetAppReinstallCandidates
   void OnIconLoaded(const std::string& icon_url);
 
+  // Should we show this package?
+  bool ShouldShowPackage(const std::string& package_id) const;
+  // Should we show anything to the user?
+  bool ShouldShowAnything() const;
+
   // Are both lists of chrome search results of the same title in the same
   // order?
   static bool ResultsIdentical(
diff --git a/chrome/browser/ui/app_list/search/arc/arc_app_reinstall_search_provider_unittest.cc b/chrome/browser/ui/app_list/search/arc/arc_app_reinstall_search_provider_unittest.cc
index bb4e842..2622baa 100644
--- a/chrome/browser/ui/app_list/search/arc/arc_app_reinstall_search_provider_unittest.cc
+++ b/chrome/browser/ui/app_list/search/arc/arc_app_reinstall_search_provider_unittest.cc
@@ -1,3 +1,4 @@
+
 // Copyright 2018 The Chromium Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
@@ -7,7 +8,9 @@
 #include <map>
 #include <utility>
 
+#include "ash/public/cpp/app_list/app_list_features.h"
 #include "base/command_line.h"
+#include "base/metrics/field_trial_params.h"
 #include "base/strings/utf_string_conversions.h"
 #include "base/timer/mock_timer.h"
 #include "chrome/browser/ui/app_list/app_list_test_util.h"
@@ -18,6 +21,7 @@
 #include "chrome/test/base/testing_profile.h"
 #include "components/arc/common/app.mojom.h"
 #include "components/arc/test/fake_app_instance.h"
+#include "components/prefs/scoped_user_pref_update.h"
 #include "extensions/grit/extensions_browser_resources.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
@@ -35,7 +39,6 @@
     app_provider_ =
         base::WrapUnique(new app_list::ArcAppReinstallSearchProvider(
             profile_.get(), /*max_result_count=*/2));
-
     std::unique_ptr<base::MockRepeatingTimer> timer =
         std::make_unique<base::MockRepeatingTimer>();
     mock_timer_ = timer.get();
@@ -47,6 +50,7 @@
     mock_timer_ = nullptr;
     app_provider_.reset(nullptr);
     arc_app_test_.TearDown();
+
     AppListTestBase::TearDown();
   }
 
@@ -69,6 +73,24 @@
     return package.Clone();
   }
 
+  std::string kAppState = "arc_app_reinstall_state";
+  void SetStateInt64(Profile* profile,
+                     const std::string& package_name,
+                     const std::string& key,
+                     const int64_t value) {
+    const std::string int64_str = base::NumberToString(value);
+    DictionaryPrefUpdate update(profile->GetPrefs(), kAppState);
+    base::DictionaryValue* const dictionary = update.Get();
+    base::Value* package_item =
+        dictionary->FindKeyOfType(package_name, base::Value::Type::DICTIONARY);
+    if (!package_item) {
+      package_item = dictionary->SetKey(
+          package_name, base::Value(base::Value::Type::DICTIONARY));
+    }
+
+    package_item->SetKey(key, base::Value(int64_str));
+  }
+
   // Owned by |app_provider_|.
   base::MockRepeatingTimer* mock_timer_;
   ArcAppTest arc_app_test_;
@@ -190,7 +212,36 @@
   app_instance()->SendInstallationFinished("com.package.fakepackage1", true);
   EXPECT_EQ(1u, app_provider_->results().size());
   app_instance()->UninstallPackage("com.package.fakepackage1");
-  EXPECT_EQ(2u, app_provider_->results().size());
+  // We expect the uninstall not to go back to the list.
+  EXPECT_EQ(1u, app_provider_->results().size());
+
+  // Check for persistence:
+
+  app_provider_ = base::WrapUnique(new app_list::ArcAppReinstallSearchProvider(
+      profile_.get(), /*max_result_count=*/2));
+
+  EXPECT_EQ(0u, app_provider_->results().size());
+  app_provider_->OnIconLoaded("http://icon.com/icon1");
+  EXPECT_EQ(1u, app_provider_->results().size());
+
+  // Check that impression counts are read appropriately.
+  const std::string impression_count = "impression_count";
+  SetStateInt64(profile_.get(), "com.package.fakepackage2", impression_count,
+                50);
+  app_provider_->UpdateResults();
+  EXPECT_EQ(0u, app_provider_->results().size());
+  SetStateInt64(profile_.get(), "com.package.fakepackage2", impression_count,
+                0);
+  app_provider_->UpdateResults();
+  app_provider_->OnIconLoaded("http://icon.com/icon1");
+  EXPECT_EQ(1u, app_provider_->results().size());
+
+  // If uninstalled recently, avoid.
+  const std::string uninstall_time = "uninstall_time";
+  SetStateInt64(profile_.get(), "com.package.fakepackage2", uninstall_time,
+                base::Time::Now().ToDeltaSinceWindowsEpoch().InMilliseconds());
+  app_provider_->UpdateResults();
+  EXPECT_EQ(0u, app_provider_->results().size());
 }
 
 TEST_F(ArcAppReinstallSearchProviderTest, TestResultListComparison) {
diff --git a/chrome/browser/ui/app_list/search/arc/arc_app_shortcuts_search_provider_unittest.cc b/chrome/browser/ui/app_list/search/arc/arc_app_shortcuts_search_provider_unittest.cc
index e92c850..a2eed18 100644
--- a/chrome/browser/ui/app_list/search/arc/arc_app_shortcuts_search_provider_unittest.cc
+++ b/chrome/browser/ui/app_list/search/arc/arc_app_shortcuts_search_provider_unittest.cc
@@ -7,10 +7,15 @@
 #include <memory>
 #include <utility>
 
+#include "ash/public/cpp/app_list/app_list_features.h"
 #include "base/files/scoped_temp_dir.h"
 #include "base/macros.h"
+#include "base/strings/strcat.h"
+#include "base/strings/string_number_conversions.h"
 #include "base/strings/stringprintf.h"
 #include "base/strings/utf_string_conversions.h"
+#include "base/test/scoped_feature_list.h"
+#include "base/test/scoped_task_environment.h"
 #include "chrome/browser/chromeos/arc/icon_decode_request.h"
 #include "chrome/browser/ui/app_list/app_list_test_util.h"
 #include "chrome/browser/ui/app_list/arc/arc_app_list_prefs.h"
@@ -39,10 +44,6 @@
     AppListTestBase::SetUp();
     arc_test_.SetUp(profile());
     controller_ = std::make_unique<test::TestAppListControllerDelegate>();
-    ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
-    ranker_ =
-        std::make_unique<AppSearchResultRanker>(temp_dir_.GetPath(),
-                                                /*is_ephemeral_user=*/true);
   }
 
   void TearDown() override {
@@ -51,6 +52,20 @@
     AppListTestBase::TearDown();
   }
 
+  void CreateRanker(const std::map<std::string, std::string>& params = {}) {
+    if (!params.empty()) {
+      scoped_feature_list_.InitAndEnableFeatureWithParameters(
+          app_list_features::kEnableAppSearchResultRanker, params);
+      ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
+      ranker_ =
+          std::make_unique<AppSearchResultRanker>(temp_dir_.GetPath(),
+                                                  /*is_ephemeral_user=*/false);
+    } else {
+      scoped_feature_list_.InitWithFeatures(
+          {}, {app_list_features::kEnableAppSearchResultRanker});
+    }
+  }
+
   arc::mojom::AppInfo CreateAppInfo(const std::string& name,
                                     const std::string& activity,
                                     const std::string& package_name) {
@@ -78,6 +93,7 @@
   }
 
   base::ScopedTempDir temp_dir_;
+  base::test::ScopedFeatureList scoped_feature_list_;
   std::unique_ptr<AppSearchResultRanker> ranker_;
   std::unique_ptr<test::TestAppListControllerDelegate> controller_;
   ArcAppTest arc_test_;
@@ -87,6 +103,8 @@
 };
 
 TEST_P(ArcAppShortcutsSearchProviderTest, Basic) {
+  CreateRanker();
+  EXPECT_EQ(ranker_, nullptr);
   const bool launchable = GetParam();
 
   const std::string app_id = AddArcAppAndShortcut(
@@ -110,27 +128,64 @@
               base::UTF16ToUTF8(results[i]->title()));
     EXPECT_EQ(ash::SearchResultDisplayType::kTile, results[i]->display_type());
   }
+}
 
-  // If ranker_ is nullptr, the program won't break
-  // TODO(crbug.com/931149): Add more tests to check ranker_ does have some
-  // effects
-  auto provider_null_ranker = std::make_unique<ArcAppShortcutsSearchProvider>(
-      kMaxResults, profile(), controller_.get(), nullptr);
+TEST_F(ArcAppShortcutsSearchProviderTest, RankerIsDisableWithFlag) {
+  CreateRanker();
+  EXPECT_EQ(ranker_, nullptr);
 
-  EXPECT_TRUE(provider_null_ranker->results().empty());
+  const std::string app_id = AddArcAppAndShortcut(
+      CreateAppInfo("FakeName", "FakeActivity", kFakeAppPackageName), true);
+  const size_t kMaxResults = 4;
+  constexpr char kQuery[] = "shortlabel";
+  constexpr char kPrefix[] = "appshortcutsearch://";
+  constexpr char kShortcutId[] = "/ShortcutId ";
+
+  // Create a search provider and train with kMaxResults shortcuts.
+  auto provider = std::make_unique<ArcAppShortcutsSearchProvider>(
+      kMaxResults, profile(), controller_.get(), ranker_.get());
   arc::IconDecodeRequest::DisableSafeDecodingForTesting();
 
-  provider_null_ranker->Start(base::UTF8ToUTF16(kQuery));
-  provider_null_ranker->Train(app_id, RankingItemType::kArcAppShortcut);
-  const auto& results_null_ranker = provider_null_ranker->results();
-  EXPECT_EQ(kMaxResults, results_null_ranker.size());
-  // Verify search results.
-  for (size_t i = 0; i < results_null_ranker.size(); ++i) {
-    EXPECT_EQ(base::StringPrintf("ShortLabel %zu", i),
-              base::UTF16ToUTF8(results_null_ranker[i]->title()));
-    EXPECT_EQ(ash::SearchResultDisplayType::kTile,
-              results_null_ranker[i]->display_type());
+  for (size_t i = 0; i < kMaxResults; i++) {
+    provider->Train(
+        base::StrCat({kPrefix, app_id, kShortcutId, base::NumberToString(i)}),
+        RankingItemType::kArcAppShortcut);
   }
+  provider->Start(base::UTF8ToUTF16(kQuery));
+
+  // Currently, without ranker, relevance scores for app
+  // shortcuts are always 0.
+  const auto& results = provider->results();
+  for (const auto& result : results)
+    EXPECT_EQ(result->relevance(), 0);
+}
+
+TEST_F(ArcAppShortcutsSearchProviderTest, RankerImproveScores) {
+  CreateRanker({{"rank_arc_app_shortcuts", "true"}});
+  EXPECT_NE(ranker_, nullptr);
+
+  const std::string app_id = AddArcAppAndShortcut(
+      CreateAppInfo("FakeName", "FakeActivity", kFakeAppPackageName), true);
+  const size_t kMaxResults = 4;
+  constexpr char kQuery[] = "shortlabel";
+  constexpr char kPrefix[] = "appshortcutsearch://";
+  constexpr char kShortcutId[] = "/ShortcutId ";
+
+  // Create a search provider and train with kMaxResults shortcuts.
+  auto provider = std::make_unique<ArcAppShortcutsSearchProvider>(
+      kMaxResults, profile(), controller_.get(), ranker_.get());
+  arc::IconDecodeRequest::DisableSafeDecodingForTesting();
+
+  for (size_t i = 0; i < kMaxResults; i++) {
+    provider->Train(
+        base::StrCat({kPrefix, app_id, kShortcutId, base::NumberToString(i)}),
+        RankingItemType::kArcAppShortcut);
+  }
+  provider->Start(base::UTF8ToUTF16(kQuery));
+  // Verify search results to see whether they were increased.
+  const auto& results = provider->results();
+  for (const auto& result : results)
+    EXPECT_GT(result->relevance(), 0);
 }
 
 INSTANTIATE_TEST_SUITE_P(, ArcAppShortcutsSearchProviderTest, testing::Bool());
diff --git a/chrome/browser/ui/app_list/search/tests/app_search_provider_unittest.cc b/chrome/browser/ui/app_list/search/tests/app_search_provider_unittest.cc
index 8309994f..17fa7b2 100644
--- a/chrome/browser/ui/app_list/search/tests/app_search_provider_unittest.cc
+++ b/chrome/browser/ui/app_list/search/tests/app_search_provider_unittest.cc
@@ -273,6 +273,21 @@
               result == "Fake App 1,Packaged App 1");
 }
 
+TEST_F(AppSearchProviderTest, NormalizeAppID) {
+  const std::string raw_id = "mgndgikekgjfcpckkfioiadnlibdjbkf";
+  const std::string id_with_scheme =
+      "chrome-extension://mgndgikekgjfcpckkfioiadnlibdjbkf";
+  const std::string id_with_slash = "mgndgikekgjfcpckkfioiadnlibdjbkf/";
+  const std::string id_with_scheme_and_slash =
+      "chrome-extension://mgndgikekgjfcpckkfioiadnlibdjbkf/";
+
+  EXPECT_EQ(AppSearchProvider::NormalizeIDForTest(raw_id), raw_id);
+  EXPECT_EQ(AppSearchProvider::NormalizeIDForTest(id_with_scheme), raw_id);
+  EXPECT_EQ(AppSearchProvider::NormalizeIDForTest(id_with_slash), raw_id);
+  EXPECT_EQ(AppSearchProvider::NormalizeIDForTest(id_with_scheme_and_slash),
+            raw_id);
+}
+
 TEST_F(AppSearchProviderTest, DisableAndEnable) {
   CreateSearch();
 
diff --git a/chrome/browser/ui/blocked_content/popup_blocker_browsertest.cc b/chrome/browser/ui/blocked_content/popup_blocker_browsertest.cc
index 0f475574..5b7337d 100644
--- a/chrome/browser/ui/blocked_content/popup_blocker_browsertest.cc
+++ b/chrome/browser/ui/blocked_content/popup_blocker_browsertest.cc
@@ -16,6 +16,7 @@
 #include "chrome/browser/content_settings/host_content_settings_map_factory.h"
 #include "chrome/browser/content_settings/tab_specific_content_settings.h"
 #include "chrome/browser/history/history_test_utils.h"
+#include "chrome/browser/policy/profile_policy_connector_factory.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/search_engines/template_url_service_factory.h"
 #include "chrome/browser/ui/blocked_content/list_item_position.h"
@@ -32,6 +33,7 @@
 #include "chrome/common/chrome_features.h"
 #include "chrome/common/chrome_paths.h"
 #include "chrome/common/chrome_switches.h"
+#include "chrome/common/pref_names.h"
 #include "chrome/common/url_constants.h"
 #include "chrome/test/base/in_process_browser_test.h"
 #include "chrome/test/base/interactive_test_utils.h"
@@ -45,6 +47,10 @@
 #include "components/omnibox/browser/autocomplete_result.h"
 #include "components/omnibox/browser/omnibox_edit_model.h"
 #include "components/omnibox/browser/omnibox_view.h"
+#include "components/policy/core/browser/browser_policy_connector.h"
+#include "components/policy/core/common/mock_configuration_policy_provider.h"
+#include "components/policy/core/common/policy_map.h"
+#include "components/policy/policy_constants.h"
 #include "content/public/browser/native_web_keyboard_event.h"
 #include "content/public/browser/navigation_controller.h"
 #include "content/public/browser/notification_registrar.h"
@@ -428,9 +434,6 @@
   ASSERT_EQ(1, GetBlockedContentsCount());
 }
 
-// Popups during page unloading is a feature being put behind a policy and
-// needing an easily-mergeable change. See https://crbug.com/936080 .
-#if 0
 IN_PROC_BROWSER_TEST_F(PopupBlockerBrowserTest, NoPopupsLaunchWhenTabIsClosed) {
   base::CommandLine::ForCurrentProcess()->AppendSwitch(
       switches::kDisablePopupBlocking);
@@ -444,7 +447,51 @@
   // Expect no popup.
   ASSERT_EQ(1u, chrome::GetBrowserCount(browser()->profile()));
 }
+
+// This only exists for the AllowPopupsWhenTabIsClosedWithSpecialPolicy test.
+// Remove this in Chrome 82. https://crbug.com/937569
+class PopupBlockerSpecialPolicyBrowserTest : public PopupBlockerBrowserTest {
+ public:
+  PopupBlockerSpecialPolicyBrowserTest() {}
+  ~PopupBlockerSpecialPolicyBrowserTest() override {}
+
+ protected:
+  void SetUpInProcessBrowserTestFixture() override {
+    policy::PolicyMap policy_map;
+
+    policy_map.Set(policy::key::kAllowPopupsDuringPageUnload,
+                   policy::POLICY_LEVEL_MANDATORY, policy::POLICY_SCOPE_USER,
+                   policy::POLICY_SOURCE_CLOUD,
+                   std::make_unique<base::Value>(true), nullptr);
+    policy_provider_.UpdateChromePolicy(policy_map);
+
+#if defined(OS_CHROMEOS)
+    policy::BrowserPolicyConnector::SetPolicyProviderForTesting(
+        &policy_provider_);
+#else
+    policy::ProfilePolicyConnectorFactory::GetInstance()
+        ->PushProviderForTesting(&policy_provider_);
 #endif
+  }
+
+ private:
+  policy::MockConfigurationPolicyProvider policy_provider_;
+
+  DISALLOW_COPY_AND_ASSIGN(PopupBlockerSpecialPolicyBrowserTest);
+};
+
+// Remove this in Chrome 82. https://crbug.com/937569
+IN_PROC_BROWSER_TEST_F(PopupBlockerSpecialPolicyBrowserTest,
+                       AllowPopupsWhenTabIsClosedWithSpecialPolicy) {
+  base::CommandLine::ForCurrentProcess()->AppendSwitch(
+      switches::kDisablePopupBlocking);
+  GURL url(
+      embedded_test_server()->GetURL("/popup_blocker/popup-on-unload.html"));
+  ui_test_utils::NavigateToURL(browser(), url);
+
+  NavigateAndCheckPopupShown(embedded_test_server()->GetURL("/popup_blocker/"),
+                             kExpectPopup);
+}
 
 // Verify that when you unblock popup, the popup shows in history and omnibox.
 IN_PROC_BROWSER_TEST_F(PopupBlockerBrowserTest,
diff --git a/chrome/browser/ui/browser_browsertest.cc b/chrome/browser/ui/browser_browsertest.cc
index eae1bde..26cd325 100644
--- a/chrome/browser/ui/browser_browsertest.cc
+++ b/chrome/browser/ui/browser_browsertest.cc
@@ -2764,3 +2764,42 @@
   browser_as_dialog_delegate->SetWebContentsBlocked(tab, false);
   tab->DecrementCapturerCount();
 }
+
+IN_PROC_BROWSER_TEST_F(BrowserTest, CountIncognitoWindows) {
+  DCHECK_EQ(0, BrowserList::GetIncognitoSessionsActiveForProfile(
+                   browser()->profile()));
+
+  // Create an incognito browser and check the count.
+  Browser* browser1 = CreateIncognitoBrowser(browser()->profile());
+  DCHECK_EQ(1, BrowserList::GetIncognitoSessionsActiveForProfile(
+                   browser()->profile()));
+
+  // Create another incognito browser and check the count.
+  Browser* browser2 = CreateIncognitoBrowser(browser()->profile());
+  DCHECK_EQ(2, BrowserList::GetIncognitoSessionsActiveForProfile(
+                   browser()->profile()));
+
+  // Open a docked DevTool window and count.
+  DevToolsWindow* devtools_window =
+      DevToolsWindowTesting::OpenDevToolsWindowSync(browser1, true);
+  DCHECK_EQ(2, BrowserList::GetIncognitoSessionsActiveForProfile(
+                   browser()->profile()));
+  DevToolsWindowTesting::CloseDevToolsWindowSync(devtools_window);
+
+  // Open a detached DevTool window and count.
+  devtools_window =
+      DevToolsWindowTesting::OpenDevToolsWindowSync(browser1, false);
+  DCHECK_EQ(2, BrowserList::GetIncognitoSessionsActiveForProfile(
+                   browser()->profile()));
+  DevToolsWindowTesting::CloseDevToolsWindowSync(devtools_window);
+
+  // Close one browser and count.
+  CloseBrowserSynchronously(browser2);
+  DCHECK_EQ(1, BrowserList::GetIncognitoSessionsActiveForProfile(
+                   browser()->profile()));
+
+  // Close another browser and count.
+  CloseBrowserSynchronously(browser1);
+  DCHECK_EQ(0, BrowserList::GetIncognitoSessionsActiveForProfile(
+                   browser()->profile()));
+}
diff --git a/chrome/browser/ui/browser_list.cc b/chrome/browser/ui/browser_list.cc
index d3f17ce..d4cb420 100644
--- a/chrome/browser/ui/browser_list.cc
+++ b/chrome/browser/ui/browser_list.cc
@@ -316,9 +316,10 @@
   BrowserList* list = BrowserList::GetInstance();
   return std::count_if(list->begin(), list->end(), [profile](Browser* browser) {
     return browser->profile()->IsSameProfile(profile) &&
-           browser->profile()->IsOffTheRecord();
+           browser->profile()->IsOffTheRecord() && !browser->is_devtools();
   });
 }
+
 ////////////////////////////////////////////////////////////////////////////////
 // BrowserList, private:
 
diff --git a/chrome/browser/ui/browser_list.h b/chrome/browser/ui/browser_list.h
index 83534ac..bebf47b 100644
--- a/chrome/browser/ui/browser_list.h
+++ b/chrome/browser/ui/browser_list.h
@@ -121,7 +121,8 @@
   static bool IsIncognitoSessionActive();
 
   // Returns the number of active incognito sessions for |profile| across all
-  // desktops.
+  // desktops. Note that this function does not count devtools windows opened
+  // for incognito windows.
   static int GetIncognitoSessionsActiveForProfile(Profile* profile);
 
  private:
diff --git a/chrome/browser/ui/browser_ui_prefs.cc b/chrome/browser/ui/browser_ui_prefs.cc
index d03a42d97..fd2e2a1b 100644
--- a/chrome/browser/ui/browser_ui_prefs.cc
+++ b/chrome/browser/ui/browser_ui_prefs.cc
@@ -127,4 +127,5 @@
 
   registry->RegisterBooleanPref(prefs::kEnterpriseHardwarePlatformAPIEnabled,
                                 false);
+  registry->RegisterBooleanPref(prefs::kAllowPopupsDuringPageUnload, false);
 }
diff --git a/chrome/browser/ui/extensions/icon_with_badge_image_source.cc b/chrome/browser/ui/extensions/icon_with_badge_image_source.cc
index eb4739cc..28fb4eb6 100644
--- a/chrome/browser/ui/extensions/icon_with_badge_image_source.cc
+++ b/chrome/browser/ui/extensions/icon_with_badge_image_source.cc
@@ -82,7 +82,7 @@
         rep, ExtensionAction::ActionIconSize(), canvas->image_scale()));
   }
   if (grayscale_)
-    skia = gfx::ImageSkiaOperations::CreateHSLShiftedImage(skia, {-1, 0, 0.75});
+    skia = gfx::ImageSkiaOperations::CreateHSLShiftedImage(skia, {-1, 0, 0.6});
 
   int x_offset =
       std::floor((size().width() - ExtensionAction::ActionIconSize()) / 2.0);
diff --git a/chrome/browser/ui/startup/credential_provider_signin_dialog_win.cc b/chrome/browser/ui/startup/credential_provider_signin_dialog_win.cc
index b6a1926..c46c3b7 100644
--- a/chrome/browser/ui/startup/credential_provider_signin_dialog_win.cc
+++ b/chrome/browser/ui/startup/credential_provider_signin_dialog_win.cc
@@ -54,6 +54,14 @@
   }
 }
 
+void WriteResultToHandleWithKeepAlive(
+    std::unique_ptr<ScopedKeepAlive> keep_alive,
+    base::Value signin_result) {
+  WriteResultToHandle(signin_result);
+
+  // Release the keep_alive implicitly and allow the dialog to die.
+}
+
 void HandleAllGcpwInfoFetched(
     std::unique_ptr<ScopedKeepAlive> keep_alive,
     std::unique_ptr<CredentialProviderSigninInfoFetcher> fetcher,
@@ -85,10 +93,15 @@
                                      base::Value::Type::INTEGER)
                       ->GetInt();
 
-  // If there was an exit code, write the result now and continue to release
-  // the keep alive.
+  // If there is an error code, write out the signin results directly.
+  // Otherwise fetch more info required for the signin.  In either case,
+  // make sure the keep alive is not destroyed on return of this function
+  // or a reentrancy crash will occur in HWNDMessageHandler().
   if (exit_code != credential_provider::kUiecSuccess) {
-    WriteResultToHandle(signin_result);
+    base::ThreadTaskRunnerHandle::Get()->PostTask(
+        FROM_HERE,
+        base::BindOnce(&WriteResultToHandleWithKeepAlive, std::move(keep_alive),
+                       std::move(signin_result)));
   } else if (signin_result.DictSize() > 1) {
     std::string access_token =
         signin_result
@@ -134,6 +147,22 @@
             base::Unretained(this)));
   }
 
+  void AbortIfPossible() {
+    // If the callback was already called, ignore.
+    if (!signin_callback_)
+      return;
+
+    // Build a result for the credential provider that includes only the abort
+    // exit code.
+    std::unique_ptr<base::Value> result(
+        new base::Value(base::Value::Type::DICTIONARY));
+    result->SetKey(credential_provider::kKeyExitCode,
+                   base::Value(credential_provider::kUiecAbort));
+    base::ListValue args;
+    args.Append(std::move(result));
+    OnSigninComplete(&args);
+  }
+
  private:
   base::Value ParseArgs(const base::ListValue* args, int* out_exit_code) {
     DCHECK(out_exit_code);
@@ -176,6 +205,13 @@
   }
 
   void OnSigninComplete(const base::ListValue* args) {
+    // If the callback was already called, ignore.  This may happen if the
+    // user presses Escape right after finishing the signin process, the
+    // Escape is processed first by AbortIfPossible(), and the signin then
+    // completes before WriteResultToHandleWithKeepAlive() executes.
+    if (!signin_callback_)
+      return;
+
     int exit_code;
     base::Value signin_result = ParseArgs(args, &exit_code);
 
@@ -274,8 +310,10 @@
   void GetWebUIMessageHandlers(
       std::vector<content::WebUIMessageHandler*>* handlers) const override {
     // The WebDialogUI will own and delete this message handler.
-    handlers->push_back(
-        new CredentialProviderWebUIMessageHandler(std::move(signin_callback_)));
+    DCHECK(!handler_);
+    handler_ =
+        new CredentialProviderWebUIMessageHandler(std::move(signin_callback_));
+    handlers->push_back(handler_);
   }
 
   void GetDialogSize(gfx::Size* size) const override {
@@ -291,6 +329,13 @@
   std::string GetDialogArgs() const override { return std::string(); }
 
   void OnDialogClosed(const std::string& json_retval) override {
+    // To handle the case where the user presses Esc to cancel the sign in,
+    // write out an "abort" result for the credential provider.  However,
+    // this function is also called when the user completes the sign in
+    // successfully and output has already been written.  In this case the
+    // abort is not possible and this call is a noop.
+    handler_->AbortIfPossible();
+
     // Class owns itself and thus needs to be deleted eventually after the
     // closed call back has been signalled since it will no longer be accessed
     // by the WebDialogView.
@@ -325,6 +370,11 @@
   // Callback that will be called when a valid sign in has been completed
   // through the dialog.
   mutable HandleGcpwSigninCompleteResult signin_callback_;
+
+  mutable CredentialProviderWebUIMessageHandler* handler_ = nullptr;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(CredentialProviderWebDialogDelegate);
 };
 
 bool ValidateSigninCompleteResult(const std::string& access_token,
diff --git a/chrome/browser/ui/startup/credential_provider_signin_dialog_win_browsertest.cc b/chrome/browser/ui/startup/credential_provider_signin_dialog_win_browsertest.cc
index b646ccfe..674c781 100644
--- a/chrome/browser/ui/startup/credential_provider_signin_dialog_win_browsertest.cc
+++ b/chrome/browser/ui/startup/credential_provider_signin_dialog_win_browsertest.cc
@@ -45,6 +45,7 @@
   content::WebContents* web_contents() { return web_contents_; }
   virtual void WaitForDialogToLoad();
 
+  views::WebDialogView* web_view_ = nullptr;
   content::WebContents* web_contents_ = nullptr;
 
  private:
@@ -129,14 +130,14 @@
 }
 
 void CredentialProviderSigninDialogWinDialogTest::ShowSigninDialog() {
-  views::WebDialogView* web_view = ShowCredentialProviderSigninDialog(
+  web_view_ = ShowCredentialProviderSigninDialog(
       base::CommandLine(base::CommandLine::NoProgram::NO_PROGRAM),
       browser()->profile(),
       base::BindOnce(
           &CredentialProviderSigninDialogWinDialogTest::HandleSignInComplete,
           base::Unretained(this)));
 
-  web_contents_ = web_view->web_contents();
+  web_contents_ = web_view_->web_contents();
 }
 
 void CredentialProviderSigninDialogWinDialogTest::HandleSignInComplete(
@@ -167,6 +168,29 @@
 }
 
 IN_PROC_BROWSER_TEST_F(CredentialProviderSigninDialogWinDialogTest,
+                       SimulateEscape) {
+  ShowSigninDialog();
+  WaitForDialogToLoad();
+
+  web_view_->GetWidget()->CloseWithReason(
+      views::Widget::ClosedReason::kEscKeyPressed);
+  base::RunLoop run_loop;
+  run_loop.RunUntilIdle();
+
+  EXPECT_TRUE(signin_complete_called_);
+  EXPECT_TRUE(result_value_.is_dict());
+  EXPECT_EQ(result_value_.DictSize(), 1u);
+  const base::DictionaryValue* result_dict;
+  EXPECT_TRUE(result_value_.GetAsDictionary(&result_dict));
+  int exit_code;
+  EXPECT_TRUE(
+      result_dict->GetInteger(credential_provider::kKeyExitCode, &exit_code));
+  EXPECT_EQ(credential_provider::kUiecAbort, exit_code);
+  EXPECT_TRUE(result_access_token_.empty());
+  EXPECT_TRUE(result_refresh_token_.empty());
+}
+
+IN_PROC_BROWSER_TEST_F(CredentialProviderSigninDialogWinDialogTest,
                        SendEmptySigninComplete) {
   ShowSigninDialog();
   WaitForDialogToLoad();
@@ -269,11 +293,13 @@
   const base::DictionaryValue* result_dict;
   EXPECT_TRUE(result_value_.GetAsDictionary(&result_dict));
   std::string id_in_dict;
-  EXPECT_TRUE(result_dict->GetString("id", &id_in_dict));
+  EXPECT_TRUE(result_dict->GetString(credential_provider::kKeyId, &id_in_dict));
   std::string email_in_dict;
-  EXPECT_TRUE(result_dict->GetString("email", &email_in_dict));
+  EXPECT_TRUE(
+      result_dict->GetString(credential_provider::kKeyEmail, &email_in_dict));
   std::string password_in_dict;
-  EXPECT_TRUE(result_dict->GetString("password", &password_in_dict));
+  EXPECT_TRUE(result_dict->GetString(credential_provider::kKeyPassword,
+                                     &password_in_dict));
 
   EXPECT_EQ(id_in_dict, test_data_storage_.GetSuccessId());
   EXPECT_EQ(email_in_dict, test_data_storage_.GetSuccessEmail());
@@ -311,14 +337,19 @@
   if (should_succeed) {
     EXPECT_GT(result_value_.DictSize(), 1u);
 
-    std::string id_in_dict =
-        result_value_.FindKeyOfType("id", base::Value::Type::STRING)
-            ->GetString();
+    std::string id_in_dict = result_value_
+                                 .FindKeyOfType(credential_provider::kKeyId,
+                                                base::Value::Type::STRING)
+                                 ->GetString();
     std::string email_in_dict =
-        result_value_.FindKeyOfType("email", base::Value::Type::STRING)
+        result_value_
+            .FindKeyOfType(credential_provider::kKeyEmail,
+                           base::Value::Type::STRING)
             ->GetString();
     std::string password_in_dict =
-        result_value_.FindKeyOfType("password", base::Value::Type::STRING)
+        result_value_
+            .FindKeyOfType(credential_provider::kKeyPassword,
+                           base::Value::Type::STRING)
             ->GetString();
 
     EXPECT_EQ(id_in_dict, test_data_storage_.GetSuccessId());
diff --git a/chrome/browser/ui/startup/credential_provider_signin_dialog_win_test_data.cc b/chrome/browser/ui/startup/credential_provider_signin_dialog_win_test_data.cc
index 74b950b..a67078e6 100644
--- a/chrome/browser/ui/startup/credential_provider_signin_dialog_win_test_data.cc
+++ b/chrome/browser/ui/startup/credential_provider_signin_dialog_win_test_data.cc
@@ -3,6 +3,7 @@
 // found in the LICENSE file.
 
 #include "chrome/browser/ui/startup/credential_provider_signin_dialog_win_test_data.h"
+#include "chrome/credential_provider/common/gcp_strings.h"
 
 #include <string>
 
@@ -36,20 +37,31 @@
 
 void CredentialProviderSigninDialogTestDataStorage::SetSigninPassword(
     const std::string& password) {
-  expected_success_signin_result_.SetKey("id", base::Value("gaia_user_id"));
-  expected_success_signin_result_.SetKey("password", base::Value(password));
-  expected_success_signin_result_.SetKey("email",
-                                         base::Value("foo_bar@gmail.com"));
-  expected_success_signin_result_.SetKey("access_token",
-                                         base::Value("access_token"));
-  expected_success_signin_result_.SetKey("refresh_token",
-                                         base::Value("refresh_token"));
-  expected_success_fetch_result_.SetKey("token_handle",
+  // Initialize expected successful result from oauth2 fetches.
+  expected_success_fetch_result_.SetKey(credential_provider::kKeyTokenHandle,
                                         base::Value("token_handle"));
-  expected_success_fetch_result_.SetKey("mdm_id_token",
+  expected_success_fetch_result_.SetKey(credential_provider::kKeyMdmIdToken,
                                         base::Value("mdm_token"));
-  expected_success_fetch_result_.SetKey("full_name", base::Value("Foo Bar"));
+  expected_success_fetch_result_.SetKey(credential_provider::kKeyFullname,
+                                        base::Value("Foo Bar"));
 
+  // Initialize expected successful full result sent to the credential provider.
+  expected_success_signin_result_.SetKey(credential_provider::kKeyId,
+                                         base::Value("gaia_user_id"));
+  expected_success_signin_result_.SetKey(credential_provider::kKeyPassword,
+                                         base::Value(password));
+  expected_success_signin_result_.SetKey(credential_provider::kKeyEmail,
+                                         base::Value("foo_bar@gmail.com"));
+  expected_success_signin_result_.SetKey(credential_provider::kKeyAccessToken,
+                                         base::Value("access_token"));
+  expected_success_signin_result_.SetKey(credential_provider::kKeyRefreshToken,
+                                         base::Value("refresh_token"));
+  expected_success_signin_result_.SetKey(
+      credential_provider::kKeyExitCode,
+      base::Value(credential_provider::kUiecSuccess));
+
+  // Merge with results from chrome://inline-signin to form the full
+  // result.
   expected_success_full_result_ = expected_success_signin_result_.Clone();
   expected_success_full_result_.MergeDictionary(
       &expected_success_fetch_result_);
@@ -65,16 +77,20 @@
     const std::string& refresh_token) {
   base::Value args(base::Value::Type::DICTIONARY);
   if (!email.empty())
-    args.SetKey("email", base::Value(email));
+    args.SetKey(credential_provider::kKeyEmail, base::Value(email));
   if (!password.empty())
-    args.SetKey("password", base::Value(password));
+    args.SetKey(credential_provider::kKeyPassword, base::Value(password));
   if (!id.empty())
-    args.SetKey("id", base::Value(id));
+    args.SetKey(credential_provider::kKeyId, base::Value(id));
   if (!refresh_token.empty())
-    args.SetKey("refresh_token", base::Value(refresh_token));
+    args.SetKey(credential_provider::kKeyRefreshToken,
+                base::Value(refresh_token));
   if (!access_token.empty())
-    args.SetKey("access_token", base::Value(access_token));
+    args.SetKey(credential_provider::kKeyAccessToken,
+                base::Value(access_token));
 
+  args.SetKey(credential_provider::kKeyExitCode,
+              base::Value(credential_provider::kUiecSuccess));
   return args;
 }
 
diff --git a/chrome/browser/ui/startup/credential_provider_signin_dialog_win_test_data.h b/chrome/browser/ui/startup/credential_provider_signin_dialog_win_test_data.h
index a479a214..c6cd4cb 100644
--- a/chrome/browser/ui/startup/credential_provider_signin_dialog_win_test_data.h
+++ b/chrome/browser/ui/startup/credential_provider_signin_dialog_win_test_data.h
@@ -93,8 +93,15 @@
     return result_value == success_value;
   }
 
+  // An expected successful result from chrome://inline-signin.
   base::Value expected_success_signin_result_;
+
+  // An expected successful result from oauth2 fetches for user info, token
+  // handle, and id token.
   base::Value expected_success_fetch_result_;
+
+  // An expected successful full result sent to the credential provider from
+  // GLS.
   base::Value expected_success_full_result_;
 };
 
diff --git a/chrome/browser/ui/views/bookmarks/bookmark_bar_view_test.cc b/chrome/browser/ui/views/bookmarks/bookmark_bar_view_test.cc
index 530fb8c..8733a18 100644
--- a/chrome/browser/ui/views/bookmarks/bookmark_bar_view_test.cc
+++ b/chrome/browser/ui/views/bookmarks/bookmark_bar_view_test.cc
@@ -421,6 +421,89 @@
   std::unique_ptr<ScopedTestingLocalState> local_state_;
 };
 
+class BookmarkBarViewDragTestBase : public BookmarkBarViewEventTestBase {
+ public:
+  BookmarkBarViewDragTestBase() = default;
+  ~BookmarkBarViewDragTestBase() override = default;
+
+  virtual void OnWidgetDragWillStart() = 0;
+
+  virtual void OnWidgetDragComplete() {
+    // All drag tests drag node f1a, so at the end of the test, if the node was
+    // dropped where it was expected, the dropped node should have f1a's URL.
+    EXPECT_EQ(f1a_url_, GetDroppedNode()->url());
+
+    Done();
+  }
+
+ protected:
+  // BookmarkBarViewEventTestBase:
+  void DoTestOnMessageLoop() override {
+    // Record the URL for node f1a.
+    f1a_url_ = model_->bookmark_bar_node()->GetChild(0)->GetChild(0)->url();
+
+    // Move the mouse to the first folder on the bookmark bar and press the
+    // mouse.
+    views::LabelButton* button = GetBookmarkButton(0);
+    ui_test_utils::MoveMouseToCenterAndPress(
+        button, ui_controls::LEFT, ui_controls::DOWN | ui_controls::UP,
+        CreateEventTask(this, &BookmarkBarViewDragTestBase::OnMenuOpened));
+  }
+
+  void OnMenuOpened() {
+    // Menu should be showing.
+    views::MenuItemView* menu = bb_view_->GetMenu();
+    ASSERT_NE(nullptr, menu);
+    views::SubmenuView* submenu = menu->GetSubmenu();
+    ASSERT_TRUE(submenu->IsShowing());
+
+    // Move mouse to center of node f1a and press button.
+    views::View* f1a = submenu->GetMenuItemAt(0);
+    ASSERT_NE(nullptr, f1a);
+    ui_test_utils::MoveMouseToCenterAndPress(
+        f1a, ui_controls::LEFT, ui_controls::DOWN,
+        CreateEventTask(this, &BookmarkBarViewDragTestBase::StartDrag));
+  }
+
+  // Called after the drag ends; returns the node the test thinks should be the
+  // dropped node.  This is used to verify that the dragged node was dropped in
+  // the expected position.
+  virtual const BookmarkNode* GetDroppedNode() const = 0;
+
+  // Returns the point the node should be dragged to, in screen coordinates.
+  virtual gfx::Point GetDragTargetInScreen() const = 0;
+
+ private:
+  void StartDrag() {
+    const gfx::Point target = GetDragTargetInScreen();
+#if defined(USE_AURA)
+    // TODO: fix this. Aura requires an additional mouse event to trigger drag
+    // and drop checking state.
+    EXPECT_TRUE(ui_controls::SendMouseMoveNotifyWhenDone(
+        target.x() + 10, target.y(),
+        CreateEventTask(this, &BookmarkBarViewDragTestBase::StartDrag2)));
+#else
+    EXPECT_TRUE(ui_controls::SendMouseMoveNotifyWhenDone(
+        target.x() + 10, target.y(),
+        CreateEventTask(this,
+                        &BookmarkBarViewDragTestBase::OnWidgetDragWillStart)));
+
+    // See comment above this method as to why we do this.
+    ScheduleMouseMoveInBackground(target.x(), target.y());
+#endif
+  }
+
+  void StartDrag2() {
+    const gfx::Point target = GetDragTargetInScreen();
+    EXPECT_TRUE(ui_controls::SendMouseMoveNotifyWhenDone(
+        target.x(), target.y(),
+        CreateEventTask(this,
+                        &BookmarkBarViewDragTestBase::OnWidgetDragWillStart)));
+  }
+
+  GURL f1a_url_;
+};
+
 #if !defined(OS_MACOSX)
 // The following tests were not enabled on Mac before. Consider enabling those
 // that are able to run on Mac (https://crbug.com/845342).
@@ -692,52 +775,10 @@
 VIEW_TEST(BookmarkBarViewTest4, ContextMenus)
 
 // Tests drag and drop within the same menu.
-class BookmarkBarViewTest5 : public BookmarkBarViewEventTestBase {
- protected:
-  void DoTestOnMessageLoop() override {
-    url_dragging_ =
-        model_->bookmark_bar_node()->GetChild(0)->GetChild(0)->url();
-
-    // Move the mouse to the first folder on the bookmark bar and press the
-    // mouse.
-    views::LabelButton* button = GetBookmarkButton(0);
-    ui_test_utils::MoveMouseToCenterAndPress(button, ui_controls::LEFT,
-        ui_controls::DOWN | ui_controls::UP,
-        CreateEventTask(this, &BookmarkBarViewTest5::Step2));
-  }
-
- private:
-  void Step2() {
-    // Menu should be showing.
-    views::MenuItemView* menu = bb_view_->GetMenu();
-    ASSERT_NE(nullptr, menu);
-    views::SubmenuView* submenu = menu->GetSubmenu();
-    ASSERT_TRUE(submenu->IsShowing());
-    views::MenuItemView* child_menu = submenu->GetMenuItemAt(0);
-    ASSERT_NE(nullptr, child_menu);
-
-    // Move mouse to center of menu and press button.
-    ui_test_utils::MoveMouseToCenterAndPress(child_menu, ui_controls::LEFT,
-        ui_controls::DOWN,
-        CreateEventTask(this, &BookmarkBarViewTest5::Step3));
-  }
-
-  void Step3() {
-    views::MenuItemView* target_menu =
-        bb_view_->GetMenu()->GetSubmenu()->GetMenuItemAt(1);
-    gfx::Point loc(1, target_menu->height() - 1);
-    views::View::ConvertPointToScreen(target_menu, &loc);
-
-    // Start a drag.
-    ASSERT_TRUE(ui_controls::SendMouseMoveNotifyWhenDone(
-        loc.x() + 10, loc.y(),
-        CreateEventTask(this, &BookmarkBarViewTest5::Step4)));
-
-    // See comment above this method as to why we do this.
-    ScheduleMouseMoveInBackground(loc.x(), loc.y());
-  }
-
-  void Step4() {
+class BookmarkBarViewTest5 : public BookmarkBarViewDragTestBase {
+ public:
+  // BookmarkBarViewDragTestBase:
+  void OnWidgetDragWillStart() override {
     // Drop the item so that it's now the second item.
     views::MenuItemView* target_menu =
         bb_view_->GetMenu()->GetSubmenu()->GetMenuItemAt(1);
@@ -747,16 +788,22 @@
 
     ASSERT_TRUE(ui_controls::SendMouseEventsNotifyWhenDone(
         ui_controls::LEFT, ui_controls::UP,
-        CreateEventTask(this, &BookmarkBarViewTest5::Step5)));
+        CreateEventTask(this, &BookmarkBarViewTest5::OnWidgetDragComplete)));
   }
 
-  void Step5() {
-    GURL url = model_->bookmark_bar_node()->GetChild(0)->GetChild(1)->url();
-    EXPECT_EQ(url_dragging_, url);
-    Done();
+ protected:
+  // BookmarkBarViewDragTestBase:
+  const BookmarkNode* GetDroppedNode() const override {
+    return model_->bookmark_bar_node()->GetChild(0)->GetChild(1);
   }
 
-  GURL url_dragging_;
+  gfx::Point GetDragTargetInScreen() const override {
+    views::MenuItemView* target_menu =
+        bb_view_->GetMenu()->GetSubmenu()->GetMenuItemAt(1);
+    gfx::Point target(1, target_menu->height() - 1);
+    views::View::ConvertPointToScreen(target_menu, &target);
+    return target;
+  }
 };
 // Flaky: https://crbug.com/758210
 VIEW_TEST(BookmarkBarViewTest5, DISABLED_DND)
@@ -793,108 +840,60 @@
               model_->bookmark_bar_node()->GetChild(6)->url());
     Done();
   }
-
-  GURL url_dragging_;
 };
 
 // If this flakes, disable and log details in http://crbug.com/523255.
 VIEW_TEST(BookmarkBarViewTest6, OpenMenuOnClickAndHold)
 
 // Tests drag and drop to different menu.
-class BookmarkBarViewTest7 : public BookmarkBarViewEventTestBase {
- protected:
-  void DoTestOnMessageLoop() override {
-    url_dragging_ =
-        model_->bookmark_bar_node()->GetChild(0)->GetChild(0)->url();
+class BookmarkBarViewTest7 : public BookmarkBarViewDragTestBase {
+ public:
+  // BookmarkBarViewDragTestBase:
+  void OnWidgetDragWillStart() override { OnDropMenuShown(); }
 
-    // Move the mouse to the first folder on the bookmark bar and press the
-    // mouse.
-    views::LabelButton* button = GetBookmarkButton(0);
-    ui_test_utils::MoveMouseToCenterAndPress(button, ui_controls::LEFT,
-        ui_controls::DOWN | ui_controls::UP,
-        CreateEventTask(this, &BookmarkBarViewTest7::Step2));
+  void OnWidgetDragComplete() override {
+    // The button should be in normal state now.
+    EXPECT_EQ(views::Button::STATE_NORMAL,
+              bb_view_->other_bookmarks_button()->state());
+
+    BookmarkBarViewDragTestBase::OnWidgetDragComplete();
   }
 
- private:
-  void Step2() {
-    // Menu should be showing.
-    views::MenuItemView* menu = bb_view_->GetMenu();
-    ASSERT_NE(nullptr, menu);
-    views::SubmenuView* submenu = menu->GetSubmenu();
-    ASSERT_TRUE(submenu->IsShowing());
-    views::MenuItemView* child_menu = submenu->GetMenuItemAt(0);
-    ASSERT_NE(nullptr, child_menu);
-
-    // Move mouse to center of menu and press button.
-    ui_test_utils::MoveMouseToCenterAndPress(child_menu, ui_controls::LEFT,
-        ui_controls::DOWN,
-        CreateEventTask(this, &BookmarkBarViewTest7::Step3));
-  }
-
-  void Step3() {
-    // Drag over other button.
-    gfx::Point loc = ui_test_utils::GetCenterInScreenCoordinates(
-        bb_view_->other_bookmarks_button());
-
-#if defined(USE_AURA)
-    // TODO: fix this. Aura requires an additional mouse event to trigger drag
-    // and drop checking state.
-    ASSERT_TRUE(ui_controls::SendMouseMoveNotifyWhenDone(
-        loc.x() + 10, loc.y(),
-        base::BindOnce(&BookmarkBarViewTest7::Step3A, base::Unretained(this))));
-#else
-    // Start a drag.
-    ASSERT_TRUE(ui_controls::SendMouseMoveNotifyWhenDone(
-        loc.x() + 10, loc.y(),
-        base::BindOnce(&BookmarkBarViewTest7::Step4, base::Unretained(this))));
-
-    // See comment above this method as to why we do this.
-    ScheduleMouseMoveInBackground(loc.x(), loc.y());
-#endif
-  }
-
-  void Step3A() {
-    // Drag over other button.
-    gfx::Point loc = ui_test_utils::GetCenterInScreenCoordinates(
-        bb_view_->other_bookmarks_button());
-
-    ASSERT_TRUE(ui_controls::SendMouseMoveNotifyWhenDone(
-        loc.x(), loc.y(),
-        base::BindOnce(&BookmarkBarViewTest7::Step4, base::Unretained(this))));
-  }
-
-  void Step4() {
+  void OnDropMenuShown() {
     views::MenuItemView* drop_menu = bb_view_->GetDropMenu();
     ASSERT_NE(nullptr, drop_menu);
     views::SubmenuView* drop_submenu = drop_menu->GetSubmenu();
     ASSERT_TRUE(drop_submenu->IsShowing());
 
     // The button should be highlighted now.
-    views::LabelButton* other_button = bb_view_->other_bookmarks_button();
-    EXPECT_EQ(views::Button::STATE_PRESSED, other_button->state());
+    EXPECT_EQ(views::Button::STATE_PRESSED,
+              bb_view_->other_bookmarks_button()->state());
 
     views::MenuItemView* target_menu = drop_submenu->GetMenuItemAt(0);
     gfx::Point loc(1, 1);
     views::View::ConvertPointToScreen(target_menu, &loc);
     ASSERT_TRUE(ui_controls::SendMouseMoveNotifyWhenDone(
-        loc.x(), loc.y(), CreateEventTask(this, &BookmarkBarViewTest7::Step5)));
+        loc.x(), loc.y(),
+        CreateEventTask(this, &BookmarkBarViewTest7::OnDragEntered)));
   }
 
-  void Step5() {
+ protected:
+  // BookmarkBarViewDragTestBase:
+  const BookmarkNode* GetDroppedNode() const override {
+    return model_->other_node()->GetChild(0);
+  }
+
+  gfx::Point GetDragTargetInScreen() const override {
+    return ui_test_utils::GetCenterInScreenCoordinates(
+        bb_view_->other_bookmarks_button());
+  }
+
+ private:
+  void OnDragEntered() {
     ASSERT_TRUE(ui_controls::SendMouseEventsNotifyWhenDone(
         ui_controls::LEFT, ui_controls::UP,
-        CreateEventTask(this, &BookmarkBarViewTest7::Step6)));
+        CreateEventTask(this, &BookmarkBarViewTest7::OnWidgetDragComplete)));
   }
-
-  void Step6() {
-    EXPECT_EQ(url_dragging_, model_->other_node()->GetChild(0)->url());
-    // The button should be in normal state now.
-    views::LabelButton* other_button = bb_view_->other_bookmarks_button();
-    EXPECT_EQ(views::Button::STATE_NORMAL, other_button->state());
-    Done();
-  }
-
-  GURL url_dragging_;
 };
 
 #if !defined(OS_WIN)
@@ -906,81 +905,25 @@
 
 // Drags from one menu to next so that original menu closes, then back to
 // original menu.
-class BookmarkBarViewTest8 : public BookmarkBarViewEventTestBase {
- protected:
-  void DoTestOnMessageLoop() override {
-    url_dragging_ =
-        model_->bookmark_bar_node()->GetChild(0)->GetChild(0)->url();
+class BookmarkBarViewTest8 : public BookmarkBarViewDragTestBase {
+ public:
+  // BookmarkBarViewDragTestBase:
+  void OnWidgetDragWillStart() override { OnDropMenuShown(); }
 
-    // Move the mouse to the first folder on the bookmark bar and press the
-    // mouse.
-    views::LabelButton* button = GetBookmarkButton(0);
-    ui_test_utils::MoveMouseToCenterAndPress(button, ui_controls::LEFT,
-        ui_controls::DOWN | ui_controls::UP,
-        CreateEventTask(this, &BookmarkBarViewTest8::Step2));
-  }
-
- private:
-  void Step2() {
-    // Menu should be showing.
-    views::MenuItemView* menu = bb_view_->GetMenu();
-    ASSERT_NE(nullptr, menu);
-    views::SubmenuView* submenu = menu->GetSubmenu();
-    ASSERT_TRUE(submenu->IsShowing());
-    views::MenuItemView* child_menu = submenu->GetMenuItemAt(0);
-    ASSERT_NE(nullptr, child_menu);
-
-    // Move mouse to center of menu and press button.
-    ui_test_utils::MoveMouseToCenterAndPress(child_menu, ui_controls::LEFT,
-        ui_controls::DOWN,
-        CreateEventTask(this, &BookmarkBarViewTest8::Step3));
-  }
-
-  void Step3() {
-    // Drag over other button.
-    gfx::Point loc = ui_test_utils::GetCenterInScreenCoordinates(
-        bb_view_->other_bookmarks_button());
-
-    // Start a drag.
-#if defined(USE_AURA)
-    // TODO: fix this. Aura requires an additional mouse event to trigger drag
-    // and drop checking state.
-    ASSERT_TRUE(ui_controls::SendMouseMoveNotifyWhenDone(
-        loc.x() + 10, loc.y(),
-        base::BindOnce(&BookmarkBarViewTest8::Step3A, base::Unretained(this))));
-#else
-    ASSERT_TRUE(ui_controls::SendMouseMoveNotifyWhenDone(
-        loc.x() + 10, loc.y(),
-        base::BindOnce(&BookmarkBarViewTest8::Step4, base::Unretained(this))));
-    // See comment above this method as to why we do this.
-    ScheduleMouseMoveInBackground(loc.x(), loc.y());
-#endif
-  }
-
-  void Step3A() {
-    // Drag over other button.
-    gfx::Point loc = ui_test_utils::GetCenterInScreenCoordinates(
-        bb_view_->other_bookmarks_button());
-
-    ASSERT_TRUE(ui_controls::SendMouseMoveNotifyWhenDone(
-        loc.x() + 10, loc.y(),
-        base::BindOnce(&BookmarkBarViewTest8::Step4, base::Unretained(this))));
-  }
-
-  void Step4() {
+  void OnDropMenuShown() {
     views::MenuItemView* drop_menu = bb_view_->GetDropMenu();
     ASSERT_NE(nullptr, drop_menu);
     ASSERT_TRUE(drop_menu->GetSubmenu()->IsShowing());
 
     // Now drag back over first menu.
-    gfx::Point loc =
-        ui_test_utils::GetCenterInScreenCoordinates(GetBookmarkButton(0));
+    views::LabelButton* button = GetBookmarkButton(0);
+    gfx::Point loc = ui_test_utils::GetCenterInScreenCoordinates(button);
     ASSERT_TRUE(ui_controls::SendMouseMoveNotifyWhenDone(
         loc.x(), loc.y(),
-        base::BindOnce(&BookmarkBarViewTest8::Step5, base::Unretained(this))));
+        CreateEventTask(this, &BookmarkBarViewTest8::OnDragEntered)));
   }
 
-  void Step5() {
+  void OnDragEntered() {
     // Drop on folder F11.
     views::MenuItemView* drop_menu = bb_view_->GetDropMenu();
     ASSERT_NE(nullptr, drop_menu);
@@ -990,18 +933,19 @@
     views::MenuItemView* target_menu = drop_submenu->GetMenuItemAt(1);
     ui_test_utils::MoveMouseToCenterAndPress(
         target_menu, ui_controls::LEFT, ui_controls::UP,
-        CreateEventTask(this, &BookmarkBarViewTest8::Step6));
+        CreateEventTask(this, &BookmarkBarViewTest8::OnWidgetDragComplete));
   }
 
-  void Step6() {
-    // Make sure drop was processed.
-    GURL final_url = model_->bookmark_bar_node()->GetChild(0)->GetChild(0)->
-        GetChild(1)->url();
-    EXPECT_EQ(url_dragging_, final_url);
-    Done();
+ protected:
+  // BookmarkBarViewDragTestBase:
+  const BookmarkNode* GetDroppedNode() const override {
+    return model_->bookmark_bar_node()->GetChild(0)->GetChild(0)->GetChild(1);
   }
 
-  GURL url_dragging_;
+  gfx::Point GetDragTargetInScreen() const override {
+    return ui_test_utils::GetCenterInScreenCoordinates(
+        bb_view_->other_bookmarks_button());
+  }
 };
 
 #if !defined(OS_WIN)
@@ -1994,51 +1938,15 @@
 
 // Test that closing the source browser window while dragging a bookmark does
 // not cause a crash.
-class BookmarkBarViewTest22 : public BookmarkBarViewEventTestBase {
- protected:
-  void DoTestOnMessageLoop() override {
-    // Move the mouse to the first folder on the bookmark bar and press the
-    // mouse.
-    views::LabelButton* button = GetBookmarkButton(0);
-    ui_test_utils::MoveMouseToCenterAndPress(
-        button, ui_controls::LEFT, ui_controls::DOWN | ui_controls::UP,
-        CreateEventTask(this, &BookmarkBarViewTest22::Step2));
+class BookmarkBarViewTest22 : public BookmarkBarViewDragTestBase {
+ public:
+  // BookmarkBarViewDragTestBase:
+  void OnWidgetDragWillStart() override {
+    OnDragEntered();
+    OnWidgetDestroyed();
   }
 
- private:
-  void Step2() {
-    // Menu should be showing.
-    views::MenuItemView* drop_menu = bb_view_->GetDropMenu();
-    ASSERT_NE(nullptr, drop_menu);
-    views::SubmenuView* drop_submenu = drop_menu->GetSubmenu();
-    ASSERT_TRUE(drop_submenu->IsShowing());
-
-    views::MenuItemView* child_menu = drop_submenu->GetMenuItemAt(0);
-    ASSERT_NE(nullptr, child_menu);
-
-    // Move mouse to center of menu and press button.
-    ui_test_utils::MoveMouseToCenterAndPress(
-        child_menu, ui_controls::LEFT, ui_controls::DOWN,
-        CreateEventTask(this, &BookmarkBarViewTest22::Step3));
-  }
-
-  void Step3() {
-    views::MenuItemView* target_menu =
-        bb_view_->GetMenu()->GetSubmenu()->GetMenuItemAt(1);
-    gfx::Point loc(1, target_menu->height() - 1);
-    views::View::ConvertPointToScreen(target_menu, &loc);
-
-    // Start a drag.
-    ASSERT_TRUE(ui_controls::SendMouseMoveNotifyWhenDone(
-        loc.x() + 10, loc.y(),
-        CreateEventTask(this, &BookmarkBarViewTest22::Step4)));
-    ScheduleMouseMoveInBackground(loc.x(), loc.y());
-  }
-
-  void Step4() {
-    window_->Close();
-    window_ = NULL;
-
+  void OnWidgetDestroyed() {
 #if defined(OS_CHROMEOS)
     ASSERT_TRUE(ui_controls::SendMouseEventsNotifyWhenDone(
         ui_controls::LEFT, ui_controls::UP,
@@ -2048,6 +1956,23 @@
     Done();
 #endif
   }
+
+ protected:
+  void OnDragEntered() {
+    window_->Close();
+    window_ = NULL;
+  }
+
+  // BookmarkBarViewDragTestBase:
+  const BookmarkNode* GetDroppedNode() const override {
+    // This test doesn't check what happens on drop.
+    return nullptr;
+  }
+
+  gfx::Point GetDragTargetInScreen() const override {
+    return ui_test_utils::GetCenterInScreenCoordinates(
+        bb_view_->GetMenu()->GetSubmenu()->GetMenuItemAt(1));
+  }
 };
 
 // This test times out on Windows. TODO(pkotwicz): Find out why.
diff --git a/chrome/browser/ui/views/extensions/extension_uninstall_dialog_view.cc b/chrome/browser/ui/views/extensions/extension_uninstall_dialog_view.cc
index 884e8f4..2660599 100644
--- a/chrome/browser/ui/views/extensions/extension_uninstall_dialog_view.cc
+++ b/chrome/browser/ui/views/extensions/extension_uninstall_dialog_view.cc
@@ -36,8 +36,30 @@
 #include "ui/views/widget/widget.h"
 #include "ui/views/window/dialog_delegate.h"
 
+#if defined(OS_CHROMEOS)
+#include "ash/public/cpp/shell_window_ids.h"
+#include "chrome/browser/ui/ash/ash_util.h"
+#endif
+
 namespace {
 
+#if defined(OS_CHROMEOS)
+views::Widget* CreateChromeOSAppListParentedDialog(
+    views::DialogDelegateView* delegate_view) {
+  views::Widget* widget = new views::Widget;
+  views::Widget::InitParams params =
+      views::DialogDelegate::GetDialogWidgetInitParams(
+          delegate_view, nullptr /* context */, nullptr /* parent */,
+          gfx::Rect() /* bounds */);
+
+  ash_util::SetupWidgetInitParamsForContainer(
+      &params, ash::kShellWindowId_AppListContainer);
+
+  widget->Init(params);
+  return widget;
+}
+#endif
+
 ToolbarActionView* GetExtensionAnchorView(const std::string& extension_id,
                                           gfx::NativeWindow window) {
   BrowserView* browser_view =
@@ -147,6 +169,14 @@
   if (anchor_view) {
     views::BubbleDialogDelegateView::CreateBubble(view_)->Show();
   } else {
+#if defined(OS_CHROMEOS)
+    // On ChromeOS, the uninstall dialog can be created from the App List, so we
+    // need to attach the AppList window as the parent window.
+    if (uninstall_source() == extensions::UNINSTALL_SOURCE_APP_LIST) {
+      CreateChromeOSAppListParentedDialog(view_)->Show();
+      return;
+    }
+#endif
     constrained_window::CreateBrowserModalDialogViews(view_, parent())->Show();
   }
 }
diff --git a/chrome/browser/ui/views/extensions/extension_uninstall_dialog_view_browsertest.cc b/chrome/browser/ui/views/extensions/extension_uninstall_dialog_view_browsertest.cc
index 10c6c9b..598e8e74 100644
--- a/chrome/browser/ui/views/extensions/extension_uninstall_dialog_view_browsertest.cc
+++ b/chrome/browser/ui/views/extensions/extension_uninstall_dialog_view_browsertest.cc
@@ -27,6 +27,15 @@
 #include "extensions/common/extension_urls.h"
 #include "extensions/common/value_builder.h"
 
+#if defined(OS_CHROMEOS)
+#include "ash/public/cpp/shell_window_ids.h"
+#include "ash/public/interfaces/constants.mojom.h"
+#include "ash/public/interfaces/shell_test_api.test-mojom-test-utils.h"
+#include "content/public/common/service_manager_connection.h"
+#include "services/service_manager/public/cpp/connector.h"
+#include "ui/aura/test/mus/change_completion_waiter.h"
+#endif
+
 namespace {
 
 const char kUninstallUrl[] = "https://www.google.com/";
@@ -53,6 +62,25 @@
                              std::make_unique<base::Value>(kUninstallUrl));
 }
 
+#if defined(OS_CHROMEOS)
+// Returns the number of child windows in the AppList container on ChromeOS.
+// Blocks until the ash service responds.
+int GetWindowCountForAppListContainer() {
+  // Wait for window visibility to stabilize.
+  aura::test::WaitForAllChangesToComplete();
+
+  ash::mojom::ShellTestApiPtr shell_test_api;
+  content::ServiceManagerConnection::GetForProcess()
+      ->GetConnector()
+      ->BindInterface(ash::mojom::kServiceName, &shell_test_api);
+  ash::mojom::ShellTestApiAsyncWaiter waiter(shell_test_api.get());
+  int child_window_count = 0;
+  waiter.GetChildWindowCountInContainer(ash::kShellWindowId_AppListContainer,
+                                        &child_window_count);
+  return child_window_count;
+}
+#endif
+
 class TestExtensionUninstallDialogDelegate
     : public extensions::ExtensionUninstallDialog::Delegate {
  public:
@@ -136,6 +164,54 @@
 }
 
 #if defined(OS_CHROMEOS)
+IN_PROC_BROWSER_TEST_F(ExtensionUninstallDialogViewBrowserTest,
+                       ParentWindowIsAppList) {
+  scoped_refptr<const extensions::Extension> extension(BuildTestExtension());
+  extensions::ExtensionSystem::Get(browser()->profile())
+      ->extension_service()
+      ->AddExtension(extension.get());
+
+  // Initially the AppList window should have no children.
+  EXPECT_EQ(0, GetWindowCountForAppListContainer());
+
+  // Open the extension uninstall dialog without a browser parent window. This
+  // occurs when the dialog is opened from the Launcher.
+  std::unique_ptr<extensions::ExtensionUninstallDialog> dialog;
+  dialog = extensions::ExtensionUninstallDialog::Create(browser()->profile(),
+                                                        nullptr, nullptr);
+  dialog->ConfirmUninstall(extension.get(),
+                           extensions::UNINSTALL_REASON_FOR_TESTING,
+                           extensions::UNINSTALL_SOURCE_APP_LIST);
+
+  // Verify that a child dialog window has been added to the AppList as a child.
+  EXPECT_EQ(1, GetWindowCountForAppListContainer());
+}
+
+IN_PROC_BROWSER_TEST_F(ExtensionUninstallDialogViewBrowserTest,
+                       ParentWindowIsBrowser) {
+  scoped_refptr<const extensions::Extension> extension(BuildTestExtension());
+  extensions::ExtensionSystem::Get(browser()->profile())
+      ->extension_service()
+      ->AddExtension(extension.get());
+
+  // When the uninstall dialog is launched from a browser, the AppList should
+  // never be the parent.
+  EXPECT_EQ(0, GetWindowCountForAppListContainer());
+
+  // Open the extension uninstall dialog with a browser parent window. This is
+  // occurs when the dialog is opened from chrome://extensions rather than the
+  // launcher.
+  std::unique_ptr<extensions::ExtensionUninstallDialog> dialog;
+  dialog = extensions::ExtensionUninstallDialog::Create(
+      browser()->profile(), browser()->window()->GetNativeWindow(), nullptr);
+  dialog->ConfirmUninstall(extension.get(),
+                           extensions::UNINSTALL_REASON_FOR_TESTING,
+                           extensions::UNINSTALL_SOURCE_FOR_TESTING);
+
+  // Verify that the uninstall dialog is not parented to the AppList.
+  EXPECT_EQ(0, GetWindowCountForAppListContainer());
+}
+
 // Test that we don't crash when uninstalling an extension from a bookmark app
 // window in Ash. Context: crbug.com/825554
 IN_PROC_BROWSER_TEST_F(ExtensionUninstallDialogViewBrowserTest,
diff --git a/chrome/browser/ui/views/frame/app_menu_button.cc b/chrome/browser/ui/views/frame/app_menu_button.cc
index 676dd1e..05e7653f 100644
--- a/chrome/browser/ui/views/frame/app_menu_button.cc
+++ b/chrome/browser/ui/views/frame/app_menu_button.cc
@@ -11,14 +11,24 @@
 #include "chrome/browser/ui/views/frame/app_menu_button_observer.h"
 #include "chrome/browser/ui/views/toolbar/app_menu.h"
 #include "chrome/browser/ui/views/toolbar/toolbar_ink_drop_util.h"
+#include "ui/views/view_class_properties.h"
 
 AppMenuButton::AppMenuButton(views::MenuButtonListener* menu_button_listener)
     : views::MenuButton(base::string16(),
                         menu_button_listener,
-                        CONTEXT_TOOLBAR_BUTTON) {}
+                        CONTEXT_TOOLBAR_BUTTON) {
+  SetProperty(views::kInternalPaddingKey, new gfx::Insets());
+}
 
 AppMenuButton::~AppMenuButton() {}
 
+void AppMenuButton::OnBoundsChanged(const gfx::Rect& previous_bounds) {
+  // TODO(pbos): Consolidate with ToolbarButton::OnBoundsChanged.
+  SetToolbarButtonHighlightPath(this, *GetProperty(views::kInternalPaddingKey));
+
+  views::MenuButton::OnBoundsChanged(previous_bounds);
+}
+
 SkColor AppMenuButton::GetInkDropBaseColor() const {
   return GetToolbarInkDropBaseColor(this);
 }
diff --git a/chrome/browser/ui/views/frame/app_menu_button.h b/chrome/browser/ui/views/frame/app_menu_button.h
index dde1c17..e1207afe 100644
--- a/chrome/browser/ui/views/frame/app_menu_button.h
+++ b/chrome/browser/ui/views/frame/app_menu_button.h
@@ -30,6 +30,7 @@
   ~AppMenuButton() override;
 
   // views::MenuButton:
+  void OnBoundsChanged(const gfx::Rect& previous_bounds) override;
   SkColor GetInkDropBaseColor() const override;
 
   void AddObserver(AppMenuButtonObserver* observer);
diff --git a/chrome/browser/ui/views/frame/hosted_app_menu_button.cc b/chrome/browser/ui/views/frame/hosted_app_menu_button.cc
index 8290fe9..4855669 100644
--- a/chrome/browser/ui/views/frame/hosted_app_menu_button.cc
+++ b/chrome/browser/ui/views/frame/hosted_app_menu_button.cc
@@ -14,6 +14,7 @@
 #include "chrome/browser/ui/views/frame/browser_view.h"
 #include "chrome/browser/ui/views/frame/hosted_app_button_container.h"
 #include "chrome/browser/ui/views/toolbar/app_menu.h"
+#include "chrome/browser/ui/views/toolbar/toolbar_ink_drop_util.h"
 #include "chrome/grit/generated_resources.h"
 #include "ui/base/hit_test.h"
 #include "ui/base/l10n/l10n_util.h"
@@ -22,6 +23,7 @@
 #include "ui/views/animation/ink_drop.h"
 #include "ui/views/border.h"
 #include "ui/views/controls/button/menu_button.h"
+#include "ui/views/view_class_properties.h"
 #include "ui/views/window/hit_test_utils.h"
 
 HostedAppMenuButton::HostedAppMenuButton(BrowserView* browser_view)
@@ -44,6 +46,9 @@
   int size = GetLayoutConstant(HOSTED_APP_MENU_BUTTON_SIZE);
   SetMinSize(gfx::Size(size, size));
   SetHorizontalAlignment(gfx::ALIGN_CENTER);
+
+  constexpr gfx::Insets kInkDropInsets(2);
+  *GetProperty(views::kInternalPaddingKey) = kInkDropInsets;
 }
 
 HostedAppMenuButton::~HostedAppMenuButton() {}
diff --git a/chrome/browser/ui/views/media_router/cast_dialog_no_sinks_view.cc b/chrome/browser/ui/views/media_router/cast_dialog_no_sinks_view.cc
index bc864e6..47a6269 100644
--- a/chrome/browser/ui/views/media_router/cast_dialog_no_sinks_view.cc
+++ b/chrome/browser/ui/views/media_router/cast_dialog_no_sinks_view.cc
@@ -86,6 +86,7 @@
   views::ImageButton* help_icon_ptr = help_icon.get();
   const SkColor icon_color = help_icon->GetNativeTheme()->GetSystemColor(
       ui::NativeTheme::kColorId_DefaultIconColor);
+  help_icon->SetInstallFocusRingOnFocus(true);
   help_icon->SetImage(views::Button::STATE_NORMAL,
                       gfx::CreateVectorIcon(::vector_icons::kHelpOutlineIcon,
                                             kPrimaryIconSize, icon_color));
diff --git a/chrome/browser/ui/views/media_router/cast_dialog_sink_button.cc b/chrome/browser/ui/views/media_router/cast_dialog_sink_button.cc
index fdff4a6..abbf004 100644
--- a/chrome/browser/ui/views/media_router/cast_dialog_sink_button.cc
+++ b/chrome/browser/ui/views/media_router/cast_dialog_sink_button.cc
@@ -31,64 +31,6 @@
 
 namespace {
 
-class StopButton : public views::LabelButton {
- public:
-  StopButton(CastDialogSinkButton* owner,
-             views::ButtonListener* button_listener,
-             const UIMediaSink& sink,
-             int button_tag,
-             bool enabled)
-      : views::LabelButton(button_listener, base::string16()), owner_(owner) {
-    const gfx::ImageSkia icon = CreateVectorIcon(
-        kGenericStopIcon, kPrimaryIconSize, gfx::kGoogleBlue500);
-    SetImage(views::Button::STATE_NORMAL, icon);
-    SetInkDropMode(InkDropMode::ON);
-    set_tag(button_tag);
-    SetBorder(views::CreateEmptyBorder(gfx::Insets(kPrimaryIconBorderWidth)));
-    SetEnabled(enabled);
-    // Make it possible to navigate to this button by pressing the tab key.
-    SetFocusBehavior(FocusBehavior::ALWAYS);
-    // Remove the outlines drawn when the button is in focus.
-    SetInstallFocusRingOnFocus(false);
-
-    SetAccessibleName(l10n_util::GetStringFUTF16(
-        IDS_MEDIA_ROUTER_STOP_CASTING_BUTTON_ACCESSIBLE_NAME,
-        sink.friendly_name, sink.status_text));
-  }
-
-  ~StopButton() override = default;
-
-  SkColor GetInkDropBaseColor() const override {
-    return views::style::GetColor(*this, views::style::CONTEXT_BUTTON,
-                                  STYLE_SECONDARY);
-  }
-
-  std::unique_ptr<views::InkDropHighlight> CreateInkDropHighlight()
-      const override {
-    return std::make_unique<views::InkDropHighlight>(
-        size(), height() / 2,
-        gfx::PointF(GetMirroredRect(GetLocalBounds()).CenterPoint()),
-        GetInkDropBaseColor());
-  }
-
-  bool CanProcessEventsWithinSubtree() const override { return true; }
-
-  // views::Button:
-  void StateChanged(ButtonState old_state) override {
-    if (state() == Button::STATE_HOVERED) {
-      owner_->OverrideStatusText(
-          l10n_util::GetStringUTF16(IDS_MEDIA_ROUTER_STOP_CASTING));
-    } else if (old_state == Button::STATE_HOVERED) {
-      owner_->RestoreStatusText();
-    }
-  }
-
- private:
-  CastDialogSinkButton* const owner_;
-
-  DISALLOW_COPY_AND_ASSIGN(StopButton);
-};
-
 gfx::ImageSkia CreateSinkIcon(SinkIconType icon_type, bool enabled = true) {
   const gfx::VectorIcon* vector_icon;
   switch (icon_type) {
@@ -128,6 +70,15 @@
   return CreateSinkIcon(icon_type, false);
 }
 
+std::unique_ptr<views::ImageView> CreatePrimaryIconView(
+    const gfx::ImageSkia& image) {
+  auto icon_view = std::make_unique<views::ImageView>();
+  icon_view->SetImage(image);
+  icon_view->SetBorder(
+      views::CreateEmptyBorder(gfx::Insets(kPrimaryIconBorderWidth)));
+  return icon_view;
+}
+
 std::unique_ptr<views::View> CreatePrimaryIconForSink(
     CastDialogSinkButton* sink_button,
     views::ButtonListener* button_listener,
@@ -135,27 +86,19 @@
     int button_tag) {
   // The stop button has the highest priority, and the issue icon comes second.
   if (sink.state == UIMediaSinkState::CONNECTED) {
-    return std::make_unique<StopButton>(
-        sink_button, button_listener, sink, button_tag,
-        sink.state == UIMediaSinkState::CONNECTED);
+    return CreatePrimaryIconView(gfx::CreateVectorIcon(
+        kGenericStopIcon, kPrimaryIconSize, gfx::kGoogleBlue500));
   } else if (sink.issue) {
-    auto icon_view = std::make_unique<views::ImageView>();
-    const SkColor icon_color = icon_view->GetNativeTheme()->GetSystemColor(
-        ui::NativeTheme::kColorId_DefaultIconColor);
-    icon_view->SetImage(CreateVectorIcon(::vector_icons::kInfoOutlineIcon,
-                                         kPrimaryIconSize, icon_color));
-    icon_view->SetBorder(
-        views::CreateEmptyBorder(gfx::Insets(kPrimaryIconBorderWidth)));
-    return icon_view;
+    const SkColor icon_color =
+        ui::NativeTheme::GetInstanceForNativeUi()->GetSystemColor(
+            ui::NativeTheme::kColorId_DefaultIconColor);
+    return CreatePrimaryIconView(gfx::CreateVectorIcon(
+        ::vector_icons::kInfoOutlineIcon, kPrimaryIconSize, icon_color));
   } else if (sink.state == UIMediaSinkState::CONNECTING ||
              sink.state == UIMediaSinkState::DISCONNECTING) {
     return CreateThrobber();
   }
-  auto icon_view = std::make_unique<views::ImageView>();
-  icon_view->SetImage(CreateSinkIcon(sink.icon_type));
-  icon_view->SetBorder(
-      views::CreateEmptyBorder(gfx::Insets(kPrimaryIconBorderWidth)));
-  return icon_view;
+  return CreatePrimaryIconView(CreateSinkIcon(sink.icon_type));
 }
 
 base::string16 GetStatusTextForSink(const UIMediaSink& sink) {
@@ -191,7 +134,8 @@
           /** secondary_icon_view */ nullptr),
       sink_(sink) {
   set_tag(button_tag);
-  SetEnabled(sink.state == UIMediaSinkState::AVAILABLE);
+  SetEnabled(sink.state == UIMediaSinkState::AVAILABLE ||
+             sink.state == UIMediaSinkState::CONNECTED);
 }
 
 CastDialogSinkButton::~CastDialogSinkButton() = default;
@@ -264,4 +208,17 @@
   }
 }
 
+void CastDialogSinkButton::OnFocus() {
+  HoverButton::OnFocus();
+  if (sink_.state == UIMediaSinkState::CONNECTED) {
+    OverrideStatusText(
+        l10n_util::GetStringUTF16(IDS_MEDIA_ROUTER_STOP_CASTING));
+  }
+}
+
+void CastDialogSinkButton::OnBlur() {
+  if (sink_.state == UIMediaSinkState::CONNECTED)
+    RestoreStatusText();
+}
+
 }  // namespace media_router
diff --git a/chrome/browser/ui/views/media_router/cast_dialog_sink_button.h b/chrome/browser/ui/views/media_router/cast_dialog_sink_button.h
index 17aef41..2a9c6db 100644
--- a/chrome/browser/ui/views/media_router/cast_dialog_sink_button.h
+++ b/chrome/browser/ui/views/media_router/cast_dialog_sink_button.h
@@ -31,6 +31,8 @@
   void OnMouseReleased(const ui::MouseEvent& event) override;
   void OnEnabledChanged() override;
   void RequestFocus() override;
+  void OnFocus() override;
+  void OnBlur() override;
 
   const UIMediaSink& sink() const { return sink_; }
 
diff --git a/chrome/browser/ui/views/media_router/cast_dialog_view.cc b/chrome/browser/ui/views/media_router/cast_dialog_view.cc
index 818c05e..e137e575 100644
--- a/chrome/browser/ui/views/media_router/cast_dialog_view.cc
+++ b/chrome/browser/ui/views/media_router/cast_dialog_view.cc
@@ -440,7 +440,11 @@
 }
 
 void CastDialogView::DisableUnsupportedSinks() {
+  // Go through the AVAILABLE sinks and enable or disable them depending on
+  // whether they support the selected cast mode.
   for (CastDialogSinkButton* sink_button : sink_buttons_) {
+    if (sink_button->sink().state != UIMediaSinkState::AVAILABLE)
+      continue;
     const bool enable = GetCastModeToUse(sink_button->sink()).has_value();
     sink_button->SetEnabled(enable);
   }
diff --git a/chrome/browser/ui/views/menu_view_drag_and_drop_test.cc b/chrome/browser/ui/views/menu_view_drag_and_drop_test.cc
index 1f0152a..cbaed71 100644
--- a/chrome/browser/ui/views/menu_view_drag_and_drop_test.cc
+++ b/chrome/browser/ui/views/menu_view_drag_and_drop_test.cc
@@ -142,6 +142,7 @@
  protected:
   // MenuTestBase:
   void BuildMenu(views::MenuItemView* menu) override;
+  void DoTestWithMenuOpen() override;
 
   TestTargetView* target_view() { return target_view_; }
   bool asked_to_close() const { return asked_to_close_; }
@@ -192,6 +193,24 @@
   menu->AppendMenuItemWithLabel(3, base::ASCIIToUTF16("item 3"));
 }
 
+void MenuViewDragAndDropTest::DoTestWithMenuOpen() {
+  // Sanity checks: We should be showing the menu, it should have three
+  // children, and the first of those children should have a nested view of the
+  // TestTargetView.
+  views::SubmenuView* submenu = menu()->GetSubmenu();
+  ASSERT_TRUE(submenu);
+  ASSERT_TRUE(submenu->IsShowing());
+  ASSERT_EQ(3, submenu->GetMenuItemCount());
+  views::View* first_view = submenu->GetMenuItemAt(0);
+  ASSERT_EQ(1, first_view->child_count());
+  views::View* child_view = first_view->child_at(0);
+  EXPECT_EQ(child_view, target_view_);
+
+  // We do this here (instead of in BuildMenu()) so that the menu is already
+  // built and the bounds are correct.
+  target_view_->Init();
+}
+
 bool MenuViewDragAndDropTest::GetDropFormats(
     views::MenuItemView* menu,
     int* formats,
@@ -248,63 +267,27 @@
   MenuViewDragAndDropTestTestInMenuDrag() = default;
   ~MenuViewDragAndDropTestTestInMenuDrag() override = default;
 
+  void OnWidgetDragWillStart();
+  void OnWidgetDragComplete();
+
  protected:
   // MenuViewDragAndDropTest:
   void DoTestWithMenuOpen() override;
 
  private:
-  void Step2();
-  void Step3();
-  void Step4();
+  void StartDrag();
+  void OnDragEntered();
 };
 
-void MenuViewDragAndDropTestTestInMenuDrag::DoTestWithMenuOpen() {
-  // A few sanity checks to make sure the menu built correctly.
-  views::SubmenuView* submenu = menu()->GetSubmenu();
-  ASSERT_TRUE(submenu);
-  ASSERT_TRUE(submenu->IsShowing());
-  ASSERT_EQ(3, submenu->GetMenuItemCount());
-
-  // We do this here (instead of in BuildMenu()) so that the menu is already
-  // built and the bounds are correct.
-  target_view()->Init();
-
-  // We're going to drag the second menu element.
-  views::MenuItemView* drag_view = submenu->GetMenuItemAt(1);
-  ASSERT_NE(nullptr, drag_view);
-
-  // Move mouse to center of menu and press button.
-  ui_test_utils::MoveMouseToCenterAndPress(
-      drag_view, ui_controls::LEFT, ui_controls::DOWN,
-      CreateEventTask(this, &MenuViewDragAndDropTestTestInMenuDrag::Step2));
-}
-
-void MenuViewDragAndDropTestTestInMenuDrag::Step2() {
-  views::MenuItemView* drop_target = menu()->GetSubmenu()->GetMenuItemAt(2);
-  gfx::Point loc(1, drop_target->height() - 1);
-  views::View::ConvertPointToScreen(drop_target, &loc);
-
-  // Start a drag.
-  ui_controls::SendMouseMoveNotifyWhenDone(
-      loc.x() + 10, loc.y(),
-      CreateEventTask(this, &MenuViewDragAndDropTestTestInMenuDrag::Step3));
-
+void MenuViewDragAndDropTestTestInMenuDrag::OnWidgetDragWillStart() {
+  // Enqueue an event to drag the second menu element to the third element.
+  views::View* drag_view = menu()->GetSubmenu()->GetMenuItemAt(2);
+  gfx::Point loc(1, drag_view->height() - 1);
+  views::View::ConvertPointToScreen(drag_view, &loc);
   ScheduleMouseMoveInBackground(loc.x(), loc.y());
 }
 
-void MenuViewDragAndDropTestTestInMenuDrag::Step3() {
-  // Drop the item on the target.
-  views::MenuItemView* drop_target = menu()->GetSubmenu()->GetMenuItemAt(2);
-  gfx::Point loc(1, drop_target->height() - 2);
-  views::View::ConvertPointToScreen(drop_target, &loc);
-  ui_controls::SendMouseMove(loc.x(), loc.y());
-
-  ui_controls::SendMouseEventsNotifyWhenDone(
-      ui_controls::LEFT, ui_controls::UP,
-      CreateEventTask(this, &MenuViewDragAndDropTestTestInMenuDrag::Step4));
-}
-
-void MenuViewDragAndDropTestTestInMenuDrag::Step4() {
+void MenuViewDragAndDropTestTestInMenuDrag::OnWidgetDragComplete() {
   // We should have performed an in-menu drop, and the nested view should not
   // have had a drag and drop. Since the drag happened in menu code, the
   // delegate should not have been asked whether or not to close, and the menu
@@ -317,6 +300,43 @@
   Done();
 }
 
+void MenuViewDragAndDropTestTestInMenuDrag::DoTestWithMenuOpen() {
+  MenuViewDragAndDropTest::DoTestWithMenuOpen();
+
+  // We're going to drag the second menu element.
+  views::MenuItemView* drag_view = menu()->GetSubmenu()->GetMenuItemAt(1);
+  ASSERT_NE(nullptr, drag_view);
+  ui_test_utils::MoveMouseToCenterAndPress(
+      drag_view, ui_controls::LEFT, ui_controls::DOWN,
+      CreateEventTask(this, &MenuViewDragAndDropTestTestInMenuDrag::StartDrag));
+}
+
+void MenuViewDragAndDropTestTestInMenuDrag::StartDrag() {
+  // Begin dragging the second menu element.
+  views::View* drag_view = menu()->GetSubmenu()->GetMenuItemAt(2);
+  gfx::Point loc(1, drag_view->height() - 1);
+  views::View::ConvertPointToScreen(drag_view, &loc);
+  EXPECT_TRUE(ui_controls::SendMouseMoveNotifyWhenDone(
+      loc.x() + 10, loc.y(),
+      CreateEventTask(this,
+                      &MenuViewDragAndDropTestTestInMenuDrag::OnDragEntered)));
+
+  OnWidgetDragWillStart();
+}
+
+void MenuViewDragAndDropTestTestInMenuDrag::OnDragEntered() {
+  // Drop the item on the target.
+  views::MenuItemView* drop_target = menu()->GetSubmenu()->GetMenuItemAt(2);
+  gfx::Point loc(1, drop_target->height() - 2);
+  views::View::ConvertPointToScreen(drop_target, &loc);
+  ui_controls::SendMouseMove(loc.x(), loc.y());
+
+  ui_controls::SendMouseEventsNotifyWhenDone(
+      ui_controls::LEFT, ui_controls::UP,
+      CreateEventTask(
+          this, &MenuViewDragAndDropTestTestInMenuDrag::OnWidgetDragComplete));
+}
+
 // Test that an in-menu (i.e., entirely implemented in the menu code) closes the
 // menu automatically once the drag is complete, and does not ask the delegate
 // to stay open.
@@ -330,74 +350,27 @@
   MenuViewDragAndDropTestNestedDrag() = default;
   ~MenuViewDragAndDropTestNestedDrag() override = default;
 
+  void OnWidgetDragWillStart();
+  void OnWidgetDragComplete();
+
  protected:
   // MenuViewDragAndDropTest:
   void DoTestWithMenuOpen() override;
 
  private:
-  void Step2();
-  void Step3();
-  void Step4();
+  void StartDrag();
+  void OnDragEntered();
 };
 
-void MenuViewDragAndDropTestNestedDrag::DoTestWithMenuOpen() {
-  // Sanity checks: We should be showing the menu, it should have three
-  // children, and the first of those children should have a nested view of the
-  // TestTargetView.
-  views::SubmenuView* submenu = menu()->GetSubmenu();
-  ASSERT_TRUE(submenu);
-  ASSERT_TRUE(submenu->IsShowing());
-  ASSERT_EQ(3, submenu->GetMenuItemCount());
-  views::View* first_view = submenu->GetMenuItemAt(0);
-  ASSERT_EQ(1, first_view->child_count());
-  views::View* child_view = first_view->child_at(0);
-  EXPECT_EQ(child_view, target_view());
-
-  // We do this here (instead of in BuildMenu()) so that the menu is already
-  // built and the bounds are correct.
-  target_view()->Init();
-
-  // The target view should now have two children.
-  ASSERT_EQ(2, target_view()->child_count());
-
-  views::View* drag_view = target_view()->child_at(0);
-  ASSERT_NE(nullptr, drag_view);
-
-  // Move mouse to center of menu and press button.
-  ui_test_utils::MoveMouseToCenterAndPress(
-      drag_view, ui_controls::LEFT, ui_controls::DOWN,
-      CreateEventTask(this, &MenuViewDragAndDropTestNestedDrag::Step2));
-}
-
-void MenuViewDragAndDropTestNestedDrag::Step2() {
+void MenuViewDragAndDropTestNestedDrag::OnWidgetDragWillStart() {
+  // Enqueue an event to drag the target's first child to its second.
   views::View* drop_target = target_view()->child_at(1);
   gfx::Point loc(2, 0);
   views::View::ConvertPointToScreen(drop_target, &loc);
-
-  // Start a drag.
-  ui_controls::SendMouseMoveNotifyWhenDone(
-      loc.x() + 3, loc.y(),
-      CreateEventTask(this, &MenuViewDragAndDropTestNestedDrag::Step3));
-
   ScheduleMouseMoveInBackground(loc.x(), loc.y());
 }
 
-void MenuViewDragAndDropTestNestedDrag::Step3() {
-  // The view should be dragging now.
-  EXPECT_TRUE(target_view()->dragging());
-
-  // Drop the item so that it's now the second item.
-  views::View* drop_target = target_view()->child_at(1);
-  gfx::Point loc(5, 0);
-  views::View::ConvertPointToScreen(drop_target, &loc);
-  ui_controls::SendMouseMove(loc.x(), loc.y());
-
-  ui_controls::SendMouseEventsNotifyWhenDone(
-      ui_controls::LEFT, ui_controls::UP,
-      CreateEventTask(this, &MenuViewDragAndDropTestNestedDrag::Step4));
-}
-
-void MenuViewDragAndDropTestNestedDrag::Step4() {
+void MenuViewDragAndDropTestNestedDrag::OnWidgetDragComplete() {
   // The target view should have finished its drag, and should have dropped the
   // view. The main menu should not have done any drag, and the delegate should
   // have been asked if it wanted to close. Since the delegate did not want to
@@ -415,6 +388,49 @@
   Done();
 }
 
+void MenuViewDragAndDropTestNestedDrag::DoTestWithMenuOpen() {
+  MenuViewDragAndDropTest::DoTestWithMenuOpen();
+
+  // The target view should now have two children.
+  ASSERT_EQ(2, target_view()->child_count());
+
+  // We're going to drag the target's first child.
+  views::View* drag_view = target_view()->child_at(0);
+  ASSERT_NE(nullptr, drag_view);
+  ui_test_utils::MoveMouseToCenterAndPress(
+      drag_view, ui_controls::LEFT, ui_controls::DOWN,
+      CreateEventTask(this, &MenuViewDragAndDropTestNestedDrag::StartDrag));
+}
+
+void MenuViewDragAndDropTestNestedDrag::StartDrag() {
+  // Begin dragging the target's first child.
+  views::View* drop_target = target_view()->child_at(1);
+  gfx::Point loc(2, 0);
+  views::View::ConvertPointToScreen(drop_target, &loc);
+  EXPECT_TRUE(ui_controls::SendMouseMoveNotifyWhenDone(
+      loc.x() + 10, loc.y(),
+      CreateEventTask(this,
+                      &MenuViewDragAndDropTestNestedDrag::OnDragEntered)));
+
+  OnWidgetDragWillStart();
+}
+
+void MenuViewDragAndDropTestNestedDrag::OnDragEntered() {
+  // The view should be dragging now.
+  EXPECT_TRUE(target_view()->dragging());
+
+  // Drop the item so that it's now the second item.
+  views::View* drop_target = target_view()->child_at(1);
+  gfx::Point loc(5, 0);
+  views::View::ConvertPointToScreen(drop_target, &loc);
+  ui_controls::SendMouseMove(loc.x(), loc.y());
+
+  ui_controls::SendMouseEventsNotifyWhenDone(
+      ui_controls::LEFT, ui_controls::UP,
+      CreateEventTask(
+          this, &MenuViewDragAndDropTestNestedDrag::OnWidgetDragComplete));
+}
+
 // Test that a nested drag (i.e. one via a child view, and not entirely
 // implemented in menu code) will consult the delegate before closing the view
 // after the drag.
@@ -442,9 +458,7 @@
 }
 
 void MenuViewDragAndDropForDropStayOpen::DoTestWithMenuOpen() {
-  views::SubmenuView* submenu = menu()->GetSubmenu();
-  ASSERT_TRUE(submenu);
-  ASSERT_TRUE(submenu->IsShowing());
+  MenuViewDragAndDropTest::DoTestWithMenuOpen();
 
   views::MenuController* controller = menu()->GetMenuController();
   ASSERT_TRUE(controller);
@@ -474,9 +488,7 @@
 }
 
 void MenuViewDragAndDropForDropCancel::DoTestWithMenuOpen() {
-  views::SubmenuView* submenu = menu()->GetSubmenu();
-  ASSERT_TRUE(submenu);
-  ASSERT_TRUE(submenu->IsShowing());
+  MenuViewDragAndDropTest::DoTestWithMenuOpen();
 
   views::MenuController* controller = menu()->GetMenuController();
   ASSERT_TRUE(controller);
diff --git a/chrome/browser/ui/views/page_action/pwa_install_view.cc b/chrome/browser/ui/views/page_action/pwa_install_view.cc
index 278aad7..f2c4e4d 100644
--- a/chrome/browser/ui/views/page_action/pwa_install_view.cc
+++ b/chrome/browser/ui/views/page_action/pwa_install_view.cc
@@ -7,12 +7,16 @@
 #include "chrome/app/chrome_command_ids.h"
 #include "chrome/browser/banners/app_banner_manager.h"
 #include "chrome/browser/web_applications/components/web_app_tab_helper_base.h"
+#include "chrome/grit/generated_resources.h"
 #include "components/omnibox/browser/vector_icons.h"
+#include "ui/base/l10n/l10n_util.h"
 
 PwaInstallView::PwaInstallView(CommandUpdater* command_updater,
                                PageActionIconView::Delegate* delegate)
     : PageActionIconView(command_updater, IDC_INSTALL_PWA, delegate) {
   SetVisible(false);
+  SetLabel(l10n_util::GetStringUTF16(IDS_OMNIBOX_PWA_INSTALL_ICON_LABEL));
+  SetUpForInOutAnimation();
 }
 
 PwaInstallView::~PwaInstallView() {}
@@ -22,8 +26,11 @@
   if (!web_contents)
     return false;
 
-  bool is_installable =
-      banners::AppBannerManager::IsWebContentsInstallable(web_contents);
+  banners::AppBannerManager* manager =
+      banners::AppBannerManager::FromWebContents(web_contents);
+  DCHECK(manager);
+
+  bool is_installable = manager->IsInstallable();
   bool is_installed =
       web_app::WebAppTabHelperBase::FromWebContents(web_contents)
           ->HasAssociatedApp();
@@ -32,6 +39,11 @@
   // the scope of a previously-determined installable site, display it as still
   // being installable.
 
+  if (show_install_button && manager->MaybeConsumeInstallAnimation())
+    AnimateIn(base::nullopt);
+  else
+    ResetSlideAnimation(false);
+
   bool was_visible = visible();
   SetVisible(show_install_button);
   return visible() != was_visible;
@@ -44,11 +56,19 @@
   return nullptr;
 }
 
+bool PwaInstallView::ShouldShowSeparator() const {
+  return false;
+}
+
 const gfx::VectorIcon& PwaInstallView::GetVectorIcon() const {
   return omnibox::kPlusIcon;
 }
 
 base::string16 PwaInstallView::GetTextForTooltipAndAccessibleName() const {
-  // TODO(https://907351): Implement.
-  return base::string16();
+  content::WebContents* web_contents = GetWebContents();
+  if (!web_contents)
+    return base::string16();
+  return l10n_util::GetStringFUTF16(
+      IDS_OMNIBOX_PWA_INSTALL_ICON_TOOLTIP,
+      banners::AppBannerManager::GetInstallableAppName(web_contents));
 }
diff --git a/chrome/browser/ui/views/page_action/pwa_install_view.h b/chrome/browser/ui/views/page_action/pwa_install_view.h
index 48272f7..464ef55 100644
--- a/chrome/browser/ui/views/page_action/pwa_install_view.h
+++ b/chrome/browser/ui/views/page_action/pwa_install_view.h
@@ -21,6 +21,7 @@
   bool Update() override;
   void OnExecuting(PageActionIconView::ExecuteSource source) override;
   views::BubbleDialogDelegateView* GetBubble() const override;
+  bool ShouldShowSeparator() const override;
   const gfx::VectorIcon& GetVectorIcon() const override;
   base::string16 GetTextForTooltipAndAccessibleName() const override;
 
diff --git a/chrome/browser/ui/views/page_action/pwa_install_view_browsertest.cc b/chrome/browser/ui/views/page_action/pwa_install_view_browsertest.cc
index bcde5cf..837e33e7 100644
--- a/chrome/browser/ui/views/page_action/pwa_install_view_browsertest.cc
+++ b/chrome/browser/ui/views/page_action/pwa_install_view_browsertest.cc
@@ -134,3 +134,27 @@
   ASSERT_FALSE(app_banner_manager->WaitForInstallableCheck());
   EXPECT_FALSE(pwa_install_view->visible());
 }
+
+// Tests that the plus icon animates its label when the installability check
+// passes but doesn't animate more than once for the same installability check.
+IN_PROC_BROWSER_TEST_F(PwaInstallViewBrowserTest, LabelAnimation) {
+  PageActionIconView* pwa_install_view = GetPwaInstallView();
+  EXPECT_FALSE(pwa_install_view->visible());
+
+  content::WebContents* web_contents = GetCurrentTab();
+  auto* app_banner_manager =
+      banners::TestAppBannerManagerDesktop::CreateForWebContents(web_contents);
+
+  ui_test_utils::NavigateToURL(browser(), GetInstallableAppURL());
+  EXPECT_FALSE(pwa_install_view->visible());
+  ASSERT_TRUE(app_banner_manager->WaitForInstallableCheck());
+  EXPECT_TRUE(pwa_install_view->visible());
+  EXPECT_TRUE(pwa_install_view->is_animating_label());
+
+  chrome::NewTab(browser());
+  EXPECT_FALSE(pwa_install_view->visible());
+
+  chrome::SelectPreviousTab(browser());
+  EXPECT_TRUE(pwa_install_view->visible());
+  EXPECT_FALSE(pwa_install_view->is_animating_label());
+}
diff --git a/chrome/browser/ui/views/plugin_vm/plugin_vm_launcher_view.cc b/chrome/browser/ui/views/plugin_vm/plugin_vm_launcher_view.cc
index fc072af..e37149a 100644
--- a/chrome/browser/ui/views/plugin_vm/plugin_vm_launcher_view.cc
+++ b/chrome/browser/ui/views/plugin_vm/plugin_vm_launcher_view.cc
@@ -14,6 +14,7 @@
 #include "chrome/browser/ui/views/chrome_layout_provider.h"
 #include "chrome/grit/chrome_unscaled_resources.h"
 #include "chrome/grit/generated_resources.h"
+#include "content/public/browser/browser_thread.h"
 #include "ui/base/l10n/l10n_util.h"
 #include "ui/base/resource/resource_bundle.h"
 #include "ui/base/text/bytes_formatting.h"
@@ -158,15 +159,13 @@
                    GetLayoutManager()->GetPreferredHeightForWidth(this, width));
 }
 
-PluginVmLauncherView::~PluginVmLauncherView() {
-  plugin_vm_image_manager_->RemoveObserver();
-  g_plugin_vm_launcher_view = nullptr;
+void PluginVmLauncherView::OnDownloadStarted() {
+  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
 }
 
-void PluginVmLauncherView::OnDownloadStarted() {}
-
 void PluginVmLauncherView::OnDownloadProgressUpdated(uint64_t bytes_downloaded,
                                                      int64_t content_length) {
+  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
   DCHECK_EQ(state_, State::DOWNLOADING);
 
   base::Optional<double> fraction_complete =
@@ -178,6 +177,7 @@
 }
 
 void PluginVmLauncherView::OnDownloadCompleted() {
+  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
   DCHECK_EQ(state_, State::DOWNLOADING);
 
   plugin_vm_image_manager_->StartUnzipping();
@@ -185,9 +185,12 @@
   OnStateUpdated();
 }
 
-void PluginVmLauncherView::OnDownloadCancelled() {}
+void PluginVmLauncherView::OnDownloadCancelled() {
+  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+}
 
 void PluginVmLauncherView::OnDownloadFailed() {
+  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
   state_ = State::ERROR;
   OnStateUpdated();
 }
@@ -195,6 +198,7 @@
 void PluginVmLauncherView::OnUnzippingProgressUpdated(
     int64_t bytes_unzipped,
     int64_t plugin_vm_image_size) {
+  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
   DCHECK_EQ(state_, State::UNZIPPING);
   base::Optional<double> fraction_complete =
       GetFractionComplete(bytes_unzipped, plugin_vm_image_size);
@@ -205,21 +209,18 @@
 }
 
 void PluginVmLauncherView::OnUnzipped() {
+  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
   DCHECK_EQ(state_, State::UNZIPPING);
   state_ = State::FINISHED;
   OnStateUpdated();
 }
 
 void PluginVmLauncherView::OnUnzippingFailed() {
+  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
   state_ = State::ERROR;
   OnStateUpdated();
 }
 
-plugin_vm::PluginVmImageManager*
-PluginVmLauncherView::GetPluginVmImageManagerForTesting() {
-  return plugin_vm_image_manager_;
-}
-
 base::string16 PluginVmLauncherView::GetBigMessage() {
   switch (state_) {
     case State::START_DOWNLOADING:
@@ -234,10 +235,30 @@
   }
 }
 
+PluginVmLauncherView::~PluginVmLauncherView() {
+  plugin_vm_image_manager_->RemoveObserver();
+  g_plugin_vm_launcher_view = nullptr;
+}
+
 void PluginVmLauncherView::AddedToWidget() {
   StartPluginVmImageDownload();
 }
 
+void PluginVmLauncherView::OnStateUpdated() {
+  DialogModelChanged();
+  SetBigMessageLabel();
+  SetMessageLabel();
+  SetBigImage();
+
+  const bool progress_bar_visible =
+      state_ == State::DOWNLOADING || state_ == State::UNZIPPING;
+  progress_bar_->SetVisible(progress_bar_visible);
+  // Values outside the range [0,1] display an infinite loading animation.
+  progress_bar_->SetValue(-1);
+
+  GetWidget()->SetSize(GetWidget()->non_client_view()->GetPreferredSize());
+}
+
 base::string16 PluginVmLauncherView::GetMessage() const {
   switch (state_) {
     case State::START_DOWNLOADING:
@@ -278,21 +299,6 @@
           IDR_PLUGIN_VM_LAUNCHER));
 }
 
-void PluginVmLauncherView::OnStateUpdated() {
-  DialogModelChanged();
-  SetBigMessageLabel();
-  SetMessageLabel();
-  SetBigImage();
-
-  const bool progress_bar_visible =
-      state_ == State::DOWNLOADING || state_ == State::UNZIPPING;
-  progress_bar_->SetVisible(progress_bar_visible);
-  // Values outside the range [0,1] display an infinite loading animation.
-  progress_bar_->SetValue(-1);
-
-  GetWidget()->SetSize(GetWidget()->non_client_view()->GetPreferredSize());
-}
-
 void PluginVmLauncherView::StartPluginVmImageDownload() {
   plugin_vm_image_manager_->SetObserver(this);
   plugin_vm_image_manager_->StartDownload();
diff --git a/chrome/browser/ui/views/plugin_vm/plugin_vm_launcher_view.h b/chrome/browser/ui/views/plugin_vm/plugin_vm_launcher_view.h
index f8b8cc7..bda4520 100644
--- a/chrome/browser/ui/views/plugin_vm/plugin_vm_launcher_view.h
+++ b/chrome/browser/ui/views/plugin_vm/plugin_vm_launcher_view.h
@@ -47,24 +47,9 @@
   void OnUnzippingFailed() override;
 
   // Public for testing purposes.
-  plugin_vm::PluginVmImageManager* GetPluginVmImageManagerForTesting();
   base::string16 GetBigMessage();
 
  protected:
-  // views::BubbleDialogDelegateView implementation.
-  void AddedToWidget() override;
-
- private:
-  ~PluginVmLauncherView() override;
-
-  base::string16 GetMessage() const;
-  void SetBigMessageLabel();
-  void SetMessageLabel();
-  void SetBigImage();
-  void OnStateUpdated();
-
-  void StartPluginVmImageDownload();
-
   enum class State {
     START_DOWNLOADING,  // PluginVm image downloading should be started.
     DOWNLOADING,        // PluginVm image downloading is in progress.
@@ -74,6 +59,20 @@
   };
 
   State state_ = State::START_DOWNLOADING;
+
+  ~PluginVmLauncherView() override;
+  virtual void OnStateUpdated();
+  // views::BubbleDialogDelegateView implementation.
+  void AddedToWidget() override;
+
+ private:
+  base::string16 GetMessage() const;
+  void SetBigMessageLabel();
+  void SetMessageLabel();
+  void SetBigImage();
+
+  void StartPluginVmImageDownload();
+
   plugin_vm::PluginVmImageManager* plugin_vm_image_manager_ = nullptr;
   views::Label* big_message_label_ = nullptr;
   views::Label* message_label_ = nullptr;
diff --git a/chrome/browser/ui/views/plugin_vm/plugin_vm_launcher_view_browsertest.cc b/chrome/browser/ui/views/plugin_vm/plugin_vm_launcher_view_browsertest.cc
index fa6442f..8115233 100644
--- a/chrome/browser/ui/views/plugin_vm/plugin_vm_launcher_view_browsertest.cc
+++ b/chrome/browser/ui/views/plugin_vm/plugin_vm_launcher_view_browsertest.cc
@@ -4,26 +4,103 @@
 
 #include "chrome/browser/ui/views/plugin_vm/plugin_vm_launcher_view.h"
 
+#include "base/bind.h"
+#include "base/files/file_util.h"
+#include "base/threading/thread_restrictions.h"
+#include "chrome/browser/chromeos/plugin_vm/plugin_vm_pref_names.h"
+#include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/test/test_browser_dialog.h"
 #include "chrome/grit/generated_resources.h"
 #include "components/download/public/background_service/download_metadata.h"
+#include "components/prefs/pref_service.h"
+#include "components/prefs/scoped_user_pref_update.h"
+#include "content/public/test/test_utils.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "ui/base/l10n/l10n_util.h"
 #include "ui/views/window/dialog_client_view.h"
 
+namespace {
+
+const char kZipFile[] = "/downloads/a_zip_file.zip";
+const char kZippedFile[] = "a_file.txt";
+const char kZipFileHash[] =
+    "bb077522e6c6fec07cf863ca44d5701935c4bc36ed12ef154f4cc22df70aec18";
+const char kNonMatchingHash[] =
+    "842841a4c75a55ad050d686f4ea5f77e83ae059877fe9b6946aa63d3d057ed32";
+const char kJpgFile[] = "/downloads/image.jpg";
+const char kJpgFileHash[] =
+    "01ba4719c80b6fe911b091a7c05124b64eeece964e09c058ef8f9805daca546b";
+
+}  // namespace
+
+class PluginVmLauncherViewForTesting : public PluginVmLauncherView {
+ public:
+  explicit PluginVmLauncherViewForTesting(Profile* profile)
+      : PluginVmLauncherView(profile) {}
+
+  void AddSetupIsFinishedCallbackForTesting(base::RepeatingClosure callback) {
+    setup_is_finished_callback_for_testing_ = callback;
+  }
+
+ private:
+  base::RepeatingClosure setup_is_finished_callback_for_testing_;
+
+  void OnStateUpdated() override {
+    PluginVmLauncherView::OnStateUpdated();
+
+    if (state_ == State::FINISHED || state_ == State::ERROR) {
+      if (setup_is_finished_callback_for_testing_)
+        setup_is_finished_callback_for_testing_.Run();
+    }
+  }
+};
+
 class PluginVmLauncherViewBrowserTest : public DialogBrowserTest {
  public:
+  class SetupObserver {
+   public:
+    void OnSetupFinished() {
+      if (closure_) {
+        base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE,
+                                                      std::move(closure_));
+      }
+    }
+
+    void WaitForSetupToFinish() {
+      base::RunLoop run_loop;
+      closure_ = run_loop.QuitClosure();
+      run_loop.Run();
+
+      content::RunAllTasksUntilIdle();
+    }
+
+   private:
+    base::OnceClosure closure_;
+  };
+
   PluginVmLauncherViewBrowserTest() {}
 
   void SetUp() override { DialogBrowserTest::SetUp(); }
 
+  void SetUpOnMainThread() override {
+    embedded_test_server()->ServeFilesFromSourceDirectory("chrome/test/data");
+    ASSERT_TRUE(embedded_test_server()->Start());
+  }
+
   // DialogBrowserTest:
   void ShowUi(const std::string& name) override {
-    view_ = new PluginVmLauncherView(browser()->profile());
+    view_ = new PluginVmLauncherViewForTesting(browser()->profile());
+    setup_observer_ = new SetupObserver();
+    view_->AddSetupIsFinishedCallbackForTesting(base::BindRepeating(
+        &SetupObserver::OnSetupFinished, base::Unretained(setup_observer_)));
     views::DialogDelegate::CreateDialogWidget(view_, nullptr, nullptr);
   }
 
+ protected:
+  PluginVmLauncherViewForTesting* view_;
+  SetupObserver* setup_observer_;
+
   bool HasAcceptButton() {
     return view_->GetDialogClientView()->ok_button() != nullptr;
   }
@@ -32,14 +109,6 @@
     return view_->GetDialogClientView()->cancel_button() != nullptr;
   }
 
-  void CheckSetupIsInProgress() {
-    EXPECT_TRUE(HasCancelButton());
-    EXPECT_FALSE(HasAcceptButton());
-    EXPECT_EQ(view_->GetBigMessage(),
-              l10n_util::GetStringUTF16(
-                  IDS_PLUGIN_VM_LAUNCHER_ENVIRONMENT_SETTING_TITLE));
-  }
-
   void CheckSetupFailed() {
     EXPECT_TRUE(HasAcceptButton());
     EXPECT_TRUE(HasCancelButton());
@@ -47,19 +116,45 @@
               l10n_util::GetStringUTF16(IDS_PLUGIN_VM_LAUNCHER_RETRY_BUTTON));
     EXPECT_EQ(view_->GetBigMessage(),
               l10n_util::GetStringUTF16(IDS_PLUGIN_VM_LAUNCHER_ERROR_TITLE));
+
+    base::FilePath plugin_vm_image_dir =
+        browser()
+            ->profile()
+            ->GetPath()
+            .AppendASCII(plugin_vm::kCrosvmDir)
+            .AppendASCII(plugin_vm::kPvmDir)
+            .AppendASCII(plugin_vm::kPluginVmImageDir);
+    base::ScopedAllowBlockingForTesting allow_blocking;
+    EXPECT_FALSE(base::DirectoryExists(plugin_vm_image_dir));
   }
 
-  void CheckSetupIsCompleted() {
+  void CheckSetupIsFinishedSuccessfully() {
     EXPECT_TRUE(HasAcceptButton());
     EXPECT_FALSE(HasCancelButton());
     EXPECT_EQ(view_->GetDialogButtonLabel(ui::DIALOG_BUTTON_OK),
               l10n_util::GetStringUTF16(IDS_PLUGIN_VM_LAUNCHER_LAUNCH_BUTTON));
     EXPECT_EQ(view_->GetBigMessage(),
               l10n_util::GetStringUTF16(IDS_PLUGIN_VM_LAUNCHER_FINISHED_TITLE));
+
+    base::FilePath plugin_vm_image_dir =
+        browser()
+            ->profile()
+            ->GetPath()
+            .AppendASCII(plugin_vm::kCrosvmDir)
+            .AppendASCII(plugin_vm::kPvmDir)
+            .AppendASCII(plugin_vm::kPluginVmImageDir);
+    base::ScopedAllowBlockingForTesting allow_blocking;
+    EXPECT_TRUE(base::DirectoryExists(plugin_vm_image_dir));
+    EXPECT_TRUE(base::PathExists(plugin_vm_image_dir.AppendASCII(kZippedFile)));
   }
 
- protected:
-  PluginVmLauncherView* view_;
+  void SetPluginVmImagePref(std::string url, std::string hash) {
+    DictionaryPrefUpdate update(browser()->profile()->GetPrefs(),
+                                plugin_vm::prefs::kPluginVmImage);
+    base::DictionaryValue* plugin_vm_image = update.Get();
+    plugin_vm_image->SetKey("url", base::Value(url));
+    plugin_vm_image->SetKey("hash", base::Value(hash));
+  }
 
  private:
   DISALLOW_COPY_AND_ASSIGN(PluginVmLauncherViewBrowserTest);
@@ -70,65 +165,64 @@
   ShowAndVerifyUi();
 }
 
-IN_PROC_BROWSER_TEST_F(PluginVmLauncherViewBrowserTest, SetupCompleted) {
-  // TODO(https://crbug.com/904852): Add a proper end-to-end test that
-  // checks that file specified by PluginVmImage user policy is being
-  // downloaded and unzipped to the specified location.
+IN_PROC_BROWSER_TEST_F(PluginVmLauncherViewBrowserTest,
+                       SetupShouldFinishSuccessfully) {
+  SetPluginVmImagePref(embedded_test_server()->GetURL(kZipFile).spec(),
+                       kZipFileHash);
 
   ShowUi("default");
   EXPECT_NE(nullptr, view_);
 
-  CheckSetupIsInProgress();
+  setup_observer_->WaitForSetupToFinish();
 
-  view_->GetPluginVmImageManagerForTesting()->OnDownloadCompleted(
-      download::CompletionInfo());
-
-  CheckSetupIsInProgress();
-
-  view_->GetPluginVmImageManagerForTesting()->OnUnzipped(true /* success */);
-
-  CheckSetupIsCompleted();
-
-  view_->GetDialogClientView()->AcceptWindow();
-
-  EXPECT_TRUE(view_->GetWidget()->IsClosed());
+  CheckSetupIsFinishedSuccessfully();
 }
 
 IN_PROC_BROWSER_TEST_F(PluginVmLauncherViewBrowserTest,
-                       RetryAfterDownloadFailed) {
+                       SetupShouldFailAsHashesDoNotMatch) {
+  SetPluginVmImagePref(embedded_test_server()->GetURL(kZipFile).spec(),
+                       kNonMatchingHash);
+
   ShowUi("default");
   EXPECT_NE(nullptr, view_);
 
-  CheckSetupIsInProgress();
-
-  view_->GetPluginVmImageManagerForTesting()->OnDownloadFailed();
+  setup_observer_->WaitForSetupToFinish();
 
   CheckSetupFailed();
+}
+
+IN_PROC_BROWSER_TEST_F(PluginVmLauncherViewBrowserTest,
+                       SetupShouldFailAsUnzippingFails) {
+  SetPluginVmImagePref(embedded_test_server()->GetURL(kJpgFile).spec(),
+                       kJpgFileHash);
+
+  ShowUi("default");
+  EXPECT_NE(nullptr, view_);
+
+  setup_observer_->WaitForSetupToFinish();
+
+  CheckSetupFailed();
+}
+
+IN_PROC_BROWSER_TEST_F(PluginVmLauncherViewBrowserTest,
+                       CouldRetryAfterFailedSetup) {
+  SetPluginVmImagePref(embedded_test_server()->GetURL(kZipFile).spec(),
+                       kNonMatchingHash);
+
+  ShowUi("default");
+  EXPECT_NE(nullptr, view_);
+
+  setup_observer_->WaitForSetupToFinish();
+
+  CheckSetupFailed();
+
+  SetPluginVmImagePref(embedded_test_server()->GetURL(kZipFile).spec(),
+                       kZipFileHash);
 
   // Retry button clicked to retry the download.
   view_->GetDialogClientView()->AcceptWindow();
 
-  CheckSetupIsInProgress();
-}
+  setup_observer_->WaitForSetupToFinish();
 
-IN_PROC_BROWSER_TEST_F(PluginVmLauncherViewBrowserTest,
-                       RetryAfterUnzippingFailed) {
-  ShowUi("default");
-  EXPECT_NE(nullptr, view_);
-
-  CheckSetupIsInProgress();
-
-  view_->GetPluginVmImageManagerForTesting()->OnDownloadCompleted(
-      download::CompletionInfo());
-
-  CheckSetupIsInProgress();
-
-  view_->GetPluginVmImageManagerForTesting()->OnUnzipped(false /* success */);
-
-  CheckSetupFailed();
-
-  // Retry button clicked to retry the download.
-  view_->GetDialogClientView()->AcceptWindow();
-
-  CheckSetupIsInProgress();
+  CheckSetupIsFinishedSuccessfully();
 }
diff --git a/chrome/browser/ui/views/test/view_event_test_base.cc b/chrome/browser/ui/views/test/view_event_test_base.cc
index ece13f10..24d2d07 100644
--- a/chrome/browser/ui/views/test/view_event_test_base.cc
+++ b/chrome/browser/ui/views/test/view_event_test_base.cc
@@ -170,17 +170,17 @@
 }
 
 void ViewEventTestBase::ScheduleMouseMoveInBackground(int x, int y) {
-  if (!dnd_thread_) {
-    dnd_thread_ = std::make_unique<base::Thread>("mouse-move-thread");
-    dnd_thread_->Start();
+  if (!drag_event_thread_) {
+    drag_event_thread_ = std::make_unique<base::Thread>("drag-event-thread");
+    drag_event_thread_->Start();
   }
-  dnd_thread_->task_runner()->PostTask(
+  drag_event_thread_->task_runner()->PostTask(
       FROM_HERE,
       base::BindOnce(base::IgnoreResult(&ui_controls::SendMouseMove), x, y));
 }
 
 void ViewEventTestBase::StopBackgroundThread() {
-  dnd_thread_.reset();
+  drag_event_thread_.reset();
 }
 
 void ViewEventTestBase::RunTestMethod(base::OnceClosure task) {
diff --git a/chrome/browser/ui/views/test/view_event_test_base.h b/chrome/browser/ui/views/test/view_event_test_base.h
index 04a9564..4831f43 100644
--- a/chrome/browser/ui/views/test/view_event_test_base.h
+++ b/chrome/browser/ui/views/test/view_event_test_base.h
@@ -139,8 +139,8 @@
   // The content of the Window.
   views::View* content_view_;
 
-  // Thread for posting background MouseMoves.
-  std::unique_ptr<base::Thread> dnd_thread_;
+  // Thread for posting background drag events.
+  std::unique_ptr<base::Thread> drag_event_thread_;
 
   content::TestBrowserThreadBundle thread_bundle_;
 
diff --git a/chrome/browser/ui/views/toolbar/browser_app_menu_button.cc b/chrome/browser/ui/views/toolbar/browser_app_menu_button.cc
index 0f15bf93..6225abc1 100644
--- a/chrome/browser/ui/views/toolbar/browser_app_menu_button.cc
+++ b/chrome/browser/ui/views/toolbar/browser_app_menu_button.cc
@@ -148,10 +148,6 @@
 
   md_observer_.Add(ui::MaterialDesignController::GetInstance());
 
-  // Because we're using the internal padding to keep track of the changes we
-  // make to the leading margin to handle Fitts' Law, it's easier to just
-  // allocate the property once and modify the value.
-  SetProperty(views::kInternalPaddingKey, new gfx::Insets());
   UpdateBorder();
 }
 
@@ -321,13 +317,6 @@
     SetBorder(views::CreateEmptyBorder(new_insets));
 }
 
-void BrowserAppMenuButton::OnBoundsChanged(const gfx::Rect& previous_bounds) {
-  // TODO(pbos): Consolidate with ToolbarButton::OnBoundsChanged.
-  SetToolbarButtonHighlightPath(this, *GetProperty(views::kInternalPaddingKey));
-
-  AppMenuButton::OnBoundsChanged(previous_bounds);
-}
-
 gfx::Rect BrowserAppMenuButton::GetAnchorBoundsInScreen() const {
   gfx::Rect bounds = GetBoundsInScreen();
   gfx::Insets insets =
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 6414e67..f6a04e1 100644
--- a/chrome/browser/ui/views/toolbar/browser_app_menu_button.h
+++ b/chrome/browser/ui/views/toolbar/browser_app_menu_button.h
@@ -49,7 +49,6 @@
 #endif
 
   // views::MenuButton:
-  void OnBoundsChanged(const gfx::Rect& previous_bounds) override;
   gfx::Rect GetAnchorBoundsInScreen() const override;
   void OnThemeChanged() override;
 
diff --git a/chrome/browser/ui/views/toolbar/toolbar_view_interactive_uitest.cc b/chrome/browser/ui/views/toolbar/toolbar_view_interactive_uitest.cc
index 53360c73..7f09b01 100644
--- a/chrome/browser/ui/views/toolbar/toolbar_view_interactive_uitest.cc
+++ b/chrome/browser/ui/views/toolbar/toolbar_view_interactive_uitest.cc
@@ -50,6 +50,12 @@
   // AppMenuButtonObserver:
   void AppMenuShown() override;
 
+  void OnWidgetDragWillStart();
+  void OnWidgetDragComplete();
+
+  // Starts a drag to the app menu button.
+  void StartDrag();
+
  protected:
   AppMenuButton* GetAppMenuButton() {
     return BrowserView::GetBrowserViewForBrowser(browser())
@@ -61,67 +67,56 @@
         ->toolbar_button_provider()
         ->GetBrowserActionsContainer();
   }
-
-  // Performs a drag-and-drop operation by moving the mouse to |start|, clicking
-  // the left button, moving the mouse to |end|, and releasing the left button.
-  void DoDragAndDrop(const gfx::Point& start, const gfx::Point& end);
+  void set_task_runner(
+      scoped_refptr<base::SingleThreadTaskRunner> task_runner) {
+    task_runner_ = task_runner;
+  }
+  void set_quit_closure(base::OnceClosure quit_closure) {
+    quit_closure_ = std::move(quit_closure);
+  }
+  bool menu_shown() const { return menu_shown_; }
 
  private:
   // InProcessBrowserTest:
   void SetUpOnMainThread() override;
 
+  scoped_refptr<base::SingleThreadTaskRunner> task_runner_;
   bool menu_shown_ = false;
   base::OnceClosure quit_closure_;
 };
 
 void ToolbarViewInteractiveUITest::AppMenuShown() {
   menu_shown_ = true;
-  ui_controls::SendMouseEventsNotifyWhenDone(ui_controls::LEFT, ui_controls::UP,
-                                             std::move(quit_closure_));
+
+  // Release the mouse button.
+  ui_controls::SendMouseEventsNotifyWhenDone(
+      ui_controls::LEFT, ui_controls::UP,
+      base::BindOnce(&ToolbarViewInteractiveUITest::OnWidgetDragComplete,
+                     base::Unretained(this)));
 }
 
-void ToolbarViewInteractiveUITest::DoDragAndDrop(const gfx::Point& start,
-                                                 const gfx::Point& end) {
-  // Much of this function is modeled after methods in ViewEventTestBase (in
-  // particular, the |dnd_thread|, but it's easier to move that here than try
-  // to make ViewEventTestBase play nice with a BrowserView (for the toolbar).
-  // TODO(devlin): In a perfect world, this would be factored better.
-
-  // Begin listening for the app menu to open.
-  ScopedObserver<AppMenuButton, AppMenuButtonObserver> observer(this);
-  observer.Add(GetAppMenuButton());
-
-  // Send the mouse to |start|, and click.  The event queue must be flushed
-  // after processing the click, or the next mouse move sent may get processed
-  // before the click is fully handled, causing the test to fail.
-  EXPECT_TRUE(ui_controls::SendMouseMove(start.x(), start.y()));
-  EXPECT_TRUE(
-      ui_test_utils::SendMouseEventsSync(ui_controls::LEFT, ui_controls::DOWN));
-
-  // Enqueue an event to move the mouse, which will start a drag.
-  EXPECT_TRUE(ui_controls::SendMouseMove(end.x() + 10, end.y()));
-
-  // Enqueue an event to move the mouse to |end|.  This must be done on a
-  // background thread, since starting a drag triggers a nested message loop
-  // that filters messages other than mouse events, so further tasks on the main
-  // message loop will be blocked.  Because the mouse move above is already
-  // queued, this is guaranteed to queue after that, and end the drag operation
-  // in the right place.
-  base::ScopedAllowBaseSyncPrimitivesForTesting allow_thread_join;
-  base::Thread dnd_thread("mouse_move_thread");
-  dnd_thread.Start();
-  dnd_thread.task_runner()->PostTask(
+void ToolbarViewInteractiveUITest::OnWidgetDragWillStart() {
+  // Enqueue an event to move the mouse to the app menu button, which should
+  // result in calling OnMenuOpened().
+  const gfx::Point target =
+      ui_test_utils::GetCenterInScreenCoordinates(GetAppMenuButton());
+  task_runner_->PostTask(
       FROM_HERE, base::BindOnce(base::IgnoreResult(&ui_controls::SendMouseMove),
-                                end.x(), end.y()));
+                                target.x(), target.y()));
+}
 
-  base::RunLoop run_loop;
-  quit_closure_ = run_loop.QuitWhenIdleClosure();
-  run_loop.Run();
+void ToolbarViewInteractiveUITest::OnWidgetDragComplete() {
+  // Return control to the testcase.
+  std::move(quit_closure_).Run();
+}
 
-  // Verify postconditions.
-  EXPECT_TRUE(menu_shown_);
-  // The app menu should have closed once the drag-and-drop completed.
-  EXPECT_FALSE(GetAppMenuButton()->IsMenuShowing());
+void ToolbarViewInteractiveUITest::StartDrag() {
+  // Move the mouse outside the app button.
+  gfx::Point target =
+      ui_test_utils::GetCenterInScreenCoordinates(GetAppMenuButton());
+  EXPECT_TRUE(ui_controls::SendMouseMove(target.x() + 10, target.y()));
+
+  OnWidgetDragWillStart();
 }
 
 void ToolbarViewInteractiveUITest::SetUpOnMainThread() {
@@ -156,20 +151,41 @@
   // with a clean slate.
   base::RunLoop().RunUntilIdle();
 
+  // Set up observers that will drive the test along.
+  AppMenuButton* const app_menu_button = GetAppMenuButton();
+  EXPECT_FALSE(app_menu_button->IsMenuShowing());
+  ScopedObserver<AppMenuButton, AppMenuButtonObserver> button_observer(this);
+  button_observer.Add(app_menu_button);
+
+  // Set up the task runner to use for posting drag actions.
+  // TODO(devlin): This is basically ViewEventTestBase::GetDragTaskRunner().  In
+  // a perfect world, this would be factored better.
+  // Drag events must be posted from a background thread, since starting a drag
+  // triggers a nested message loop that filters messages other than mouse
+  // events, so further tasks on the main message loop will be blocked.
+  base::ScopedAllowBaseSyncPrimitivesForTesting allow_thread_join;
+  base::Thread drag_event_thread("drag-event-thread");
+  drag_event_thread.Start();
+  set_task_runner(drag_event_thread.task_runner());
+
+  // Click on the toolbar action.
   BrowserActionsContainer* const browser_actions = GetBrowserActions();
   ASSERT_EQ(1u, browser_actions->VisibleBrowserActions());
   ToolbarActionView* toolbar_action =
       browser_actions->GetToolbarActionViewAt(0);
   ASSERT_TRUE(toolbar_action);
+  ui_test_utils::MoveMouseToCenterAndPress(
+      toolbar_action, ui_controls::LEFT, ui_controls::DOWN,
+      base::BindRepeating(&ToolbarViewInteractiveUITest::StartDrag,
+                          base::Unretained(this)));
+  base::RunLoop run_loop;
+  set_quit_closure(run_loop.QuitWhenIdleClosure());
+  run_loop.Run();
 
-  gfx::Point browser_action_view_loc =
-      ui_test_utils::GetCenterInScreenCoordinates(toolbar_action);
-  gfx::Point app_button_loc =
-      ui_test_utils::GetCenterInScreenCoordinates(GetAppMenuButton());
-
-  // Perform a drag and drop from the browser action view to the app button,
-  // which should open the app menu.
-  DoDragAndDrop(browser_action_view_loc, app_button_loc);
+  // Verify postconditions.
+  EXPECT_TRUE(menu_shown());
+  // The app menu should have closed once the drag-and-drop completed.
+  EXPECT_FALSE(app_menu_button->IsMenuShowing());
 }
 
 class ToolbarViewTest : public InProcessBrowserTest {
diff --git a/chrome/browser/ui/webui/app_management/app_management_ui.cc b/chrome/browser/ui/webui/app_management/app_management_ui.cc
index bd4fafb..2c9df00 100644
--- a/chrome/browser/ui/webui/app_management/app_management_ui.cc
+++ b/chrome/browser/ui/webui/app_management/app_management_ui.cc
@@ -59,8 +59,6 @@
                           IDR_APP_MANAGEMENT_MOJO_LITE_JS);
   source->AddResourcePath("types.mojom-lite.js",
                           IDR_APP_MANAGEMENT_TYPES_MOJO_LITE_JS);
-  source->AddResourcePath("big_buffer.mojom-lite.js",
-                          IDR_APP_MANAGEMENT_BIG_BUFFER_MOJO_LITE_JS);
   source->AddResourcePath("bitmap.mojom-lite.js",
                           IDR_APP_MANAGEMENT_BITMAP_MOJO_LITE_JS);
   source->AddResourcePath("image.mojom-lite.js",
diff --git a/chrome/browser/ui/webui/management_ui.cc b/chrome/browser/ui/webui/management_ui.cc
index 768a3be..336a210 100644
--- a/chrome/browser/ui/webui/management_ui.cc
+++ b/chrome/browser/ui/webui/management_ui.cc
@@ -15,6 +15,7 @@
 #include "chrome/common/url_constants.h"
 #include "chrome/grit/browser_resources.h"
 #include "chrome/grit/theme_resources.h"
+#include "components/safe_browsing/common/safebrowsing_constants.h"
 #include "components/strings/grit/components_strings.h"
 #include "content/public/browser/web_ui.h"
 #include "extensions/buildflags/buildflags.h"
@@ -99,8 +100,6 @@
      IDS_MANAGEMENT_EXTENSION_REPORT_VERSION},
     {kManagementExtensionReportExtensionsPlugin,
      IDS_MANAGEMENT_EXTENSION_REPORT_EXTENSIONS_PLUGINS},
-    {kManagementExtensionReportSafeBrowsingWarnings,
-     IDS_MANAGEMENT_EXTENSION_REPORT_SAFE_BROWSING_WARNINGS},
     {kManagementExtensionReportPerfCrash,
      IDS_MANAGEMENT_EXTENSION_REPORT_PERF_CRASH},
     {kManagementExtensionReportUserBrowsingData,
@@ -111,6 +110,11 @@
   AddLocalizedStringsBulk(source, kLocalizedStrings,
                           base::size(kLocalizedStrings));
 
+  source->AddString(kManagementExtensionReportSafeBrowsingWarnings,
+                    l10n_util::GetStringFUTF16(
+                        IDS_MANAGEMENT_EXTENSION_REPORT_SAFE_BROWSING_WARNINGS,
+                        base::UTF8ToUTF16(safe_browsing::kSafeBrowsingUrl)));
+
 #if BUILDFLAG(ENABLE_EXTENSIONS)
 
 #endif  // BUILDFLAG(ENABLE_EXTENSIONS)
diff --git a/chrome/browser/ui/webui/policy_ui.cc b/chrome/browser/ui/webui/policy_ui.cc
index 5f5fa6f3..22d2d4a 100644
--- a/chrome/browser/ui/webui/policy_ui.cc
+++ b/chrome/browser/ui/webui/policy_ui.cc
@@ -52,6 +52,10 @@
   source->AddLocalizedString("labelRefreshInterval",
                              IDS_POLICY_LABEL_REFRESH_INTERVAL);
   source->AddLocalizedString("labelStatus", IDS_POLICY_LABEL_STATUS);
+  source->AddLocalizedString("labelPoliciesPush",
+                             IDS_POLICY_LABEL_PUSH_POLICIES);
+  source->AddLocalizedString("policiesPushOn", IDS_POLICY_PUSH_POLICIES_ON);
+  source->AddLocalizedString("policiesPushOff", IDS_POLICY_PUSH_POLICIES_OFF);
   source->AddLocalizedString("showUnset", IDS_POLICY_SHOW_UNSET);
   source->AddLocalizedString("noPoliciesSet", IDS_POLICY_NO_POLICIES_SET);
   source->AddLocalizedString("showMore", IDS_POLICY_SHOW_MORE);
diff --git a/chrome/browser/ui/webui/policy_ui_handler.cc b/chrome/browser/ui/webui/policy_ui_handler.cc
index 7c943dc..abe7222 100644
--- a/chrome/browser/ui/webui/policy_ui_handler.cc
+++ b/chrome/browser/ui/webui/policy_ui_handler.cc
@@ -150,6 +150,8 @@
   bool no_error = store->status() == policy::CloudPolicyStore::STATUS_OK &&
                   client && client->status() == policy::DM_STATUS_SUCCESS;
   dict->SetBoolean("error", !no_error);
+  dict->SetBoolean("policiesPushAvailable",
+                   refresh_scheduler->invalidations_available());
   dict->SetString("status", status);
   dict->SetString("clientId", client_id);
   dict->SetString("username", username);
diff --git a/chrome/browser/ui/webui/print_preview/print_preview_ui.cc b/chrome/browser/ui/webui/print_preview/print_preview_ui.cc
index 264ccbe..424212a 100644
--- a/chrome/browser/ui/webui/print_preview/print_preview_ui.cc
+++ b/chrome/browser/ui/webui/print_preview/print_preview_ui.cc
@@ -364,6 +364,10 @@
     {"pdf/elements/viewer-pdf-toolbar/viewer-pdf-toolbar.js",
      IDR_PDF_VIEWER_PDF_TOOLBAR_JS},
 #if defined(OS_CHROMEOS)
+    {"pdf/elements/viewer-form-warning/viewer-form-warning.html",
+     IDR_PDF_VIEWER_FORM_WARNING_HTML},
+    {"pdf/elements/viewer-form-warning/viewer-form-warning.js",
+     IDR_PDF_VIEWER_FORM_WARNING_JS},
     {"pdf/elements/viewer-pen-options/viewer-pen-options.html",
      IDR_PDF_VIEWER_PEN_OPTIONS_HTML},
     {"pdf/elements/viewer-pen-options/viewer-pen-options.js",
diff --git a/chrome/browser/ui/webui/settings/md_settings_localized_strings_provider.cc b/chrome/browser/ui/webui/settings/md_settings_localized_strings_provider.cc
index 6bee934..d773e37 100644
--- a/chrome/browser/ui/webui/settings/md_settings_localized_strings_provider.cc
+++ b/chrome/browser/ui/webui/settings/md_settings_localized_strings_provider.cc
@@ -1467,8 +1467,6 @@
       {"autofillPageTitle", IDS_SETTINGS_AUTOFILL},
       {"passwords", IDS_SETTINGS_PASSWORDS},
       {"creditCards", IDS_AUTOFILL_PAYMENT_METHODS},
-      {"localCreditCardsSectionTitle", IDS_AUTOFILL_LOCAL_PAYMENT_METHODS},
-      {"serverCreditCardsSectionTitle", IDS_AUTOFILL_SERVER_PAYMENT_METHODS},
       {"noCreditCardsFound", IDS_SETTINGS_PAYMENT_METHODS_NONE},
       {"googlePayments", IDS_SETTINGS_GOOGLE_PAYMENTS},
       {"googlePaymentsCached", IDS_SETTINGS_GOOGLE_PAYMENTS_CACHED},
@@ -1952,11 +1950,6 @@
   html_source->AddBoolean("isAccountManagerEnabled",
                           chromeos::IsAccountManagerAvailable(profile));
 #endif
-
-  html_source->AddBoolean(
-      "splitCreditCardList",
-      base::FeatureList::IsEnabled(
-          autofill::features::kAutofillSettingsCardTypeSplit));
 }
 
 void AddPrintingStrings(content::WebUIDataSource* html_source) {
diff --git a/chrome/browser/ui/webui/signin/inline_login_handler_impl.cc b/chrome/browser/ui/webui/signin/inline_login_handler_impl.cc
index e47b5ed9..b7f731e 100644
--- a/chrome/browser/ui/webui/signin/inline_login_handler_impl.cc
+++ b/chrome/browser/ui/webui/signin/inline_login_handler_impl.cc
@@ -332,8 +332,8 @@
     Profile::CreateStatus /*status*/) {
   HandlerSigninReason reason = GetHandlerSigninReason(current_url_);
   if (reason == HandlerSigninReason::FETCH_LST_ONLY) {
-// Constants are only available on Windows for the Google Credential Provider
-// for Windows.
+    // Constants are only available on Windows for the Google Credential
+    // Provider for Windows.
 #if defined(OS_WIN)
     std::string json_retval;
     base::Value args(base::Value::Type::DICTIONARY);
diff --git a/chrome/browser/ui/webui/welcome/nux/ntp_background_handler.cc b/chrome/browser/ui/webui/welcome/nux/ntp_background_handler.cc
index dab7ae1..bf02622 100644
--- a/chrome/browser/ui/webui/welcome/nux/ntp_background_handler.cc
+++ b/chrome/browser/ui/webui/welcome/nux/ntp_background_handler.cc
@@ -35,6 +35,11 @@
 
 void NtpBackgroundHandler::RegisterMessages() {
   web_ui()->RegisterMessageCallback(
+      "clearBackground",
+      base::BindRepeating(&NtpBackgroundHandler::HandleClearBackground,
+                          base::Unretained(this)));
+
+  web_ui()->RegisterMessageCallback(
       "getBackgrounds",
       base::BindRepeating(&NtpBackgroundHandler::HandleGetBackgrounds,
                           base::Unretained(this)));
@@ -45,6 +50,12 @@
                           base::Unretained(this)));
 }
 
+void NtpBackgroundHandler::HandleClearBackground(const base::ListValue* args) {
+  InstantService* instant_service =
+      InstantServiceFactory::GetForProfile(Profile::FromWebUI(web_ui()));
+  instant_service->SetCustomBackgroundURL(GURL(""));
+}
+
 void NtpBackgroundHandler::HandleGetBackgrounds(const base::ListValue* args) {
   AllowJavascript();
   CHECK_EQ(1U, args->GetSize());
diff --git a/chrome/browser/ui/webui/welcome/nux/ntp_background_handler.h b/chrome/browser/ui/webui/welcome/nux/ntp_background_handler.h
index 3a1df111..d2c23fa 100644
--- a/chrome/browser/ui/webui/welcome/nux/ntp_background_handler.h
+++ b/chrome/browser/ui/webui/welcome/nux/ntp_background_handler.h
@@ -18,6 +18,7 @@
   void RegisterMessages() override;
 
   // Callbacks for JS APIs.
+  void HandleClearBackground(const base::ListValue* args);
   void HandleGetBackgrounds(const base::ListValue* args);
   void HandleSetBackground(const base::ListValue* args);
 
diff --git a/chrome/browser/vr/ui_host/vr_ui_host_impl.cc b/chrome/browser/vr/ui_host/vr_ui_host_impl.cc
index edb2e2ea..47d8cd2 100644
--- a/chrome/browser/vr/ui_host/vr_ui_host_impl.cc
+++ b/chrome/browser/vr/ui_host/vr_ui_host_impl.cc
@@ -4,6 +4,8 @@
 
 #include "chrome/browser/vr/ui_host/vr_ui_host_impl.h"
 
+#include <memory>
+
 #include "base/task/post_task.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/ssl/security_state_tab_helper.h"
@@ -145,7 +147,8 @@
   DVLOG(1) << __func__;
 
   DCHECK(info_);
-  ui_rendering_thread_ = std::make_unique<VRBrowserRendererThreadWin>();
+  ui_rendering_thread_ =
+      std::make_unique<VRBrowserRendererThreadWin>(compositor_.get());
   ui_rendering_thread_->SetVRDisplayInfo(info_.Clone());
 }
 
@@ -156,22 +159,28 @@
   ui_rendering_thread_ = nullptr;
 }
 
+void VRUiHostImpl::SetLocationInfoOnUi() {
+  GURL gurl;
+  if (web_contents_) {
+    content::NavigationEntry* entry =
+        web_contents_->GetController().GetVisibleEntry();
+    if (entry) {
+      gurl = entry->GetVirtualURL();
+    }
+  }
+  // TODO(https://crbug.com/905375): The below call should eventually be
+  // rewritten to take a LocationBarState and not just GURL. See
+  // VRBrowserRendererThreadWin::StartOverlay() also.
+  ui_rendering_thread_->SetLocationInfo(gurl);
+}
+
 void VRUiHostImpl::OnBubbleAdded() {
   if (!ui_rendering_thread_) {
     DVLOG(1) << __func__ << ": no ui_rendering_thread_";
     return;
   }
 
-  ui_rendering_thread_->StartOverlay(compositor_.get());
-
-  if (web_contents_) {
-    content::NavigationEntry* entry =
-        web_contents_->GetController().GetVisibleEntry();
-    if (entry) {
-      GURL gurl = entry->GetVirtualURL();
-      ui_rendering_thread_->SetLocationInfo(gurl);
-    }
-  }
+  SetLocationInfoOnUi();
 
   ui_rendering_thread_->SetVisibleExternalPromptNotification(
       ExternalPromptNotificationType::kPromptGenericPermission);
@@ -198,6 +207,5 @@
   is_prompt_showing_in_headset_ = false;
   ui_rendering_thread_->SetVisibleExternalPromptNotification(
       ExternalPromptNotificationType::kPromptNone);
-  ui_rendering_thread_->StopOverlay();
 }
 }  // namespace vr
diff --git a/chrome/browser/vr/ui_host/vr_ui_host_impl.h b/chrome/browser/vr/ui_host/vr_ui_host_impl.h
index 06178ce..03f3bd8 100644
--- a/chrome/browser/vr/ui_host/vr_ui_host_impl.h
+++ b/chrome/browser/vr/ui_host/vr_ui_host_impl.h
@@ -47,6 +47,7 @@
   void OnBubbleRemoved() override;
 
   void RemoveHeadsetNotificationPrompt(int prompt_sequence_num);
+  void SetLocationInfoOnUi();
 
   device::mojom::XRCompositorHostPtr compositor_;
   std::unique_ptr<VRBrowserRendererThreadWin> ui_rendering_thread_;
diff --git a/chrome/browser/vr/win/vr_browser_renderer_thread_win.cc b/chrome/browser/vr/win/vr_browser_renderer_thread_win.cc
index ee4de102..f416c2e0 100644
--- a/chrome/browser/vr/win/vr_browser_renderer_thread_win.cc
+++ b/chrome/browser/vr/win/vr_browser_renderer_thread_win.cc
@@ -26,18 +26,20 @@
 VRBrowserRendererThreadWin* VRBrowserRendererThreadWin::instance_for_testing_ =
     nullptr;
 
-VRBrowserRendererThreadWin::VRBrowserRendererThreadWin() {
+VRBrowserRendererThreadWin::VRBrowserRendererThreadWin(
+    device::mojom::XRCompositorHost* compositor)
+    : compositor_(compositor) {
   DCHECK(instance_for_testing_ == nullptr);
   instance_for_testing_ = this;
 }
 
 VRBrowserRendererThreadWin::~VRBrowserRendererThreadWin() {
   // Call Cleanup to ensure correct destruction order of VR-UI classes.
-  CleanUp();
+  StopOverlay();
   instance_for_testing_ = nullptr;
 }
 
-void VRBrowserRendererThreadWin::CleanUp() {
+void VRBrowserRendererThreadWin::StopOverlay() {
   browser_renderer_ = nullptr;
   initializing_graphics_ = nullptr;
   overlay_ = nullptr;
@@ -51,28 +53,23 @@
 }
 
 void VRBrowserRendererThreadWin::SetLocationInfo(GURL gurl) {
-  // TODO(https://crbug.com/905375): Set more of this state.  Only the GURL is
-  // currently used, so its the only thing we are setting correctly.
-  DCHECK(ui_) << "Must be called after StartOverlay";
-  LocationBarState state(gurl, security_state::SecurityLevel::SECURE,
-                         nullptr /* vector icon */, true /* display url */,
-                         false /* offline */);
-
-  ui_->SetLocationBarState(state);
+  gurl_ = gurl;
 }
 
 void VRBrowserRendererThreadWin::SetVisibleExternalPromptNotification(
     ExternalPromptNotificationType prompt) {
-  ui_->SetVisibleExternalPromptNotification(prompt);
-
   if (!draw_state_.SetPrompt(prompt))
     return;
 
+  if (draw_state_.ShouldDrawUI())
+    StartOverlay();
+
+  ui_->SetVisibleExternalPromptNotification(prompt);
+
   overlay_->SetOverlayAndWebXRVisibility(draw_state_.ShouldDrawUI(),
                                          draw_state_.ShouldDrawWebXR());
-  if (draw_state_.ShouldDrawUI())
-    overlay_->RequestNextOverlayPose(base::BindOnce(
-        &VRBrowserRendererThreadWin::OnPose, base::Unretained(this)));
+  if (!draw_state_.ShouldDrawUI())
+    StopOverlay();
 }
 
 VRBrowserRendererThreadWin*
@@ -120,17 +117,14 @@
   void ShowPageInfo() override {}
 };
 
-void VRBrowserRendererThreadWin::StartOverlay(
-    device::mojom::XRCompositorHost* compositor) {
-  device::mojom::ImmersiveOverlayPtrInfo overlay_info;
-  compositor->CreateImmersiveOverlay(mojo::MakeRequest(&overlay_info));
+void VRBrowserRendererThreadWin::StartOverlay() {
+  compositor_->CreateImmersiveOverlay(mojo::MakeRequest(&overlay_));
 
   initializing_graphics_ = std::make_unique<GraphicsDelegateWin>();
   if (!initializing_graphics_->InitializeOnMainThread()) {
     return;
   }
 
-  overlay_.Bind(std::move(overlay_info));
   initializing_graphics_->InitializeOnGLThread();
   initializing_graphics_->BindContext();
 
@@ -157,6 +151,16 @@
   ui_ = static_cast<BrowserUiInterface*>(ui.get());
   ui_->SetWebVrMode(true);
 
+  if (gurl_.is_valid()) {
+    // TODO(https://crbug.com/905375): Set more of this state.  Only the GURL is
+    // currently used, so its the only thing we are setting correctly. See
+    // VRUiHostImpl::SetLocationInfoOnUi also.
+    LocationBarState state(gurl_, security_state::SecurityLevel::SECURE,
+                           nullptr /* vector icon */, true /* display url */,
+                           false /* offline */);
+    ui_->SetLocationBarState(state);
+  }
+
   // Create the delegates, and keep raw pointers to them.  They are owned by
   // browser_renderer_.
   std::unique_ptr<SchedulerDelegateWin> scheduler_delegate =
@@ -183,13 +187,6 @@
   graphics_->ClearContext();
 }
 
-void VRBrowserRendererThreadWin::StopOverlay() {
-  draw_state_.StopOverlay();
-  overlay_->SetOverlayAndWebXRVisibility(draw_state_.ShouldDrawUI(),
-                                         draw_state_.ShouldDrawWebXR());
-  CleanUp();
-}
-
 void VRBrowserRendererThreadWin::OnPose(device::mojom::XRFrameDataPtr data) {
   if (!draw_state_.ShouldDrawUI()) {
     // We shouldn't be showing UI.
@@ -251,10 +248,6 @@
 }
 
 // VRBrowserRendererThreadWin::DrawContentType functions.
-void VRBrowserRendererThreadWin::DrawState::StopOverlay() {
-  prompt_ = ExternalPromptNotificationType::kPromptNone;
-}
-
 bool VRBrowserRendererThreadWin::DrawState::ShouldDrawUI() {
   return prompt_ != ExternalPromptNotificationType::kPromptNone;
 }
diff --git a/chrome/browser/vr/win/vr_browser_renderer_thread_win.h b/chrome/browser/vr/win/vr_browser_renderer_thread_win.h
index 7476326..ad4f52e 100644
--- a/chrome/browser/vr/win/vr_browser_renderer_thread_win.h
+++ b/chrome/browser/vr/win/vr_browser_renderer_thread_win.h
@@ -23,13 +23,14 @@
 
 class VR_EXPORT VRBrowserRendererThreadWin {
  public:
-  VRBrowserRendererThreadWin();
+  explicit VRBrowserRendererThreadWin(
+      device::mojom::XRCompositorHost* compositor);
   ~VRBrowserRendererThreadWin();
 
-  void StartOverlay(device::mojom::XRCompositorHost* host);
-  void StopOverlay();
   void SetVRDisplayInfo(device::mojom::VRDisplayInfoPtr display_info);
   void SetLocationInfo(GURL gurl);
+
+  // The below function(s) affect(s) whether UI is drawn or not.
   void SetVisibleExternalPromptNotification(
       ExternalPromptNotificationType prompt);
 
@@ -45,7 +46,6 @@
       prompt_ = prompt;
       return prompt_ != old;
     }
-    void StopOverlay();
 
     // State querying methods.
     bool ShouldDrawUI();
@@ -56,10 +56,11 @@
         ExternalPromptNotificationType::kPromptNone;
   };
 
-  void CleanUp();
   void OnPose(device::mojom::XRFrameDataPtr data);
   void SubmitResult(bool success);
   void SubmitFrame(device::mojom::XRFrameDataPtr data);
+  void StartOverlay();
+  void StopOverlay();
 
   // We need to do some initialization of GraphicsDelegateWin before
   // browser_renderer_, so we first store it in a unique_ptr, then transition
@@ -74,6 +75,10 @@
   SchedulerDelegateWin* scheduler_ = nullptr;
   BrowserUiInterface* ui_ = nullptr;
 
+  // Owned by vr_ui_host:
+  device::mojom::XRCompositorHost* compositor_;
+
+  GURL gurl_;
   DrawState draw_state_;
 
   device::mojom::ImmersiveOverlayPtr overlay_;
diff --git a/chrome/browser/web_applications/components/web_app_shortcut.cc b/chrome/browser/web_applications/components/web_app_shortcut.cc
index 547a340..095752d 100644
--- a/chrome/browser/web_applications/components/web_app_shortcut.cc
+++ b/chrome/browser/web_applications/components/web_app_shortcut.cc
@@ -4,6 +4,8 @@
 
 #include "chrome/browser/web_applications/components/web_app_shortcut.h"
 
+#include <functional>
+
 #include "base/bind.h"
 #include "base/i18n/file_util_icu.h"
 #include "base/strings/utf_string_conversions.h"
@@ -93,8 +95,7 @@
   // outlive the const reference.
   const web_app::ShortcutInfo& shortcut_info_ref = *shortcut_info;
   GetShortcutIOTaskRunner()->PostTaskAndReply(
-      FROM_HERE,
-      base::BindOnce(std::move(task), base::ConstRef(shortcut_info_ref)),
+      FROM_HERE, base::BindOnce(std::move(task), std::cref(shortcut_info_ref)),
       base::BindOnce(&DeleteShortcutInfoOnUIThread, std::move(shortcut_info),
                      std::move(reply)));
 }
diff --git a/chrome/browser/web_applications/extensions/bookmark_app_install_finalizer.cc b/chrome/browser/web_applications/extensions/bookmark_app_install_finalizer.cc
index ffddb62f..62210fc 100644
--- a/chrome/browser/web_applications/extensions/bookmark_app_install_finalizer.cc
+++ b/chrome/browser/web_applications/extensions/bookmark_app_install_finalizer.cc
@@ -4,9 +4,11 @@
 
 #include "chrome/browser/web_applications/extensions/bookmark_app_install_finalizer.h"
 
+#include <memory>
 #include <utility>
 
 #include "base/bind.h"
+#include "base/optional.h"
 #include "chrome/browser/extensions/bookmark_app_extension_util.h"
 #include "chrome/browser/extensions/crx_installer.h"
 #include "chrome/browser/extensions/launch_util.h"
@@ -17,6 +19,7 @@
 #include "chrome/common/extensions/manifest_handlers/app_launch_info.h"
 #include "chrome/common/web_application_info.h"
 #include "extensions/browser/extension_registry.h"
+#include "extensions/browser/install/crx_install_error.h"
 #include "extensions/common/extension.h"
 #include "extensions/common/extension_id.h"
 #include "extensions/common/extension_set.h"
@@ -38,6 +41,34 @@
   return app;
 }
 
+void OnExtensionInstalled(
+    const GURL& app_url,
+    LaunchType launch_type,
+    web_app::InstallFinalizer::InstallFinalizedCallback callback,
+    scoped_refptr<CrxInstaller> crx_installer,
+    const base::Optional<CrxInstallError>& error) {
+  if (error) {
+    std::move(callback).Run(web_app::AppId(),
+                            web_app::InstallResultCode::kFailedUnknownReason);
+    return;
+  }
+
+  const Extension* extension = crx_installer->extension();
+  DCHECK(extension);
+
+  DCHECK_EQ(AppLaunchInfo::GetLaunchWebURL(extension), app_url);
+
+  // Set the launcher type for the app.
+  SetLaunchType(crx_installer->profile(), extension->id(), launch_type);
+
+  // Set this app to be locally installed, as it was installed from this
+  // machine.
+  SetBookmarkAppIsLocallyInstalled(crx_installer->profile(), extension, true);
+
+  std::move(callback).Run(extension->id(),
+                          web_app::InstallResultCode::kSuccess);
+}
+
 }  // namespace
 
 BookmarkAppInstallFinalizer::BookmarkAppInstallFinalizer(Profile* profile)
@@ -55,45 +86,14 @@
 void BookmarkAppInstallFinalizer::FinalizeInstall(
     const WebApplicationInfo& web_app_info,
     InstallFinalizedCallback callback) {
-  DCHECK(!crx_installer_);
+  scoped_refptr<CrxInstaller> crx_installer =
+      crx_installer_factory_.Run(profile_);
 
-  crx_installer_ = crx_installer_factory_.Run(profile_);
-  DCHECK(crx_installer_);
+  crx_installer->set_installer_callback(base::BindOnce(
+      OnExtensionInstalled, web_app_info.app_url, GetLaunchType(web_app_info),
+      std::move(callback), crx_installer));
 
-  crx_installer_->set_installer_callback(base::BindOnce(
-      &BookmarkAppInstallFinalizer::OnExtensionInstalled,
-      weak_ptr_factory_.GetWeakPtr(),
-      std::make_unique<WebApplicationInfo>(web_app_info), std::move(callback)));
-  crx_installer_->InstallWebApp(web_app_info);
-}
-
-void BookmarkAppInstallFinalizer::OnExtensionInstalled(
-    std::unique_ptr<WebApplicationInfo> web_app_info,
-    InstallFinalizedCallback callback,
-    const base::Optional<CrxInstallError>& error) {
-  const Extension* extension = crx_installer_->extension();
-  crx_installer_.reset();
-
-  if (error) {
-    std::move(callback).Run(web_app::AppId(),
-                            web_app::InstallResultCode::kFailedUnknownReason);
-    return;
-  }
-
-  DCHECK(extension);
-  DCHECK_EQ(AppLaunchInfo::GetLaunchWebURL(extension), web_app_info->app_url);
-
-  const LaunchType launch_type = GetLaunchType(*web_app_info);
-
-  // Set the launcher type for the app.
-  SetLaunchType(profile_, extension->id(), launch_type);
-
-  // Set this app to be locally installed, as it was installed from this
-  // machine.
-  SetBookmarkAppIsLocallyInstalled(profile_, extension, true);
-
-  std::move(callback).Run(extension->id(),
-                          web_app::InstallResultCode::kSuccess);
+  crx_installer->InstallWebApp(web_app_info);
 }
 
 bool BookmarkAppInstallFinalizer::CanCreateOsShortcuts() const {
diff --git a/chrome/browser/web_applications/extensions/bookmark_app_install_finalizer.h b/chrome/browser/web_applications/extensions/bookmark_app_install_finalizer.h
index 2c0b009..9a6ea48 100644
--- a/chrome/browser/web_applications/extensions/bookmark_app_install_finalizer.h
+++ b/chrome/browser/web_applications/extensions/bookmark_app_install_finalizer.h
@@ -8,24 +8,16 @@
 #include "base/callback.h"
 #include "base/macros.h"
 #include "base/memory/ref_counted.h"
-#include "base/memory/weak_ptr.h"
-#include "base/optional.h"
 #include "chrome/browser/web_applications/components/install_finalizer.h"
 
 class Profile;
 
-struct WebApplicationInfo;
-
-namespace content {
-class WebContents;
-}
-
 namespace extensions {
 
 class CrxInstaller;
-class CrxInstallError;
 
 // Class used to actually install the Bookmark App in the system.
+// TODO(loyso): Erase this subclass once crbug.com/877898 fixed.
 class BookmarkAppInstallFinalizer : public web_app::InstallFinalizer {
  public:
   // Constructs a BookmarkAppInstallFinalizer that will install the Bookmark App
@@ -53,18 +45,9 @@
       CrxInstallerFactory crx_installer_factory);
 
  private:
-  void OnExtensionInstalled(std::unique_ptr<WebApplicationInfo> web_app_info,
-                            InstallFinalizedCallback callback,
-                            const base::Optional<CrxInstallError>& error);
-
-  scoped_refptr<CrxInstaller> crx_installer_;
   CrxInstallerFactory crx_installer_factory_;
   Profile* profile_;
 
-  // We need a WeakPtr because CrxInstaller is refcounted and it can run its
-  // callback after this class has been destroyed.
-  base::WeakPtrFactory<BookmarkAppInstallFinalizer> weak_ptr_factory_{this};
-
   DISALLOW_COPY_AND_ASSIGN(BookmarkAppInstallFinalizer);
 };
 
diff --git a/chrome/browser/web_applications/extensions/bookmark_app_install_finalizer_unittest.cc b/chrome/browser/web_applications/extensions/bookmark_app_install_finalizer_unittest.cc
index 279d663..f30a2776 100644
--- a/chrome/browser/web_applications/extensions/bookmark_app_install_finalizer_unittest.cc
+++ b/chrome/browser/web_applications/extensions/bookmark_app_install_finalizer_unittest.cc
@@ -151,4 +151,55 @@
   EXPECT_TRUE(callback_called);
 }
 
+TEST_F(BookmarkAppInstallFinalizerTest, ConcurrentInstallSucceeds) {
+  BookmarkAppInstallFinalizer finalizer(profile());
+
+  base::RunLoop run_loop;
+
+  const GURL url1("https://foo1.example");
+  const GURL url2("https://foo2.example");
+
+  bool callback1_called = false;
+  bool callback2_called = false;
+
+  // Start install finalization for the 1st app
+  {
+    WebApplicationInfo web_application_info;
+    web_application_info.app_url = url1;
+
+    finalizer.FinalizeInstall(
+        web_application_info,
+        base::BindLambdaForTesting([&](const web_app::AppId& installed_app_id,
+                                       web_app::InstallResultCode code) {
+          EXPECT_EQ(web_app::InstallResultCode::kSuccess, code);
+          EXPECT_EQ(installed_app_id, web_app::GenerateAppIdFromURL(url1));
+          callback1_called = true;
+          if (callback2_called)
+            run_loop.Quit();
+        }));
+  }
+
+  // Start install finalization for the 2nd app
+  {
+    WebApplicationInfo web_application_info;
+    web_application_info.app_url = url2;
+
+    finalizer.FinalizeInstall(
+        web_application_info,
+        base::BindLambdaForTesting([&](const web_app::AppId& installed_app_id,
+                                       web_app::InstallResultCode code) {
+          EXPECT_EQ(web_app::InstallResultCode::kSuccess, code);
+          EXPECT_EQ(installed_app_id, web_app::GenerateAppIdFromURL(url2));
+          callback2_called = true;
+          if (callback1_called)
+            run_loop.Quit();
+        }));
+  }
+
+  run_loop.Run();
+
+  EXPECT_TRUE(callback1_called);
+  EXPECT_TRUE(callback2_called);
+}
+
 }  // namespace extensions
diff --git a/chrome/common/extensions/api/bookmark_manager_private.json b/chrome/common/extensions/api/bookmark_manager_private.json
index c65c190..88f4e61 100644
--- a/chrome/common/extensions/api/bookmark_manager_private.json
+++ b/chrome/common/extensions/api/bookmark_manager_private.json
@@ -119,24 +119,6 @@
         ]
       },
       {
-        "name": "getStrings",
-        "type": "function",
-        "description": "Gets the i18n strings for the bookmark manager.",
-        "parameters": [
-          {
-            "type": "function",
-             "name": "callback",
-             "parameters": [
-              {
-                "name": "result",
-                "type": "object",
-                "additionalProperties": {"type": "string"}
-              }
-            ]
-          }
-        ]
-      },
-      {
         "name": "startDrag",
         "type": "function",
         "description": "Begins dragging a set of bookmarks.",
@@ -287,6 +269,7 @@
           },
           {
             "type": "function",
+            "optional": true,
             "name": "callback",
             "parameters": [
               {
diff --git a/chrome/common/pref_names.cc b/chrome/common/pref_names.cc
index 289c52d..f31b5e1 100644
--- a/chrome/common/pref_names.cc
+++ b/chrome/common/pref_names.cc
@@ -2585,6 +2585,10 @@
 const char kEnterpriseHardwarePlatformAPIEnabled[] =
     "enterprise_hardware_platform_api.enabled";
 
+// Boolean that allows a page to show popups during its unloading.
+// TODO(https://crbug.com/937569): Remove this in Chrome 82.
+const char kAllowPopupsDuringPageUnload[] = "allow_popups_during_page_unload";
+
 #if defined(OS_CHROMEOS)
 // Enum that specifies certificate management permissions for user. It can have
 // one of the following values.
diff --git a/chrome/common/pref_names.h b/chrome/common/pref_names.h
index 718376d0f..80ceecc 100644
--- a/chrome/common/pref_names.h
+++ b/chrome/common/pref_names.h
@@ -914,6 +914,8 @@
 
 extern const char kEnterpriseHardwarePlatformAPIEnabled[];
 
+extern const char kAllowPopupsDuringPageUnload[];
+
 #if defined(OS_ANDROID)
 extern const char kUsageStatsEnabled[];
 #endif
diff --git a/chrome/credential_provider/gaiacp/gaia_credential_base.cc b/chrome/credential_provider/gaiacp/gaia_credential_base.cc
index 3a96da295..abee88b9 100644
--- a/chrome/credential_provider/gaiacp/gaia_credential_base.cc
+++ b/chrome/credential_provider/gaiacp/gaia_credential_base.cc
@@ -207,32 +207,30 @@
   // Check the exit_code to see if any errors were detected by the GLS.
   const base::Value* exit_code_value =
       result->FindKeyOfType(kKeyExitCode, base::Value::Type::INTEGER);
-  if (exit_code_value && exit_code_value->GetInt() != kUiecSuccess) {
-    int exit_code = exit_code_value->GetInt();
-    if (exit_code == kUiecEMailMissmatch) {
-      LOGFN(ERROR) << "Email mismatch";
-      *status_text =
-          CGaiaCredentialBase::AllocErrorString(IDS_EMAIL_MISMATCH_BASE);
-      return E_FAIL;
+  int exit_code = exit_code_value->GetInt();
+  if (exit_code != kUiecSuccess) {
+    switch (exit_code) {
+      case kUiecAbort:
+        // This case represents a user abort and no error message is shown.
+        return E_ABORT;
+      case kUiecTimeout:
+      case kUiecKilled:
+        NOTREACHED() << "Internal codes, not returned by GLS";
+        break;
+      case kUiecEMailMissmatch:
+        *status_text =
+            CGaiaCredentialBase::AllocErrorString(IDS_EMAIL_MISMATCH_BASE);
+        break;
+      case kUiecInvalidEmailDomain:
+        *status_text = CGaiaCredentialBase::AllocErrorString(
+            IDS_INVALID_EMAIL_DOMAIN_BASE);
+        break;
+      case kUiecMissingSigninData:
+        *status_text =
+            CGaiaCredentialBase::AllocErrorString(IDS_INVALID_UI_RESPONSE_BASE);
+        break;
     }
-    if (exit_code == kUiecInvalidEmailDomain) {
-      LOGFN(ERROR) << "Invalid email domain";
-      *status_text =
-          CGaiaCredentialBase::AllocErrorString(IDS_INVALID_EMAIL_DOMAIN_BASE);
-      return E_FAIL;
-    }
-    if (exit_code == kUiecMissingSigninData) {
-      LOGFN(ERROR) << "Missing sign in data";
-      *status_text =
-          CGaiaCredentialBase::AllocErrorString(IDS_INVALID_UI_RESPONSE_BASE);
-      return E_FAIL;
-    }
-    if (exit_code != kUiecSuccess) {
-      LOGFN(ERROR) << "Unspecified failure";
-      *status_text =
-          CGaiaCredentialBase::AllocErrorString(IDS_INVALID_UI_RESPONSE_BASE);
-      return E_FAIL;
-    }
+    return E_FAIL;
   }
 
   // Check that the webui returned all expected values.
@@ -619,15 +617,31 @@
     base::CommandLine* command_line) {
   DCHECK(command_line);
 
-  base::FilePath chrome_path =
+  base::FilePath gls_path =
       chrome_launcher_support::GetChromePathForInstallationLevel(
           chrome_launcher_support::SYSTEM_LEVEL_INSTALLATION, false);
-  if (chrome_path.empty()) {
+
+  constexpr wchar_t kGlsPath[] = L"gls_path";
+
+  wchar_t custom_gls_path_value[MAX_PATH];
+  ULONG path_len = base::size(custom_gls_path_value) * sizeof(wchar_t);
+  HRESULT hr = GetGlobalFlag(kGlsPath, custom_gls_path_value, &path_len);
+  if (SUCCEEDED(hr)) {
+    base::FilePath custom_gls_path(custom_gls_path_value);
+    if (base::PathExists(custom_gls_path)) {
+      gls_path = custom_gls_path;
+    } else {
+      LOGFN(ERROR) << "Specified gls path ('" << custom_gls_path.value()
+                   << "') does not exist, using default gls path.";
+    }
+  }
+
+  if (gls_path.empty()) {
     LOGFN(ERROR) << "No path to chrome.exe could be found.";
     return HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND);
   }
 
-  command_line->SetProgram(chrome_path);
+  command_line->SetProgram(gls_path);
 
   LOGFN(INFO) << "App exe: " << command_line->GetProgram().value();
 
@@ -991,6 +1005,7 @@
 
   *status_text = nullptr;
   *status_icon = CPSI_NONE;
+  memset(cpcs, 0, sizeof(*cpcs));
 
   // This may be a long running function so disable user input while processing.
   if (events_) {
diff --git a/chrome/credential_provider/gaiacp/gaia_credential_base_unittests.cc b/chrome/credential_provider/gaiacp/gaia_credential_base_unittests.cc
index 3309c47..84210d0 100644
--- a/chrome/credential_provider/gaiacp/gaia_credential_base_unittests.cc
+++ b/chrome/credential_provider/gaiacp/gaia_credential_base_unittests.cc
@@ -171,6 +171,35 @@
   EXPECT_EQ(2ul, fake_os_user_manager()->GetUserCount());
 }
 
+TEST_F(GcpGaiaCredentialBaseTest, GetSerialization_Abort) {
+  FakeGaiaCredentialProvider provider;
+
+  // Start logon.
+  CComPtr<IGaiaCredential> gaia_cred;
+  CComPtr<ICredentialProviderCredential> cred;
+  ASSERT_EQ(S_OK, CreateCredentialWithProvider(&provider, &gaia_cred, &cred));
+
+  CComPtr<ITestCredential> test;
+  ASSERT_EQ(S_OK, cred.QueryInterface(&test));
+  ASSERT_EQ(S_OK, test->SetDefaultExitCode(kUiecAbort));
+
+  ASSERT_EQ(S_OK, run_helper()->StartLogonProcessAndWait(cred));
+
+  // Now finish the logon.
+  CREDENTIAL_PROVIDER_GET_SERIALIZATION_RESPONSE cpgsr;
+  CREDENTIAL_PROVIDER_CREDENTIAL_SERIALIZATION cpcs;
+  wchar_t* status_text;
+  CREDENTIAL_PROVIDER_STATUS_ICON status_icon;
+  ASSERT_EQ(S_OK,
+            cred->GetSerialization(&cpgsr, &cpcs, &status_text, &status_icon));
+  EXPECT_EQ(nullptr, status_text);
+  EXPECT_EQ(CPSI_NONE, status_icon);
+  EXPECT_EQ(CPGSR_NO_CREDENTIAL_NOT_FINISHED, cpgsr);
+  EXPECT_EQ(nullptr, test->GetErrorText());
+
+  EXPECT_EQ(S_OK, gaia_cred->Terminate());
+}
+
 TEST_F(GcpGaiaCredentialBaseTest, GetSerialization_MdmEnrollment) {
   // Force a successful MDM enrollment.
   ASSERT_EQ(S_OK, SetGlobalFlagForTesting(kRegMdmUrl, L"https://mdm.com"));
diff --git a/chrome/credential_provider/test/fake_gls_run_helper.cc b/chrome/credential_provider/test/fake_gls_run_helper.cc
index 132d46b..d0b6775 100644
--- a/chrome/credential_provider/test/fake_gls_run_helper.cc
+++ b/chrome/credential_provider/test/fake_gls_run_helper.cc
@@ -9,9 +9,9 @@
 #include "base/base_switches.h"
 #include "base/command_line.h"
 #include "base/json/json_writer.h"
+#include "base/strings/string_number_conversions.h"
 #include "base/strings/utf_string_conversions.h"
 #include "base/test/multiprocess_test.h"
-#include "chrome/credential_provider/common/gcp_strings.h"
 #include "chrome/credential_provider/gaiacp/gaia_credential_provider_i.h"
 #include "chrome/credential_provider/gaiacp/scoped_lsa_policy.h"
 #include "chrome/credential_provider/test/gcp_fakes.h"
@@ -23,6 +23,7 @@
 
 namespace switches {
 
+constexpr char kDefaultExitCode[] = "default-exit-code";
 constexpr char kGlsUserEmail[] = "gls-user-email";
 constexpr char kStartGlsEventName[] = "start-gls-event-name";
 constexpr char kOverrideGaiaId[] = "override-gaia-id";
@@ -59,9 +60,12 @@
     }
   }
 
+  int default_exit_code = kUiecSuccess;
+  EXPECT_TRUE(base::StringToInt(
+      command_line->GetSwitchValueASCII(switches::kDefaultExitCode),
+      &default_exit_code));
   std::string gls_email =
       command_line->GetSwitchValueASCII(switches::kGlsUserEmail);
-
   std::string gaia_id_override =
       command_line->GetSwitchValueASCII(switches::kOverrideGaiaId);
   std::string expected_gaia_id =
@@ -79,6 +83,7 @@
   if (!gaia_id_override.empty() && gaia_id_override != expected_gaia_id) {
     dict.SetInteger(kKeyExitCode, kUiecEMailMissmatch);
   } else {
+    dict.SetInteger(kKeyExitCode, static_cast<UiExitCodes>(default_exit_code));
     dict.SetString(kKeyEmail, expected_email);
     dict.SetString(kKeyFullname, "Full Name");
     dict.SetString(kKeyId, expected_gaia_id);
@@ -163,6 +168,7 @@
 
 // static
 HRESULT FakeGlsRunHelper::GetFakeGlsCommandline(
+    UiExitCodes default_exit_code,
     const std::string& gls_email,
     const std::string& gaia_id_override,
     const base::string16& start_gls_event_name,
@@ -170,6 +176,8 @@
   *command_line = base::GetMultiProcessTestChildBaseCommandLine();
   command_line->AppendSwitchASCII(::switches::kTestChildProcess, "gls_main");
   command_line->AppendSwitchASCII(switches::kGlsUserEmail, gls_email);
+  command_line->AppendSwitchNative(switches::kDefaultExitCode,
+                                   base::NumberToString16(default_exit_code));
 
   if (!gaia_id_override.empty()) {
     command_line->AppendSwitchASCII(switches::kOverrideGaiaId,
diff --git a/chrome/credential_provider/test/fake_gls_run_helper.h b/chrome/credential_provider/test/fake_gls_run_helper.h
index 97d9e4b..f870adf8 100644
--- a/chrome/credential_provider/test/fake_gls_run_helper.h
+++ b/chrome/credential_provider/test/fake_gls_run_helper.h
@@ -8,6 +8,7 @@
 #include <atlcomcli.h>
 
 #include "base/strings/string16.h"
+#include "chrome/credential_provider/common/gcp_strings.h"
 
 struct ICredentialProviderCredential;
 
@@ -35,7 +36,12 @@
   HRESULT StartLogonProcess(ICredentialProviderCredential* cred, bool succeeds);
   HRESULT WaitForLogonProcess(ICredentialProviderCredential* cred);
   HRESULT StartLogonProcessAndWait(ICredentialProviderCredential* cred);
+
+  // Gets a command line that runs a fake GLS that produces the desired output.
+  // |default_exit_code| is the default value that will be written unless the
+  // other command line arguments require a specific error code to be returned.
   static HRESULT GetFakeGlsCommandline(
+      UiExitCodes default_exit_code,
       const std::string& gls_email,
       const std::string& gaia_id_override,
       const base::string16& start_gls_event_name,
diff --git a/chrome/credential_provider/test/test_credential.h b/chrome/credential_provider/test/test_credential.h
index 7824c0ad..e4d3f53 100644
--- a/chrome/credential_provider/test/test_credential.h
+++ b/chrome/credential_provider/test/test_credential.h
@@ -31,6 +31,8 @@
     : public IUnknown {
  public:
   virtual HRESULT STDMETHODCALLTYPE
+  SetDefaultExitCode(UiExitCodes default_exit_code) = 0;
+  virtual HRESULT STDMETHODCALLTYPE
   SetGlsEmailAddress(const std::string& email) = 0;
   virtual HRESULT STDMETHODCALLTYPE
   SetGaiaIdOverride(const std::string& gaia_id) = 0;
@@ -67,6 +69,7 @@
   ~CTestCredentialBase();
 
   // ITestCredential.
+  IFACEMETHODIMP SetDefaultExitCode(UiExitCodes default_exit_code) override;
   IFACEMETHODIMP SetGlsEmailAddress(const std::string& email) override;
   IFACEMETHODIMP SetGaiaIdOverride(const std::string& gaia_id) override;
   IFACEMETHODIMP WaitForGls() override;
@@ -112,6 +115,7 @@
 
   void ResetInternalState() override;
 
+  UiExitCodes default_exit_code_ = kUiecSuccess;
   std::string gls_email_;
   std::string gaia_id_override_;
   base::WaitableEvent gls_done_;
@@ -131,6 +135,13 @@
 CTestCredentialBase<T>::~CTestCredentialBase() {}
 
 template <class T>
+HRESULT CTestCredentialBase<T>::SetDefaultExitCode(
+    UiExitCodes default_exit_code) {
+  default_exit_code_ = default_exit_code;
+  return S_OK;
+}
+
+template <class T>
 HRESULT CTestCredentialBase<T>::SetGlsEmailAddress(const std::string& email) {
   gls_email_ = email;
   return S_OK;
@@ -221,7 +232,8 @@
 HRESULT CTestCredentialBase<T>::GetBaseGlsCommandline(
     base::CommandLine* command_line) {
   return FakeGlsRunHelper::GetFakeGlsCommandline(
-      gls_email_, gaia_id_override_, start_gls_event_name_, command_line);
+      default_exit_code_, gls_email_, gaia_id_override_, start_gls_event_name_,
+      command_line);
 }
 
 template <class T>
diff --git a/chrome/renderer/BUILD.gn b/chrome/renderer/BUILD.gn
index 8014684..e86e1f0 100644
--- a/chrome/renderer/BUILD.gn
+++ b/chrome/renderer/BUILD.gn
@@ -129,6 +129,7 @@
     "//chrome/common/net",
     "//components/autofill/content/renderer",
     "//components/cdm/renderer",
+    "//components/content_capture/renderer",
     "//components/contextual_search/content:renderer",
     "//components/data_reduction_proxy/content/common",
     "//components/data_reduction_proxy/content/renderer",
diff --git a/chrome/renderer/DEPS b/chrome/renderer/DEPS
index f565d31..5c14740 100644
--- a/chrome/renderer/DEPS
+++ b/chrome/renderer/DEPS
@@ -4,6 +4,8 @@
   "+components/autofill/content/renderer",
   "+components/autofill/core/common",
   "+components/cdm/renderer",
+  "+components/content_capture/common",
+  "+components/content_capture/renderer",
   "+components/content_settings/core/common",
   "+components/contextual_search/content/renderer",
   "+components/crash/core/common/crash_key.h",
diff --git a/chrome/renderer/chrome_content_renderer_client.cc b/chrome/renderer/chrome_content_renderer_client.cc
index c723285e..d35c15a 100644
--- a/chrome/renderer/chrome_content_renderer_client.cc
+++ b/chrome/renderer/chrome_content_renderer_client.cc
@@ -4,6 +4,7 @@
 
 #include "chrome/renderer/chrome_content_renderer_client.h"
 
+#include <functional>
 #include <memory>
 #include <utility>
 
@@ -69,6 +70,8 @@
 #include "components/autofill/content/renderer/autofill_agent.h"
 #include "components/autofill/content/renderer/password_autofill_agent.h"
 #include "components/autofill/content/renderer/password_generation_agent.h"
+#include "components/content_capture/common/content_capture_features.h"
+#include "components/content_capture/renderer/content_capture_sender.h"
 #include "components/content_settings/core/common/content_settings_pattern.h"
 #include "components/contextual_search/content/renderer/overlay_js_render_frame_observer.h"
 #include "components/data_reduction_proxy/content/renderer/content_previews_render_frame_observer.h"
@@ -326,9 +329,9 @@
   // over to it. It is safe to pass an unretained pointer to
   // |module_event_sink|: it is owned by a ChromeContentRendererClient, which is
   // a leaked singleton in the process.
-  io_task_runner->PostTask(
-      FROM_HERE, base::BindOnce(&HandleModuleEventOnIOThread,
-                                base::ConstRef(module_event_sink), event));
+  io_task_runner->PostTask(FROM_HERE,
+                           base::BindOnce(&HandleModuleEventOnIOThread,
+                                          std::cref(module_event_sink), event));
 }
 #endif
 
@@ -386,7 +389,7 @@
   // leaked.
   module_watcher_ = ModuleWatcher::Create(
       base::BindRepeating(&OnModuleEvent, thread->GetIOTaskRunner(),
-                          base::ConstRef(module_event_sink_)));
+                          std::cref(module_event_sink_)));
 #endif
 
   chrome_observer_.reset(new ChromeRenderThreadObserver());
@@ -571,6 +574,9 @@
   new AutofillAgent(render_frame, password_autofill_agent,
                     password_generation_agent, associated_interfaces);
 
+  if (content_capture::features::IsContentCaptureEnabled())
+    new content_capture::ContentCaptureSender(render_frame);
+
   // Owned by |render_frame|.
   page_load_metrics::MetricsRenderFrameObserver* metrics_render_frame_observer =
       new page_load_metrics::MetricsRenderFrameObserver(render_frame);
diff --git a/chrome/service/cloud_print/cloud_print_url_fetcher_unittest.cc b/chrome/service/cloud_print/cloud_print_url_fetcher_unittest.cc
index c368165..c2a1494 100644
--- a/chrome/service/cloud_print/cloud_print_url_fetcher_unittest.cc
+++ b/chrome/service/cloud_print/cloud_print_url_fetcher_unittest.cc
@@ -92,7 +92,10 @@
 class CloudPrintURLFetcherTest : public testing::Test,
                                  public CloudPrintURLFetcher::Delegate {
  public:
-  CloudPrintURLFetcherTest() : max_retries_(0), fetcher_(nullptr) {}
+  CloudPrintURLFetcherTest()
+      : max_retries_(0),
+        fetcher_(nullptr),
+        quit_run_loop_(run_loop_.QuitClosure()) {}
 
   // Creates a URLFetcher, using the program's main thread to do IO.
   virtual void CreateFetcher(const GURL& url, int max_retries);
@@ -141,6 +144,10 @@
   int max_retries_;
   Time start_time_;
   scoped_refptr<TestCloudPrintURLFetcher> fetcher_;
+  base::RunLoop run_loop_;
+  base::OnceClosure quit_run_loop_;
+
+  DISALLOW_COPY_AND_ASSIGN(CloudPrintURLFetcherTest);
 };
 
 class CloudPrintURLFetcherBasicTest : public CloudPrintURLFetcherTest {
@@ -257,8 +264,7 @@
   if (handle_raw_response_) {
     // If the current message loop is not the IO loop, it will be shut down when
     // the main loop returns and this thread subsequently goes out of scope.
-    io_task_runner()->PostTask(
-        FROM_HERE, base::RunLoop::QuitCurrentWhenIdleClosureDeprecated());
+    std::move(quit_run_loop_).Run();
     return CloudPrintURLFetcher::STOP_PROCESSING;
   }
   return CloudPrintURLFetcher::CONTINUE_PROCESSING;
@@ -272,8 +278,7 @@
   // We should never get here if we returned true in HandleRawResponse
   EXPECT_FALSE(handle_raw_response_);
   if (handle_raw_data_) {
-    io_task_runner()->PostTask(
-        FROM_HERE, base::RunLoop::QuitCurrentWhenIdleClosureDeprecated());
+    std::move(quit_run_loop_).Run();
     return CloudPrintURLFetcher::STOP_PROCESSING;
   }
   return CloudPrintURLFetcher::CONTINUE_PROCESSING;
@@ -287,8 +292,7 @@
   // We should never get here if we returned true in one of the above methods.
   EXPECT_FALSE(handle_raw_response_);
   EXPECT_FALSE(handle_raw_data_);
-  io_task_runner()->PostTask(
-      FROM_HERE, base::RunLoop::QuitCurrentWhenIdleClosureDeprecated());
+  std::move(quit_run_loop_).Run();
   return CloudPrintURLFetcher::STOP_PROCESSING;
 }
 
@@ -306,8 +310,7 @@
     // We have already sent 20 requests continuously. And we expect that
     // it takes more than 1 second due to the overload protection settings.
     EXPECT_TRUE(Time::Now() - start_time_ >= one_second);
-    io_task_runner()->PostTask(
-        FROM_HERE, base::RunLoop::QuitCurrentWhenIdleClosureDeprecated());
+    std::move(quit_run_loop_).Run();
   }
   return CloudPrintURLFetcher::STOP_PROCESSING;
 }
@@ -326,8 +329,7 @@
 void CloudPrintURLFetcherRetryBackoffTest::OnRequestGiveUp() {
   // It takes more than 200 ms to finish all 11 requests.
   EXPECT_TRUE(Time::Now() - start_time_ >= TimeDelta::FromMilliseconds(200));
-  io_task_runner()->PostTask(
-      FROM_HERE, base::RunLoop::QuitCurrentWhenIdleClosureDeprecated());
+  std::move(quit_run_loop_).Run();
 }
 
 TEST_F(CloudPrintURLFetcherBasicTest, HandleRawResponse) {
@@ -337,7 +339,7 @@
   SetHandleRawResponse(true);
 
   CreateFetcher(test_server.GetURL("/echo"), 0);
-  base::RunLoop().Run();
+  run_loop_.Run();
 }
 
 TEST_F(CloudPrintURLFetcherBasicTest, HandleRawData) {
@@ -347,7 +349,7 @@
 
   SetHandleRawData(true);
   CreateFetcher(test_server.GetURL("/echo"), 0);
-  base::RunLoop().Run();
+  run_loop_.Run();
 }
 
 TEST_F(CloudPrintURLFetcherOverloadTest, Protect) {
@@ -358,7 +360,7 @@
   GURL url(test_server.GetURL("/defaultresponse"));
   CreateFetcher(url, 11);
 
-  base::RunLoop().Run();
+  run_loop_.Run();
 }
 
 TEST_F(CloudPrintURLFetcherRetryBackoffTest, GiveUp) {
@@ -369,7 +371,7 @@
   GURL url(test_server.GetURL("/defaultresponse"));
   CreateFetcher(url, 11);
 
-  base::RunLoop().Run();
+  run_loop_.Run();
 }
 
 }  // namespace cloud_print
diff --git a/chrome/test/BUILD.gn b/chrome/test/BUILD.gn
index 65ceba34..58be2d3 100644
--- a/chrome/test/BUILD.gn
+++ b/chrome/test/BUILD.gn
@@ -693,6 +693,7 @@
       "../browser/lifetime/browser_shutdown_browsertest.cc",
       "../browser/loader/chrome_resource_dispatcher_host_delegate_browsertest.cc",
       "../browser/loader/cors_origin_access_list_browsertest.cc",
+      "../browser/loader/signed_exchange_policy_browsertest.cc",
       "../browser/loadtimes_extension_bindings_browsertest.cc",
       "../browser/locale_tests_browsertest.cc",
       "../browser/lookalikes/lookalike_url_navigation_throttle_browsertest.cc",
@@ -3077,6 +3078,7 @@
     "//chrome:resources",
     "//chrome:strings",
     "//chrome/browser/media/router:test_support",
+    "//chrome/browser/notifications:unit_tests",
     "//chrome/common:test_support",
     "//chrome/common/media_router:test_support",
     "//components/autofill/content/renderer:test_support",
@@ -5768,7 +5770,11 @@
 
     data = [
       "//chrome/test/data/autofill/captured_sites/",
+      "//chrome/test/data/password/captured_sites/",
+      "//chrome/test/data/web_page_replay_go_helper_scripts/automation_helper.js",
+      "//components/test/data/autofill/web_page_replay_support_files/",
       "//third_party/catapult/telemetry/telemetry/internal/bin/",
+      "//third_party/catapult/web_page_replay_go/deterministic.js",
     ]
 
     if (is_linux || is_win) {
diff --git a/chrome/test/chromedriver/server/http_handler.cc b/chrome/test/chromedriver/server/http_handler.cc
index 55f13da..ac33013 100644
--- a/chrome/test/chromedriver/server/http_handler.cc
+++ b/chrome/test/chromedriver/server/http_handler.cc
@@ -1123,6 +1123,7 @@
       body_params, base::JSONWriter::OPTIONS_OMIT_DOUBLE_TYPE_PRESERVATION,
       &body);
   response->SetBody(body, "application/json; charset=utf-8");
+  response->AddHeader("cache-control", "no-cache");
   return response;
 }
 
diff --git a/chrome/test/chromedriver/test/run_py_tests.py b/chrome/test/chromedriver/test/run_py_tests.py
index 8cc42e6..324e502 100755
--- a/chrome/test/chromedriver/test/run_py_tests.py
+++ b/chrome/test/chromedriver/test/run_py_tests.py
@@ -402,6 +402,22 @@
   def testGetCurrentWindowHandle(self):
     self._driver.GetCurrentWindowHandle()
 
+  def testDragAndDropWithSVGImage(self):
+    self._driver.Load(
+                    self.GetHttpUrlForFile('/chromedriver/drag_and_drop.svg'))
+
+    drag = self._driver.FindElement("id", "GreenRectangle")
+    drop = self._driver.FindElement("id", "FolderRectangle")
+    self._driver.MouseMoveTo(drag)
+    self._driver.MouseButtonDown()
+    self._driver.MouseMoveTo(drop)
+    self._driver.MouseButtonUp()
+    self.assertTrue(self._driver.IsAlertOpen())
+    self.assertEquals('GreenRectangle has been dropped into a folder.',
+                      self._driver.GetAlertMessage())
+    self._driver.HandleAlert(True)
+    self.assertEquals('translate(300,55)', drag.GetAttribute("transform"))
+
   def testCloseWindow(self):
     self._driver.Load(self.GetHttpUrlForFile('/chromedriver/page_test.html'))
     old_handles = self._driver.GetWindowHandles()
diff --git a/chrome/test/data/chromedriver/drag_and_drop.svg b/chrome/test/data/chromedriver/drag_and_drop.svg
new file mode 100644
index 0000000..6cb9b7d5
--- /dev/null
+++ b/chrome/test/data/chromedriver/drag_and_drop.svg
@@ -0,0 +1,84 @@
+<?xml version='1.0' standalone='no'?>
+<!DOCTYPE svg PUBLIC '-//W3C//DTD SVG 20001102//EN'
+    'http://www.w3.org/TR/2000/CR-SVG-20001102/DTD/svg-20001102.dtd'>
+<svg width='100%' height='100%' xmlns='http://www.w3.org/2000/svg'
+    onload='Init(evt)'
+    onmousedown='Grab(evt)'
+    onmousemove='Drag(evt)'
+    onmouseup='Drop(evt)'>
+
+  <title>Drag And Drop</title>
+
+  <script><![CDATA[
+      var SVGDocument = null;
+      var SVGRoot = null;
+
+      var TrueCoords = null;
+      var GrabPoint = null;
+      var DragTarget = null;
+
+      function Init(evt)
+      {
+         SVGDocument = evt.target.ownerDocument;
+         SVGRoot = SVGDocument.documentElement;
+         TrueCoords = SVGRoot.createSVGPoint();
+         GrabPoint = SVGRoot.createSVGPoint();
+      }
+
+      function Grab(evt)
+      {
+         var targetElement = evt.target;
+         DragTarget = targetElement;
+         DragTarget.parentNode.appendChild( DragTarget );
+         DragTarget.setAttributeNS(null, 'pointer-events', 'none');
+         var transMatrix = DragTarget.getCTM();
+         GrabPoint.x = TrueCoords.x - Number(transMatrix.e);
+         GrabPoint.y = TrueCoords.y - Number(transMatrix.f);
+      };
+
+
+      function Drag(evt)
+      {
+         GetTrueCoords(evt);
+         if (DragTarget)
+         {
+            var newX = TrueCoords.x - GrabPoint.x;
+            var newY = TrueCoords.y - GrabPoint.y;
+            DragTarget.setAttributeNS(null, 'transform', 'translate(' + newX + ',' + newY + ')');
+         }
+      };
+
+
+      function Drop(evt)
+      {
+         if ( DragTarget )
+         {
+            var targetElement = evt.target;
+            DragTarget.setAttributeNS(null, 'pointer-events', 'all');
+            if ( 'Folder' == targetElement.parentNode.id )
+            {
+               targetElement.parentNode.appendChild( DragTarget );
+               alert(DragTarget.id + ' has been dropped into a folder.');
+            }
+            DragTarget = null;
+         }
+      };
+
+
+      function GetTrueCoords(evt)
+      {
+         var newScale = SVGRoot.currentScale;
+         var translation = SVGRoot.currentTranslate;
+         TrueCoords.x = (evt.clientX - translation.x)/newScale;
+         TrueCoords.y = (evt.clientY - translation.y)/newScale;
+      };
+
+   ]]></script>
+
+  <rect id='GreenRectangle' x='50' y='70' width='100' height='100' style='fill:green; '/>
+
+  <g id='Folder'>
+    <rect id='FolderRectangle' x='300' y='100' width='200' height='150' style='fill:tan; stroke:brown; stroke-width:3;'/>
+  </g>
+
+</svg>
\ No newline at end of file
diff --git a/chrome/test/data/local_ntp/local_ntp_browsertest.html b/chrome/test/data/local_ntp/local_ntp_browsertest.html
index dd9575e..eb8b6e5c 100644
--- a/chrome/test/data/local_ntp/local_ntp_browsertest.html
+++ b/chrome/test/data/local_ntp/local_ntp_browsertest.html
@@ -6,7 +6,7 @@
 <head>
   <script>window.localNTPUnitTest = true;</script>
   <link rel="stylesheet" href="chrome-search://local-ntp/animations.css"></link>
-  <link rel="stylesheet" href="chrome-search://local-ntp/constants.css"></link>
+  <link rel="stylesheet" href="chrome-search://local-ntp/local-ntp-common.css"></link>
   <link rel="stylesheet" href="chrome-search://local-ntp/custom-backgrounds.css"></link>
   <link rel="stylesheet" href="chrome-search://local-ntp/doodles.css"></link>
   <link rel="stylesheet" href="chrome-search://local-ntp/local-ntp.css"></link>
diff --git a/chrome/test/data/password/captured_sites/cipd.yaml b/chrome/test/data/password/captured_sites/cipd.yaml
new file mode 100644
index 0000000..daaefea9a
--- /dev/null
+++ b/chrome/test/data/password/captured_sites/cipd.yaml
@@ -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.
+
+# To create CIPD package run the following command.
+# cipd create --pkg-def cipd.yaml -tag version:$(cat version.txt)
+package: chromium/chrome/test/data/password/captured_sites
+
+# Web Page Replay (WPR) Go archives and test recipes for Chrome Password Manager
+# captured sites tests.
+# Each WPR go archive can simulate the checkout workflow on a site.
+# Each recipe contains instructions for the test framework to perform a password
+# manager scenario.
+# Captured Sites Test Framework Eng doc:
+# https://docs.google.com/document/d/12ZLoGmBK9kc5C5ComHkJWWJUXOda5K0uFr-hfSt6ZUg
+description: captured sites test archives and recipes.
+install_mode: copy
+data:
+  - file: sign_in/amazon
+  - file: sign_in/amazon.test
+  - file: sign_in_fill/amazon
+  - file: sign_in_fill/amazon.test
+  - file: sign_up/tumblr
+  - file: sign_up/tumblr.test
+  - file: sign_up_fill/amazon
+  - file: sign_up_fill/amazon.test
\ No newline at end of file
diff --git a/chrome/test/data/policy/policy_test_cases.json b/chrome/test/data/policy/policy_test_cases.json
index b748967a..077c5a8 100644
--- a/chrome/test/data/policy/policy_test_cases.json
+++ b/chrome/test/data/policy/policy_test_cases.json
@@ -2638,6 +2638,12 @@
     ]
   },
 
+  "AllowPopupsDuringPageUnload": {
+    "os": ["win", "linux", "mac", "chromeos", "android"],
+    "test_policy": { "AllowPopupsDuringPageUnload": true },
+    "pref_mappings": [{ "pref": "allow_popups_during_page_unload" }]
+  },
+
   "----- Chrome OS policies ------------------------------------------------": {},
 
   "ChromeOsLockOnIdleSuspend": {
diff --git a/chrome/test/data/webui/app_management/main_view_test.js b/chrome/test/data/webui/app_management/main_view_test.js
index ddfde28f..ae553207 100644
--- a/chrome/test/data/webui/app_management/main_view_test.js
+++ b/chrome/test/data/webui/app_management/main_view_test.js
@@ -7,22 +7,25 @@
 suite('<app-management-main-view>', function() {
   let mainView;
   let fakeHandler;
-  let appIdCounter;
+  let store;
 
   /**
    * @param {number} numApps
    */
   async function addApps(numApps) {
     for (let i = 0; i < numApps; i++) {
-      await fakeHandler.addApp((appIdCounter++).toString());
+      await fakeHandler.addApp();
     }
   }
 
-  setup(function() {
-    appIdCounter = 0;
+  function getAppItems() {
+    return mainView.$$('app-management-expandable-app-list')
+        .querySelectorAll('app-management-app-item');
+  }
 
+  setup(function() {
     fakeHandler = setupFakeHandler();
-    replaceStore();
+    store = replaceStore();
 
     mainView = document.createElement('app-management-main-view');
     replaceBody(mainView);
@@ -30,41 +33,31 @@
 
   test('simple app addition', async function() {
     // Ensure there is no apps initially
-    expectEquals(
-        0,
-        mainView.$$('app-management-expandable-app-list')
-            .root.querySelectorAll('app-management-app-item')
-            .length);
+    expectEquals(0, getAppItems().length);
 
-    const appId = '1';
-    await fakeHandler.addApp(appId);
+    const app = await fakeHandler.addApp();
 
-    let appItems = mainView.$$('app-management-expandable-app-list')
-                       .root.querySelectorAll('app-management-app-item');
+    let appItems = getAppItems();
     expectEquals(1, appItems.length);
+    expectEquals(app.id, appItems[0].app.id);
 
-    expectEquals(appId, appItems[0].app.id);
+    store.setReducersEnabled(false);
+    appItems[0].click();
+    const expected = app_management.actions.changePage(PageType.DETAIL, app.id);
+    assertDeepEquals(expected, store.lastAction);
   });
 
   test('more apps bar visibility', async function() {
     // The more apps bar shouldn't appear when there are 4 apps.
-    await addApps(4);
-    expectEquals(
-        4,
-        mainView.$$('app-management-expandable-app-list')
-            .root.querySelectorAll('app-management-app-item')
-            .length);
+    await addApps(NUMBER_OF_APPS_DISPLAYED_DEFAULT);
+    expectEquals(NUMBER_OF_APPS_DISPLAYED_DEFAULT, getAppItems().length);
     expectTrue(mainView.$$('app-management-expandable-app-list')
                    .$['expander-row']
                    .hidden);
 
     // The more apps bar appears when there are 5 apps.
     await addApps(1);
-    expectEquals(
-        5,
-        mainView.$$('app-management-expandable-app-list')
-            .root.querySelectorAll('app-management-app-item')
-            .length);
+    expectEquals(NUMBER_OF_APPS_DISPLAYED_DEFAULT + 1, getAppItems().length);
     expectFalse(mainView.$$('app-management-expandable-app-list')
                     .$['expander-row']
                     .hidden);
diff --git a/chrome/test/data/webui/app_management/pwa_permission_view_test.js b/chrome/test/data/webui/app_management/pwa_permission_view_test.js
index 84694d7..325de47d 100644
--- a/chrome/test/data/webui/app_management/pwa_permission_view_test.js
+++ b/chrome/test/data/webui/app_management/pwa_permission_view_test.js
@@ -8,10 +8,9 @@
   let pwaPermissionView;
   let fakeHandler;
 
-  const TEST_APP_ID = '1';
-
-  function getToggleFromPermissionItem(permissionItemId) {
-    return pwaPermissionView.root.getElementById(permissionItemId)
+  function getPermissionToggleByType(permissionType) {
+    return pwaPermissionView.root
+        .querySelector('[permission-type=' + permissionType + ']')
         .root.querySelector('app-management-permission-toggle')
         .root.querySelector('cr-toggle');
   }
@@ -21,8 +20,8 @@
         pwaPermissionView.app_, permissionType);
   }
 
-  async function clickToggle(permissionItemId) {
-    getToggleFromPermissionItem(permissionItemId).click();
+  async function clickToggle(permissionType) {
+    getPermissionToggleByType(permissionType).click();
     await fakeHandler.$.flushForTesting();
   }
 
@@ -31,9 +30,10 @@
     replaceStore();
 
     // Add an app, and make it the currently selected app.
-    await fakeHandler.addApp(TEST_APP_ID);
+    const app = await fakeHandler.addApp();
     app_management.Store.getInstance().dispatch(
-        app_management.actions.changePage(PageType.DETAIL, TEST_APP_ID));
+        app_management.actions.changePage(PageType.DETAIL, app.id));
+
     pwaPermissionView =
         document.createElement('app-management-pwa-permission-view');
     replaceBody(pwaPermissionView);
@@ -45,22 +45,25 @@
         pwaPermissionView.app_.id);
   });
 
-  test(
-      'Clicking the permission toggle changes the permission of the app',
-      async function() {
-        let checkToggle = async function(permissionType, permissionId) {
-          assertTrue(getPermissionBoolByType(permissionType));
-          assertTrue(getToggleFromPermissionItem(permissionId).checked);
-          await clickToggle(permissionId);
-          assertFalse(getPermissionBoolByType(permissionType));
-          assertFalse(getToggleFromPermissionItem(permissionId).checked);
-        };
+  test('toggle permissions', async function() {
+    let checkToggle = async function(permissionType) {
+      assertTrue(getPermissionBoolByType(permissionType));
+      assertTrue(getPermissionToggleByType(permissionType).checked);
 
-        await checkToggle(
-            'CONTENT_SETTINGS_TYPE_NOTIFICATIONS', 'notifications');
-        await checkToggle('CONTENT_SETTINGS_TYPE_GEOLOCATION', 'location');
-        await checkToggle('CONTENT_SETTINGS_TYPE_MEDIASTREAM_CAMERA', 'camera');
-        await checkToggle(
-            'CONTENT_SETTINGS_TYPE_MEDIASTREAM_MIC', 'microphone');
-      });
+      // Toggle off.
+      await clickToggle(permissionType);
+      assertFalse(getPermissionBoolByType(permissionType));
+      assertFalse(getPermissionToggleByType(permissionType).checked);
+
+      // Toggle on.
+      await clickToggle(permissionType);
+      assertTrue(getPermissionBoolByType(permissionType));
+      assertTrue(getPermissionToggleByType(permissionType).checked);
+    };
+
+    await checkToggle('CONTENT_SETTINGS_TYPE_NOTIFICATIONS');
+    await checkToggle('CONTENT_SETTINGS_TYPE_GEOLOCATION');
+    await checkToggle('CONTENT_SETTINGS_TYPE_MEDIASTREAM_CAMERA');
+    await checkToggle('CONTENT_SETTINGS_TYPE_MEDIASTREAM_MIC');
+  });
 });
diff --git a/chrome/test/data/webui/app_management/router_test.js b/chrome/test/data/webui/app_management/router_test.js
index a03bcc1..9867fcf3 100644
--- a/chrome/test/data/webui/app_management/router_test.js
+++ b/chrome/test/data/webui/app_management/router_test.js
@@ -19,9 +19,8 @@
 
   setup(async function() {
     fakeHandler = setupFakeHandler();
-    store = new app_management.TestStore();
+    store = replaceStore();
     await fakeHandler.addApp('1');
-    store.replaceSingleton();
     router = document.createElement('app-management-router');
     replaceBody(router);
   });
diff --git a/chrome/test/data/webui/app_management/test_util.js b/chrome/test/data/webui/app_management/test_util.js
index 703cb32..87ba4aa 100644
--- a/chrome/test/data/webui/app_management/test_util.js
+++ b/chrome/test/data/webui/app_management/test_util.js
@@ -27,13 +27,14 @@
 }
 
 /**
- * Replace the app management store instance with a new, empty store.
+ * Replace the app management store instance with a new, empty TestStore.
+ * @return {app_management.TestStore}
  */
 function replaceStore() {
-  app_management.Store.instance_ = new app_management.Store();
-
-  app_management.Store.getInstance().init(
-      app_management.util.createEmptyState());
+  let store = new app_management.TestStore();
+  store.setReducersEnabled(true);
+  store.replaceSingleton();
+  return store;
 }
 
 /**
diff --git a/chrome/test/data/webui/print_preview/color_settings_test.js b/chrome/test/data/webui/print_preview/color_settings_test.js
new file mode 100644
index 0000000..c0259f02
--- /dev/null
+++ b/chrome/test/data/webui/print_preview/color_settings_test.js
@@ -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.
+
+cr.define('color_settings_test', function() {
+  suite('ColorSettingsTest', function() {
+    let colorSection = null;
+
+    /** @override */
+    setup(function() {
+      PolymerTest.clearBody();
+      colorSection = document.createElement('print-preview-color-settings');
+      colorSection.settings = {
+        color: {
+          value: true,
+          unavailableValue: false,
+          valid: true,
+          available: true,
+          setByPolicy: false,
+          key: 'isColorEnabled',
+        },
+      };
+      colorSection.disabled = false;
+      document.body.appendChild(colorSection);
+    });
+
+    // Tests that setting the setting updates the UI.
+    test('set setting', async () => {
+      const select = colorSection.$$('select');
+      assertEquals('color', select.value);
+
+      colorSection.set('settings.color.value', false);
+      await test_util.eventToPromise('process-select-change', colorSection);
+      assertEquals('bw', select.value);
+    });
+
+    // Tests that selecting a new option in the dropdown updates the setting.
+    test('select option', async () => {
+      // Verify that the selected option and names are as expected.
+      const select = colorSection.$$('select');
+      assertEquals('color', select.value);
+      assertTrue(colorSection.getSettingValue('color'));
+      assertEquals(2, select.options.length);
+
+      // Verify that selecting an new option in the dropdown sets the setting.
+      await print_preview_test_utils.selectOption(colorSection, 'bw');
+      assertFalse(colorSection.getSettingValue('color'));
+    });
+
+    if (cr.isChromeOS) {
+      // Tests that if the setting is enforced by enterprise policy it is
+      // disabled.
+      test('disabled by policy', function() {
+        // Verify that the selected option and names are as expected.
+        const select = colorSection.$$('select');
+        assertFalse(select.disabled);
+
+        colorSection.set('settings.color.setByPolicy', true);
+        assertTrue(select.disabled);
+      });
+    }
+  });
+});
diff --git a/chrome/test/data/webui/print_preview/layout_settings_test.js b/chrome/test/data/webui/print_preview/layout_settings_test.js
new file mode 100644
index 0000000..90e84df7
--- /dev/null
+++ b/chrome/test/data/webui/print_preview/layout_settings_test.js
@@ -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.
+
+cr.define('layout_settings_test', function() {
+  suite('LayoutSettingsTest', function() {
+    let layoutSection = null;
+
+    /** @override */
+    setup(function() {
+      PolymerTest.clearBody();
+      layoutSection = document.createElement('print-preview-layout-settings');
+      layoutSection.settings = {
+        layout: {
+          value: false,
+          unavailableValue: false,
+          valid: true,
+          available: true,
+          setByPolicy: false,
+          key: 'isLandscapeEnabled',
+        },
+      };
+      layoutSection.disabled = false;
+      document.body.appendChild(layoutSection);
+    });
+
+    // Tests that setting the setting updates the UI.
+    test('set setting', async () => {
+      const select = layoutSection.$$('select');
+      assertEquals('portrait', select.value);
+
+      layoutSection.set('settings.layout.value', true);
+      await test_util.eventToPromise('process-select-change', layoutSection);
+      assertEquals('landscape', select.value);
+    });
+
+    // Tests that selecting a new option in the dropdown updates the setting.
+    test('select option', async () => {
+      // Verify that the selected option and names are as expected.
+      const select = layoutSection.$$('select');
+      assertEquals('portrait', select.value);
+      assertFalse(layoutSection.getSettingValue('layout'));
+      assertEquals(2, select.options.length);
+
+      // Verify that selecting an new option in the dropdown sets the setting.
+      await print_preview_test_utils.selectOption(layoutSection, 'landscape');
+      assertTrue(layoutSection.getSettingValue('layout'));
+    });
+  });
+});
diff --git a/chrome/test/data/webui/print_preview/margins_settings_test.js b/chrome/test/data/webui/print_preview/margins_settings_test.js
new file mode 100644
index 0000000..8276058
--- /dev/null
+++ b/chrome/test/data/webui/print_preview/margins_settings_test.js
@@ -0,0 +1,78 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+cr.define('margins_settings_test', function() {
+  suite('MarginsSettingsTest', function() {
+    let marginsSection = null;
+
+    let marginsTypeEnum = null;
+
+    /** @override */
+    setup(function() {
+      PolymerTest.clearBody();
+      marginsSection = document.createElement('print-preview-margins-settings');
+      document.body.appendChild(marginsSection);
+      Polymer.dom.flush();
+      marginsTypeEnum = print_preview.ticket_items.MarginsTypeValue;
+      marginsSection.settings = {
+        margins: {
+          value: marginsTypeEnum.DEFAULT,
+          unavailableValue: marginsTypeEnum.DEFAULT,
+          valid: true,
+          available: true,
+          setByPolicy: false,
+          key: 'marginsType',
+        },
+        pagesPerSheet: {
+          value: 1,
+          unavailableValue: 1,
+          valid: true,
+          available: true,
+          setByPolicy: false,
+          key: '',
+        },
+      };
+      marginsSection.disabled = false;
+    });
+
+    // Tests that setting the setting updates the UI.
+    test('set setting', async () => {
+      const select = marginsSection.$$('select');
+      assertEquals(marginsTypeEnum.DEFAULT.toString(), select.value);
+
+      marginsSection.set('settings.margins.value', marginsTypeEnum.MINIMUM);
+      await test_util.eventToPromise('process-select-change', marginsSection);
+      assertEquals(marginsTypeEnum.MINIMUM.toString(), select.value);
+    });
+
+    // Tests that selecting a new option in the dropdown updates the setting.
+    test('select option', async () => {
+      // Verify that the selected option and names are as expected.
+      const select = marginsSection.$$('select');
+      assertEquals(marginsTypeEnum.DEFAULT.toString(), select.value);
+      assertEquals(
+          marginsTypeEnum.DEFAULT, marginsSection.getSettingValue('margins'));
+      assertEquals(4, select.options.length);
+
+      // Verify that selecting an new option in the dropdown sets the setting.
+      await print_preview_test_utils.selectOption(
+          marginsSection, marginsTypeEnum.MINIMUM.toString());
+      assertEquals(
+          marginsTypeEnum.MINIMUM, marginsSection.getSettingValue('margins'));
+    });
+
+    // This test verifies that changing pages per sheet to N > 1 disables the
+    // margins dropdown.
+    test('disabled by pages per sheet', function() {
+      const select = marginsSection.$$('select');
+      assertFalse(select.disabled);
+
+      marginsSection.set('settings.pagesPerSheet.value', 2);
+      assertTrue(select.disabled);
+
+      marginsSection.set('settings.pagesPerSheet.value', 1);
+      assertFalse(select.disabled);
+    });
+  });
+});
diff --git a/chrome/test/data/webui/print_preview/model_settings_availability_test.js b/chrome/test/data/webui/print_preview/model_settings_availability_test.js
index 941366b..0dd8f377 100644
--- a/chrome/test/data/webui/print_preview/model_settings_availability_test.js
+++ b/chrome/test/data/webui/print_preview/model_settings_availability_test.js
@@ -173,7 +173,11 @@
       });
 
       // Each of these settings should make the setting available, with the
-      // default value given by expectedValue.
+      // default value given by expectedValue. Note: Normally, changing the
+      // printer capabilities will keep the setting the same when possible
+      // (see ModelTest.ChangeDestination), but in this test, we never set
+      // model.initialized_ to true, to verify that setting the default value
+      // at startup works correctly for different possible printers.
       [{
         colorCap: {
           option: [
diff --git a/chrome/test/data/webui/print_preview/model_settings_policy_test.js b/chrome/test/data/webui/print_preview/model_settings_policy_test.js
new file mode 100644
index 0000000..dd5edc81
--- /dev/null
+++ b/chrome/test/data/webui/print_preview/model_settings_policy_test.js
@@ -0,0 +1,198 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+cr.define('model_settings_policy_test', function() {
+  suite('ModelSettingsPolicyTest', function() {
+    let model = null;
+
+    /** @override */
+    setup(function() {
+      PolymerTest.clearBody();
+      model = document.createElement('print-preview-model');
+      document.body.appendChild(model);
+
+      model.documentSettings = {
+        hasCssMediaStyles: false,
+        hasSelection: false,
+        isModifiable: true,
+        isScalingDisabled: false,
+        fitToPageScaling: 100,
+        pageCount: 3,
+        title: 'title',
+      };
+
+      model.pageSize = new print_preview.Size(612, 792);
+      model.margins = new print_preview.Margins(72, 72, 72, 72);
+
+      // Create a test destination.
+      model.destination = new print_preview.Destination(
+          'FooDevice', print_preview.DestinationType.LOCAL,
+          print_preview.DestinationOrigin.LOCAL, 'FooName',
+          print_preview.DestinationConnectionStatus.ONLINE);
+      model.set(
+          'destination.capabilities',
+          print_preview_test_utils.getCddTemplate(model.destination.id)
+              .capabilities);
+    });
+
+    test('color managed', function() {
+      // Remove color capability.
+      let capabilities =
+          print_preview_test_utils.getCddTemplate(model.destination.id)
+              .capabilities;
+      delete capabilities.printer.color;
+
+      [{
+        // Policy has no effect, setting unavailable
+        colorCap: {option: [{type: 'STANDARD_COLOR', is_default: true}]},
+        colorPolicy: print_preview.ColorMode.COLOR,
+        colorDefault: print_preview.ColorMode.COLOR,
+        expectedValue: true,
+        expectedAvailable: false,
+        expectedManaged: false,
+        expectedEnforced: true,
+      },
+       {
+         // Policy contradicts actual capabilities, setting unavailable.
+         colorCap: {option: [{type: 'STANDARD_COLOR', is_default: true}]},
+         colorPolicy: print_preview.ColorMode.GRAY,
+         colorDefault: print_preview.ColorMode.GRAY,
+         expectedValue: true,
+         expectedAvailable: false,
+         expectedManaged: false,
+         expectedEnforced: true,
+       },
+       {
+         // Policy overrides default.
+         colorCap: {
+           option: [
+             {type: 'STANDARD_MONOCHROME', is_default: true},
+             {type: 'STANDARD_COLOR'}
+           ]
+         },
+         colorPolicy: print_preview.ColorMode.COLOR,
+         // Default mismatches restriction and is ignored.
+         colorDefault: print_preview.ColorMode.GRAY,
+         expectedValue: true,
+         expectedAvailable: true,
+         expectedManaged: true,
+         expectedEnforced: true,
+       },
+       {
+         // Default defined by policy but setting is modifiable.
+         colorCap: {
+           option: [
+             {type: 'STANDARD_MONOCHROME', is_default: true},
+             {type: 'STANDARD_COLOR'}
+           ]
+         },
+         colorDefault: print_preview.ColorMode.COLOR,
+         expectedValue: true,
+         expectedAvailable: true,
+         expectedManaged: false,
+         expectedEnforced: false,
+       }].forEach(subtestParams => {
+        capabilities =
+            print_preview_test_utils.getCddTemplate(model.destination.id)
+                .capabilities;
+        capabilities.printer.color = subtestParams.colorCap;
+        const policies = {
+          allowedColorModes: subtestParams.colorPolicy,
+          defaultColorMode: subtestParams.colorDefault,
+        };
+        // In practice |capabilities| are always set after |policies| and
+        // observers only check for |capabilities|, so the order is important.
+        model.set('destination.policies', policies);
+        model.set('destination.capabilities', capabilities);
+        model.applyDestinationSpecificPolicies();
+        assertEquals(
+            subtestParams.expectedValue, model.getSettingValue('color'));
+        assertEquals(
+            subtestParams.expectedAvailable, model.settings.color.available);
+        assertEquals(subtestParams.expectedManaged, model.controlsManaged);
+        assertEquals(
+            subtestParams.expectedEnforced, model.settings.color.setByPolicy);
+      });
+    });
+
+    test('duplex managed', function() {
+      // Remove duplex capability.
+      let capabilities =
+          print_preview_test_utils.getCddTemplate(model.destination.id)
+              .capabilities;
+      delete capabilities.printer.duplex;
+
+      [{
+        // Policy has no effect.
+        duplexCap: {option: [{type: 'NO_DUPLEX', is_default: true}]},
+        duplexPolicy: print_preview.DuplexModeRestriction.SIMPLEX,
+        duplexDefault: print_preview.DuplexModeRestriction.SIMPLEX,
+        expectedValue: false,
+        expectedAvailable: false,
+        expectedManaged: false,
+        expectedEnforced: true,
+      },
+       {
+         // Policy contradicts actual capabilities and is ignored.
+         duplexCap: {option: [{type: 'NO_DUPLEX', is_default: true}]},
+         duplexPolicy: print_preview.DuplexModeRestriction.DUPLEX,
+         duplexDefault: print_preview.DuplexModeRestriction.LONG_EDGE,
+         expectedValue: false,
+         expectedAvailable: false,
+         expectedManaged: false,
+         expectedEnforced: true,
+       },
+       {
+         // Policy overrides default.
+         duplexCap: {
+           option: [
+             {type: 'NO_DUPLEX', is_default: true}, {type: 'LONG_EDGE'},
+             {type: 'SHORT_EDGE'}
+           ]
+         },
+         duplexPolicy: print_preview.DuplexModeRestriction.DUPLEX,
+         // Default mismatches restriction and is ignored.
+         duplexDefault: print_preview.DuplexModeRestriction.SIMPLEX,
+         expectedValue: true,
+         expectedAvailable: true,
+         expectedManaged: true,
+         expectedEnforced: true,
+       },
+       {
+         // Default defined by policy but setting is modifiable.
+         duplexCap: {
+           option: [
+             {type: 'NO_DUPLEX', is_default: true}, {type: 'LONG_EDGE'},
+             {type: 'SHORT_EDGE'}
+           ]
+         },
+         duplexDefault: print_preview.DuplexModeRestriction.LONG_EDGE,
+         expectedValue: true,
+         expectedAvailable: true,
+         expectedManaged: false,
+         expectedEnforced: false,
+       }].forEach(subtestParams => {
+        capabilities =
+            print_preview_test_utils.getCddTemplate('FooPrinter').capabilities;
+        capabilities.printer.duplex = subtestParams.duplexCap;
+        const policies = {
+          allowedDuplexModes: subtestParams.duplexPolicy,
+          defaultDuplexMode: subtestParams.duplexDefault,
+        };
+        // In practice |capabilities| are always set after |policies| and
+        // observers only check for |capabilities|, so the order is important.
+        model.set('destination.policies', policies);
+        model.set('destination.capabilities', capabilities);
+        model.applyDestinationSpecificPolicies();
+        assertEquals(
+            subtestParams.expectedValue, model.getSettingValue('duplex'));
+        assertEquals(
+            subtestParams.expectedAvailable, model.settings.duplex.available);
+        assertEquals(subtestParams.expectedManaged, model.controlsManaged);
+        assertEquals(
+            subtestParams.expectedEnforced, model.settings.duplex.setByPolicy);
+      });
+    });
+  });
+});
diff --git a/chrome/test/data/webui/print_preview/model_test.js b/chrome/test/data/webui/print_preview/model_test.js
index 9a4b65e0..f9a2091 100644
--- a/chrome/test/data/webui/print_preview/model_test.js
+++ b/chrome/test/data/webui/print_preview/model_test.js
@@ -10,6 +10,7 @@
     GetPrintTicket: 'get print ticket',
     GetCloudPrintTicket: 'get cloud print ticket',
     UpdateRecentDestinations: 'update recent destinations',
+    ChangeDestination: 'change destination'
   };
 
   const suiteName = 'ModelTest';
@@ -33,7 +34,7 @@
         version: 2,
         recentDestinations: [],
         dpi: {},
-        mediaSize: {width_microns: 215900, height_microns: 279400},
+        mediaSize: {},
         marginsType: 0, /* default */
         customScaling: false,
         scaling: '100',
@@ -149,18 +150,18 @@
       assertTrue(model.settings.headerFooter.value);
     });
 
-    function toggleSettings() {
+    /** @param {!print_preview.Destination} testDestination */
+    function toggleSettings(testDestination) {
       // Some non default setting values to change to.
+      // Manually set fit to page to available so it can be toggled.
+      model.settings.fitToPage.available = true;
       const settingsChange = {
         pages: [2],
         copies: '2',
         collate: false,
         layout: true,
         color: false,
-        mediaSize: {
-          width_microns: 240000,
-          height_microns: 200000,
-        },
+        mediaSize: testDestination.capabilities.printer.media_size.option[1],
         margins: print_preview.ticket_items.MarginsTypeValue.CUSTOM,
         customMargins: {
           marginTop: 100,
@@ -172,17 +173,17 @@
           horizontal_dpi: 100,
           vertical_dpi: 100,
         },
-        fitToPage: true,
+        fitToPage: false,
         customScaling: true,
         scaling: '90',
-        duplex: false,
+        duplex: true,
         cssBackground: true,
         selectionOnly: true,
         headerFooter: false,
         rasterize: true,
         vendorItems: {
-          paperType: 1,
           printArea: 6,
+          paperType: 1,
         },
         ranges: [{from: 2, to: 2}],
       };
@@ -203,7 +204,6 @@
         pageCount: 3,
         title: 'title',
       };
-
       model.pageSize = new print_preview.Size(612, 792);
 
       // Update pages accordingly.
@@ -232,16 +232,17 @@
               .capabilities;
 
       initializeModel();
+      model.destination = testDestination;
       const defaultTicket =
           model.createPrintTicket(testDestination, false, false);
       const expectedDefaultTicket = JSON.stringify({
-        mediaSize: {width_microns: 215900, height_microns: 279400},
+        mediaSize: testDestination.capabilities.printer.media_size.option[0],
         pageCount: 3,
         landscape: false,
         color: testDestination.getNativeColorModel(true),
         headerFooterEnabled: false,  // Only used in print preview
         marginsType: print_preview.ticket_items.MarginsTypeValue.DEFAULT,
-        duplex: print_preview_new.DuplexMode.LONG_EDGE,
+        duplex: print_preview_new.DuplexMode.SIMPLEX,
         copies: 1,
         collate: true,
         shouldPrintBackgrounds: false,
@@ -257,9 +258,9 @@
         pagesPerSheet: 1,
         dpiHorizontal: 200,
         dpiVertical: 200,
-        dpiDefault: false,
+        dpiDefault: true,
         deviceName: 'FooDevice',
-        fitToPageEnabled: false,
+        fitToPageEnabled: true,
         pageWidth: 612,
         pageHeight: 792,
         showSystemDialog: false,
@@ -267,16 +268,16 @@
       expectEquals(expectedDefaultTicket, defaultTicket);
 
       // Toggle all the values and create a new print ticket.
-      toggleSettings();
+      toggleSettings(testDestination);
       const newTicket = model.createPrintTicket(testDestination, false, false);
       const expectedNewTicket = JSON.stringify({
-        mediaSize: {width_microns: 240000, height_microns: 200000},
+        mediaSize: testDestination.capabilities.printer.media_size.option[1],
         pageCount: 1,
         landscape: true,
         color: testDestination.getNativeColorModel(false),
         headerFooterEnabled: false,
         marginsType: print_preview.ticket_items.MarginsTypeValue.CUSTOM,
-        duplex: print_preview_new.DuplexMode.SIMPLEX,
+        duplex: print_preview_new.DuplexMode.LONG_EDGE,
         copies: 2,
         collate: false,
         shouldPrintBackgrounds: true,
@@ -294,7 +295,7 @@
         dpiVertical: 100,
         dpiDefault: false,
         deviceName: 'FooDevice',
-        fitToPageEnabled: true,
+        fitToPageEnabled: false,
         pageWidth: 612,
         pageHeight: 792,
         showSystemDialog: false,
@@ -323,6 +324,7 @@
       testDestination.capabilities =
           print_preview_test_utils.getCddTemplateWithAdvancedSettings(2)
               .capabilities;
+      model.destination = testDestination;
 
       const defaultTicket = model.createCloudJobTicket(testDestination);
       const expectedDefaultTicket = JSON.stringify({
@@ -333,7 +335,7 @@
             type: testDestination.getSelectedColorOption(true).type,
           },
           copies: {copies: 1},
-          duplex: {type: 'LONG_EDGE'},
+          duplex: {type: 'NO_DUPLEX'},
           media_size: {
             width_microns: 215900,
             height_microns: 279400,
@@ -344,15 +346,15 @@
             vertical_dpi: 200,
           },
           vendor_ticket_item: [
-            {id: 'paperType', value: 0},
             {id: 'printArea', value: 4},
+            {id: 'paperType', value: 0},
           ],
         },
       });
       expectEquals(expectedDefaultTicket, defaultTicket);
 
       // Toggle all the values and create a new cloud job ticket.
-      toggleSettings();
+      toggleSettings(testDestination);
       const newTicket = model.createCloudJobTicket(testDestination);
       const expectedNewTicket = JSON.stringify({
         version: '1.0',
@@ -362,10 +364,10 @@
             type: testDestination.getSelectedColorOption(false).type,
           },
           copies: {copies: 2},
-          duplex: {type: 'NO_DUPLEX'},
+          duplex: {type: 'LONG_EDGE'},
           media_size: {
-            width_microns: 240000,
-            height_microns: 200000,
+            width_microns: 215900,
+            height_microns: 215900,
           },
           page_orientation: {type: 'LANDSCAPE'},
           dpi: {
@@ -373,8 +375,8 @@
             vertical_dpi: 100,
           },
           vendor_ticket_item: [
-            {id: 'paperType', value: 1},
             {id: 'printArea', value: 6},
+            {id: 'paperType', value: 1},
           ],
         },
       });
@@ -429,6 +431,111 @@
       model.destination = destinations[3];
       assertRecentDestinations(['ID4', 'ID3', 'ID1']);
     });
+
+    test(assert(TestNames.ChangeDestination), function() {
+      const testDestination = new print_preview.Destination(
+          'FooDevice', print_preview.DestinationType.LOCAL,
+          print_preview.DestinationOrigin.LOCAL, 'FooName',
+          print_preview.DestinationConnectionStatus.ONLINE);
+      testDestination.capabilities =
+          print_preview_test_utils.getCddTemplateWithAdvancedSettings(2)
+              .capabilities;
+      // Make black and white printing the default.
+      testDestination.capabilities.printer.color = {
+        option: [
+          {type: 'STANDARD_COLOR'},
+          {type: 'STANDARD_MONOCHROME', is_default: true}
+        ]
+      };
+
+      const testDestination2 = new print_preview.Destination(
+          'BarDevice', print_preview.DestinationType.LOCAL,
+          print_preview.DestinationOrigin.LOCAL, 'BarName',
+          print_preview.DestinationConnectionStatus.ONLINE);
+      testDestination2.capabilities =
+          Object.assign({}, testDestination.capabilities);
+
+      // Initialize
+      initializeModel();
+      model.destination = testDestination;
+      model.applyStickySettings();
+
+      // Confirm some defaults.
+      assertEquals(false, model.getSettingValue('color'));
+      assertEquals('NA_LETTER', model.getSettingValue('mediaSize').name);
+      assertEquals(200, model.getSettingValue('dpi').horizontal_dpi);
+      assertEquals(false, model.getSettingValue('duplex'));
+
+      // Toggle some printer specified settings.
+      model.setSetting('duplex', true);
+      model.setSetting(
+          'mediaSize',
+          testDestination.capabilities.printer.media_size.option[1]);
+      model.setSetting('color', true);
+      model.setSetting(
+          'dpi', testDestination.capabilities.printer.dpi.option[1]);
+
+      // Confirm toggles.
+      assertEquals(true, model.getSettingValue('color'));
+      assertEquals('CUSTOM_SQUARE', model.getSettingValue('mediaSize').name);
+      assertEquals(100, model.getSettingValue('dpi').horizontal_dpi);
+      assertEquals(true, model.getSettingValue('duplex'));
+
+      // Set to a new destination with the same capabilities. Confirm that
+      // everything stays the same.
+      const oldSettings = JSON.stringify(model.settings);
+      model.destination = testDestination2;
+      const newSettings = JSON.stringify(model.settings);
+
+      // Should be the same (same printer capabilities).
+      assertEquals(oldSettings, newSettings);
+
+      // Create a printer with different capabilities.
+      const testDestination3 = new print_preview.Destination(
+          'Device1', print_preview.DestinationType.LOCAL,
+          print_preview.DestinationOrigin.LOCAL, 'One',
+          print_preview.DestinationConnectionStatus.ONLINE);
+      testDestination3.capabilities =
+          Object.assign({}, testDestination.capabilities);
+      testDestination3.capabilities.printer.media_size = {
+        option: [
+          {
+            name: 'ISO_A4',
+            width_microns: 210000,
+            height_microns: 297000,
+            custom_display_name: 'A4',
+            is_default: true,
+          },
+        ]
+      };
+      testDestination3.capabilities.printer.color = {
+        option: [
+          {type: 'STANDARD_MONOCHROME', is_default: true},
+        ]
+      };
+      testDestination3.capabilities.printer.duplex = {
+        option: [
+          {type: 'NO_DUPLEX', is_default: true},
+        ]
+      };
+      testDestination3.capabilities.printer.dpi = {
+        option: [
+          {horizontal_dpi: 400, vertical_dpi: 400, is_default: true},
+          {horizontal_dpi: 800, vertical_dpi: 800},
+        ]
+      };
+
+      model.destination = testDestination3;
+      Polymer.dom.flush();
+
+      // Verify things changed.
+      const updatedSettings = JSON.stringify(model.settings);
+      assertNotEquals(oldSettings, updatedSettings);
+      assertEquals(false, model.getSettingValue('color'));
+      assertEquals('ISO_A4', model.getSettingValue('mediaSize').name);
+      assertEquals(400, model.getSettingValue('dpi').horizontal_dpi);
+      assertEquals(false, model.getSettingValue('duplex'));
+    });
   });
 
   return {
diff --git a/chrome/test/data/webui/print_preview/new_print_preview_ui_browsertest.js b/chrome/test/data/webui/print_preview/new_print_preview_ui_browsertest.js
index 595d839..03508b3b 100644
--- a/chrome/test/data/webui/print_preview/new_print_preview_ui_browsertest.js
+++ b/chrome/test/data/webui/print_preview/new_print_preview_ui_browsertest.js
@@ -64,78 +64,15 @@
   this.runMochaTest(print_preview_app_test.TestNames.PrintToGoogleDrive);
 });
 
-PrintPreviewSettingsSectionsTest = class extends NewPrintPreviewTest {
-  /** @override */
-  get browsePreload() {
-    return 'chrome://print/new/app.html';
-  }
-
-  /** @override */
-  get extraLibraries() {
-    return super.extraLibraries.concat([
-      '../settings/test_util.js',
-      '../test_browser_proxy.js',
-      'native_layer_stub.js',
-      'plugin_stub.js',
-      'print_preview_test_utils.js',
-      'settings_section_test.js',
-    ]);
-  }
-
-  /** @override */
-  get suiteName() {
-    return settings_sections_tests.suiteName;
-  }
-};
-
-TEST_F(
-    'PrintPreviewSettingsSectionsTest', 'SettingsSectionsVisibilityChange',
-    function() {
-      this.runMochaTest(
-          settings_sections_tests.TestNames.SettingsSectionsVisibilityChange);
-    });
-
-TEST_F('PrintPreviewSettingsSectionsTest', 'SetLayout', function() {
-  this.runMochaTest(settings_sections_tests.TestNames.SetLayout);
+TEST_F('PrintPreviewAppTest', 'SettingsSectionsVisibilityChange', function() {
+  this.runMochaTest(
+      print_preview_app_test.TestNames.SettingsSectionsVisibilityChange);
 });
 
-TEST_F('PrintPreviewSettingsSectionsTest', 'SetColor', function() {
-  this.runMochaTest(settings_sections_tests.TestNames.SetColor);
+TEST_F('PrintPreviewAppTest', 'PrintPresets', function() {
+  this.runMochaTest(print_preview_app_test.TestNames.PrintPresets);
 });
 
-TEST_F('PrintPreviewSettingsSectionsTest', 'SetMargins', function() {
-  this.runMochaTest(settings_sections_tests.TestNames.SetMargins);
-});
-
-TEST_F('PrintPreviewSettingsSectionsTest', 'SetPagesPerSheet', function() {
-  this.runMochaTest(settings_sections_tests.TestNames.SetPagesPerSheet);
-});
-
-TEST_F('PrintPreviewSettingsSectionsTest', 'PresetCopies', function() {
-  this.runMochaTest(settings_sections_tests.TestNames.PresetCopies);
-});
-
-TEST_F('PrintPreviewSettingsSectionsTest', 'PresetDuplex', function() {
-  this.runMochaTest(settings_sections_tests.TestNames.PresetDuplex);
-});
-
-GEN('#if defined(OS_CHROMEOS)');
-TEST_F('PrintPreviewSettingsSectionsTest', 'ColorManaged', function() {
-  this.runMochaTest(settings_sections_tests.TestNames.ColorManaged);
-});
-
-TEST_F('PrintPreviewSettingsSectionsTest', 'DuplexManaged', function() {
-  this.runMochaTest(settings_sections_tests.TestNames.DuplexManaged);
-});
-GEN('#endif');
-
-TEST_F(
-    'PrintPreviewSettingsSectionsTest', 'DisableMarginsByPagesPerSheet',
-    function() {
-      this.runMochaTest(
-          settings_sections_tests.TestNames.DisableMarginsByPagesPerSheet);
-    });
-
 PrintPreviewPagesSettingsTest = class extends NewPrintPreviewTest {
   /** @override */
   get browsePreload() {
@@ -358,6 +295,10 @@
   this.runMochaTest(model_test.TestNames.UpdateRecentDestinations);
 });
 
+TEST_F('PrintPreviewModelTest', 'ChangeDestination', function() {
+  this.runMochaTest(model_test.TestNames.ChangeDestination);
+});
+
 PrintPreviewModelSettingsAvailabilityTest = class extends NewPrintPreviewTest {
   /** @override */
   get browsePreload() {
@@ -378,6 +319,28 @@
   mocha.run();
 });
 
+GEN('#if defined(OS_CHROMEOS)');
+PrintPreviewModelSettingsPolicyTest = class extends NewPrintPreviewTest {
+  /** @override */
+  get browsePreload() {
+    return 'chrome://print/new/model.html';
+  }
+
+  /** @override */
+  get extraLibraries() {
+    return super.extraLibraries.concat([
+      '../settings/test_util.js',
+      'print_preview_test_utils.js',
+      'model_settings_policy_test.js',
+    ]);
+  }
+};
+
+TEST_F('PrintPreviewModelSettingsPolicyTest', 'All', function() {
+  mocha.run();
+});
+GEN('#endif');
+
 PrintPreviewPreviewGenerationTest = class extends NewPrintPreviewTest {
   /** @override */
   get browsePreload() {
@@ -1361,3 +1324,83 @@
 TEST_F('PrintPreviewOtherOptionsSettingsTest', 'All', function() {
   mocha.run();
 });
+
+PrintPreviewLayoutSettingsTest = class extends NewPrintPreviewTest {
+  /** @override */
+  get browsePreload() {
+    return 'chrome://print/new/layout_settings.html';
+  }
+
+  /** @override */
+  get extraLibraries() {
+    return super.extraLibraries.concat([
+      '../settings/test_util.js',
+      'print_preview_test_utils.js',
+      'layout_settings_test.js',
+    ]);
+  }
+};
+
+TEST_F('PrintPreviewLayoutSettingsTest', 'All', function() {
+  mocha.run();
+});
+
+PrintPreviewColorSettingsTest = class extends NewPrintPreviewTest {
+  /** @override */
+  get browsePreload() {
+    return 'chrome://print/new/color_settings.html';
+  }
+
+  /** @override */
+  get extraLibraries() {
+    return super.extraLibraries.concat([
+      '../settings/test_util.js',
+      'print_preview_test_utils.js',
+      'color_settings_test.js',
+    ]);
+  }
+};
+
+TEST_F('PrintPreviewColorSettingsTest', 'All', function() {
+  mocha.run();
+});
+
+PrintPreviewMarginsSettingsTest = class extends NewPrintPreviewTest {
+  /** @override */
+  get browsePreload() {
+    return 'chrome://print/new/margins_settings.html';
+  }
+
+  /** @override */
+  get extraLibraries() {
+    return super.extraLibraries.concat([
+      '../settings/test_util.js',
+      'print_preview_test_utils.js',
+      'margins_settings_test.js',
+    ]);
+  }
+};
+
+TEST_F('PrintPreviewMarginsSettingsTest', 'All', function() {
+  mocha.run();
+});
+
+PrintPreviewPagesPerSheetSettingsTest = class extends NewPrintPreviewTest {
+  /** @override */
+  get browsePreload() {
+    return 'chrome://print/new/pages_per_sheet_settings.html';
+  }
+
+  /** @override */
+  get extraLibraries() {
+    return super.extraLibraries.concat([
+      '../settings/test_util.js',
+      'print_preview_test_utils.js',
+      'pages_per_sheet_settings_test.js',
+    ]);
+  }
+};
+
+TEST_F('PrintPreviewPagesPerSheetSettingsTest', 'All', function() {
+  mocha.run();
+});
diff --git a/chrome/test/data/webui/print_preview/other_options_settings_test.js b/chrome/test/data/webui/print_preview/other_options_settings_test.js
index 083aaa3..04f0590 100644
--- a/chrome/test/data/webui/print_preview/other_options_settings_test.js
+++ b/chrome/test/data/webui/print_preview/other_options_settings_test.js
@@ -110,13 +110,29 @@
       ['headerFooter', 'duplex', 'cssBackground', 'rasterize', 'selectionOnly']
           .forEach(setting => {
             const checkbox = otherOptionsSection.$$(`#${setting}`);
-            // Set false and then true.
+            // Set true and then false.
             [true, false].forEach(value => {
               otherOptionsSection.setSetting(setting, value);
-              // Element expected to be visible when available.
+              // Element expected to be checked when setting is true.
               assertEquals(value, checkbox.checked);
             });
           });
     });
+
+    // Tests that if settings are enforced by enterprise policy the checkbox
+    // is disabled.
+    test('disabled by policy', function() {
+      const policyControlledSettings =
+          cr.isChromeOS ? ['headerFooter', 'duplex'] : ['headerFooter'];
+      policyControlledSettings.forEach(setting => {
+        const checkbox = otherOptionsSection.$$(`#${setting}`);
+        // Set true and then false.
+        [true, false].forEach(value => {
+          otherOptionsSection.set(`settings.${setting}.setByPolicy`, value);
+          // Element expected to be disabled when policy is set.
+          assertEquals(value, checkbox.disabled);
+        });
+      });
+    });
   });
 });
diff --git a/chrome/test/data/webui/print_preview/pages_per_sheet_settings_test.js b/chrome/test/data/webui/print_preview/pages_per_sheet_settings_test.js
new file mode 100644
index 0000000..cafb455
--- /dev/null
+++ b/chrome/test/data/webui/print_preview/pages_per_sheet_settings_test.js
@@ -0,0 +1,52 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+cr.define('pages_per_sheet_settings_test', function() {
+  suite('PagesPerSheetSettingsTest', function() {
+    let pagesPerSheetSection = null;
+
+    /** @override */
+    setup(function() {
+      PolymerTest.clearBody();
+      pagesPerSheetSection =
+          document.createElement('print-preview-pages-per-sheet-settings');
+      pagesPerSheetSection.settings = {
+        pagesPerSheet: {
+          value: 1,
+          unavailableValue: 1,
+          valid: true,
+          available: true,
+          setByPolicy: false,
+          key: '',
+        },
+      };
+      pagesPerSheetSection.disabled = false;
+      document.body.appendChild(pagesPerSheetSection);
+    });
+
+    // Tests that setting the setting updates the UI.
+    test('set setting', async () => {
+      const select = pagesPerSheetSection.$$('select');
+      assertEquals('1', select.value);
+
+      pagesPerSheetSection.set('settings.pagesPerSheet.value', 4);
+      await test_util.eventToPromise(
+          'process-select-change', pagesPerSheetSection);
+      assertEquals('4', select.value);
+    });
+
+    // Tests that selecting a new option in the dropdown updates the setting.
+    test('select option', async () => {
+      // Verify that the selected option and names are as expected.
+      const select = pagesPerSheetSection.$$('select');
+      assertEquals('1', select.value);
+      assertEquals(1, pagesPerSheetSection.getSettingValue('pagesPerSheet'));
+      assertEquals(6, select.options.length);
+
+      // Verify that selecting an new option in the dropdown sets the setting.
+      await print_preview_test_utils.selectOption(pagesPerSheetSection, '2');
+      assertEquals(2, pagesPerSheetSection.getSettingValue('pagesPerSheet'));
+    });
+  });
+});
diff --git a/chrome/test/data/webui/print_preview/print_preview_app_test.js b/chrome/test/data/webui/print_preview/print_preview_app_test.js
index 697f5a2d..d7bddbf 100644
--- a/chrome/test/data/webui/print_preview/print_preview_app_test.js
+++ b/chrome/test/data/webui/print_preview/print_preview_app_test.js
@@ -6,6 +6,8 @@
   /** @enum {string} */
   const TestNames = {
     PrintToGoogleDrive: 'print to google drive',
+    SettingsSectionsVisibilityChange: 'settings sections visibility change',
+    PrintPresets: 'print presets',
   };
 
   const suiteName = 'PrintPreviewAppTest';
@@ -116,6 +118,40 @@
           print_preview.Destination.GooglePromotedId.DOCS, args.destination.id);
       assertEquals('1.0', JSON.parse(args.printTicket).version);
     });
+
+    test(assert(TestNames.SettingsSectionsVisibilityChange), async () => {
+      await finishSetup();
+      const moreSettingsElement = page.$$('print-preview-more-settings');
+      moreSettingsElement.$.label.click();
+      const camelToKebab = s => s.replace(/([A-Z])/g, '-$1').toLowerCase();
+      ['copies', 'layout', 'color', 'mediaSize', 'margins', 'dpi', 'scaling',
+       'otherOptions']
+          .forEach(setting => {
+            const element =
+                page.$$(`print-preview-${camelToKebab(setting)}-settings`);
+            // Show, hide and reset.
+            [true, false, true].forEach(value => {
+              page.set(`settings.${setting}.available`, value);
+              // Element expected to be visible when available.
+              assertEquals(!value, element.hidden);
+            });
+          });
+    });
+
+    test(assert(TestNames.PrintPresets), async () => {
+      await finishSetup();
+
+      assertEquals(1, page.settings.copies.value);
+      page.setSetting('duplex', false);
+      assertFalse(page.settings.duplex.value);
+
+      // Send preset values of duplex LONG_EDGE and 2 copies.
+      const copies = 2;
+      const duplex = print_preview_new.DuplexMode.LONG_EDGE;
+      cr.webUIListenerCallback('print-preset-options', true, copies, duplex);
+      assertEquals(copies, page.getSettingValue('copies'));
+      assertTrue(page.getSettingValue('duplex'));
+    });
   });
 
   return {
diff --git a/chrome/test/data/webui/print_preview/settings_section_test.js b/chrome/test/data/webui/print_preview/settings_section_test.js
deleted file mode 100644
index f374ac9..0000000
--- a/chrome/test/data/webui/print_preview/settings_section_test.js
+++ /dev/null
@@ -1,435 +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.
-
-cr.define('settings_sections_tests', function() {
-  /** @enum {string} */
-  const TestNames = {
-    SettingsSectionsVisibilityChange: 'settings sections visibility change',
-    SetLayout: 'set layout',
-    SetColor: 'set color',
-    SetMargins: 'set margins',
-    SetPagesPerSheet: 'set pages per sheet',
-    PresetCopies: 'preset copies',
-    PresetDuplex: 'preset duplex',
-    DisableMarginsByPagesPerSheet: 'disable margins by pages per sheet',
-    ColorManaged: 'color managed',
-    DuplexManaged: 'duplex managed',
-  };
-
-  const suiteName = 'SettingsSectionsTests';
-  suite(suiteName, function() {
-    /** @type {?PrintPreviewAppElement} */
-    let page = null;
-
-    /** @override */
-    setup(function() {
-      const initialSettings =
-          print_preview_test_utils.getDefaultInitialSettings();
-      const nativeLayer = new print_preview.NativeLayerStub();
-      nativeLayer.setInitialSettings(initialSettings);
-      nativeLayer.setLocalDestinationCapabilities(
-          print_preview_test_utils.getCddTemplate(initialSettings.printerName));
-      nativeLayer.setPageCount(3);
-      print_preview.NativeLayer.setInstance(nativeLayer);
-      const pluginProxy = new print_preview.PDFPluginStub();
-      print_preview_new.PluginProxy.setInstance(pluginProxy);
-
-      PolymerTest.clearBody();
-      page = document.createElement('print-preview-app');
-      document.body.appendChild(page);
-      const previewArea = page.$.previewArea;
-      pluginProxy.setLoadCallback(previewArea.onPluginLoad_.bind(previewArea));
-
-      // Wait for initialization to complete.
-      return Promise
-          .all([
-            nativeLayer.whenCalled('getInitialSettings'),
-            nativeLayer.whenCalled('getPrinterCapabilities')
-          ])
-          .then(function() {
-            Polymer.dom.flush();
-          });
-    });
-
-    /**
-     * @param {boolean} isPdf Whether the document should be a PDF.
-     * @param {boolean} hasSelection Whether the document has a selection.
-     */
-    function initDocumentInfo(isPdf, hasSelection) {
-      const info = page.$.documentInfo;
-      info.init(!isPdf, 'title', hasSelection);
-      if (isPdf) {
-        info.set('documentSettings.fitToPageScaling', 98);
-      }
-      info.set('documentSettings.pageCount', 3);
-      info.margins = null;
-      Polymer.dom.flush();
-    }
-
-    function addSelection() {
-      // Add a selection.
-      page.$.documentInfo.init(
-          page.documentSettings_.isModifiable, 'title', true);
-      Polymer.dom.flush();
-    }
-
-    function setPdfDestination() {
-      const saveAsPdfDestination =
-          print_preview_test_utils.getSaveAsPdfDestination();
-      saveAsPdfDestination.capabilities =
-          print_preview_test_utils.getCddTemplate(saveAsPdfDestination.id)
-              .capabilities;
-      page.set('destination_', saveAsPdfDestination);
-    }
-
-    function toggleMoreSettings() {
-      const moreSettingsElement = page.$$('print-preview-more-settings');
-      moreSettingsElement.$.label.click();
-    }
-
-    test(assert(TestNames.SettingsSectionsVisibilityChange), function() {
-      toggleMoreSettings();
-      const camelToKebab = s => s.replace(/([A-Z])/g, '-$1').toLowerCase();
-      ['copies', 'layout', 'color', 'mediaSize', 'margins', 'dpi', 'scaling',
-       'otherOptions']
-          .forEach(setting => {
-            const element =
-                page.$$(`print-preview-${camelToKebab(setting)}-settings`);
-            // Show, hide and reset.
-            [true, false, true].forEach(value => {
-              page.set(`settings.${setting}.available`, value);
-              // Element expected to be visible when available.
-              assertEquals(!value, element.hidden);
-            });
-          });
-    });
-
-    test(assert(TestNames.SetLayout), function() {
-      const layoutElement = page.$$('print-preview-layout-settings');
-      assertFalse(layoutElement.hidden);
-
-      // Default is portrait
-      const layoutInput = layoutElement.$$('select');
-      assertEquals('portrait', layoutInput.value);
-      assertFalse(page.settings.layout.value);
-
-      // Change to landscape
-      return print_preview_test_utils.selectOption(layoutElement, 'landscape')
-          .then(function() {
-            assertTrue(page.settings.layout.value);
-          });
-    });
-
-    test(assert(TestNames.SetColor), function() {
-      const colorElement = page.$$('print-preview-color-settings');
-      assertFalse(colorElement.hidden);
-
-      // Default is color
-      const colorInput = colorElement.$$('select');
-      assertEquals('color', colorInput.value);
-      assertTrue(page.settings.color.value);
-
-      // Change to black and white.
-      return print_preview_test_utils.selectOption(colorElement, 'bw')
-          .then(function() {
-            assertFalse(page.settings.color.value);
-          });
-    });
-
-    test(assert(TestNames.SetMargins), function() {
-      toggleMoreSettings();
-      const marginsElement = page.$$('print-preview-margins-settings');
-      assertFalse(marginsElement.hidden);
-
-      // Default is DEFAULT_MARGINS
-      const marginsInput = marginsElement.$$('select');
-      assertEquals(
-          print_preview.ticket_items.MarginsTypeValue.DEFAULT.toString(),
-          marginsInput.value);
-      assertEquals(
-          print_preview.ticket_items.MarginsTypeValue.DEFAULT,
-          page.settings.margins.value);
-
-      // Change to minimum.
-      return print_preview_test_utils
-          .selectOption(
-              marginsElement,
-              print_preview.ticket_items.MarginsTypeValue.MINIMUM.toString())
-          .then(function() {
-            assertEquals(
-                print_preview.ticket_items.MarginsTypeValue.MINIMUM,
-                page.settings.margins.value);
-          });
-    });
-
-    test(assert(TestNames.SetPagesPerSheet), function() {
-      toggleMoreSettings();
-      const pagesPerSheetElement =
-          page.$$('print-preview-pages-per-sheet-settings');
-      assertFalse(pagesPerSheetElement.hidden);
-
-      assertEquals('1', pagesPerSheetElement.$$('select').value);
-
-      // Change to a different value
-      return print_preview_test_utils.selectOption(pagesPerSheetElement, '2')
-          .then(function() {
-            assertEquals(2, page.settings.pagesPerSheet.value);
-          });
-    });
-
-    // This test verifies that changing pages per sheet to N > 1 resets the
-    // margins dropdown value to DEFAULT and disables it, and resetting
-    // pages per sheet back to 1 re-enables the dropdown.
-    test(assert(TestNames.DisableMarginsByPagesPerSheet), function() {
-      toggleMoreSettings();
-      const pagesPerSheetElement =
-          page.$$('print-preview-pages-per-sheet-settings');
-      assertFalse(pagesPerSheetElement.hidden);
-
-      const pagesPerSheetInput = pagesPerSheetElement.$$('select');
-      assertEquals(1, page.settings.pagesPerSheet.value);
-
-      const marginsElement = page.$$('print-preview-margins-settings');
-      assertFalse(marginsElement.hidden);
-
-      const marginsTypeEnum = print_preview.ticket_items.MarginsTypeValue;
-
-      // Default is DEFAULT_MARGINS
-      const marginsInput = marginsElement.$$('select');
-      assertEquals(marginsTypeEnum.DEFAULT, page.settings.margins.value);
-
-      // Change margins to minimum.
-      return print_preview_test_utils
-          .selectOption(marginsElement, marginsTypeEnum.MINIMUM.toString())
-          .then(function() {
-            assertEquals(marginsTypeEnum.MINIMUM, page.settings.margins.value);
-            assertFalse(marginsInput.disabled);
-            // Change pages per sheet to a different value.
-            return print_preview_test_utils.selectOption(
-                pagesPerSheetElement, '2');
-          })
-          .then(function() {
-            assertEquals(2, page.settings.pagesPerSheet.value);
-            assertEquals(marginsTypeEnum.DEFAULT, page.settings.margins.value);
-            assertEquals(
-                marginsTypeEnum.DEFAULT.toString(), marginsInput.value);
-            assertTrue(marginsInput.disabled);
-
-            // Set pages per sheet back to 1.
-            return print_preview_test_utils.selectOption(
-                pagesPerSheetElement, '1');
-          })
-          .then(function() {
-            assertEquals(1, page.settings.pagesPerSheet.value);
-            assertEquals(marginsTypeEnum.DEFAULT, page.settings.margins.value);
-            assertEquals(
-                marginsTypeEnum.DEFAULT.toString(), marginsInput.value);
-            assertFalse(marginsInput.disabled);
-          });
-    });
-
-    test(assert(TestNames.PresetCopies), function() {
-      const copiesElement = page.$$('print-preview-copies-settings');
-      assertFalse(copiesElement.hidden);
-
-      // Default value is 1
-      const copiesInput =
-          copiesElement.$$('print-preview-number-settings-section')
-              .$.userValue.inputElement;
-      assertEquals('1', copiesInput.value);
-      assertEquals(1, page.settings.copies.value);
-
-      // Send a preset value of 2
-      const copies = 2;
-      cr.webUIListenerCallback('print-preset-options', true, copies);
-      assertEquals(copies, page.settings.copies.value);
-      assertEquals(copies.toString(), copiesInput.value);
-    });
-
-    test(assert(TestNames.PresetDuplex), function() {
-      toggleMoreSettings();
-      const optionsElement = page.$$('print-preview-other-options-settings');
-      assertFalse(optionsElement.hidden);
-
-      // Default value is on, so turn it off
-      page.setSetting('duplex', false);
-      const checkbox = optionsElement.$$('#duplex');
-      assertFalse(checkbox.checked);
-      assertFalse(page.settings.duplex.value);
-
-      // Send a preset value of LONG_EDGE
-      const duplex = print_preview_new.DuplexMode.LONG_EDGE;
-      cr.webUIListenerCallback('print-preset-options', false, 1, duplex);
-      assertTrue(page.settings.duplex.value);
-      assertTrue(checkbox.checked);
-    });
-
-    test(assert(TestNames.ColorManaged), function() {
-      const colorElement = page.$$('print-preview-color-settings');
-      assertFalse(colorElement.hidden);
-
-      // Remove color capability.
-      let capabilities =
-          print_preview_test_utils.getCddTemplate('FooPrinter').capabilities;
-      delete capabilities.printer.color;
-
-      [{
-        // Policy has no effect.
-        colorCap: {option: [{type: 'STANDARD_COLOR', is_default: true}]},
-        colorPolicy: print_preview.ColorMode.COLOR,
-        colorDefault: print_preview.ColorMode.COLOR,
-        expectedValue: true,
-        expectedHidden: true,
-        expectedManaged: false,
-      },
-       {
-         // Policy contradicts actual capabilities and is ignored.
-         colorCap: {option: [{type: 'STANDARD_COLOR', is_default: true}]},
-         colorPolicy: print_preview.ColorMode.GRAY,
-         colorDefault: print_preview.ColorMode.GRAY,
-         expectedValue: true,
-         expectedHidden: true,
-         expectedManaged: false,
-       },
-       {
-         // Policy overrides default.
-         colorCap: {
-           option: [
-             {type: 'STANDARD_MONOCHROME', is_default: true},
-             {type: 'STANDARD_COLOR'}
-           ]
-         },
-         colorPolicy: print_preview.ColorMode.COLOR,
-         // Default mismatches restriction and is ignored.
-         colorDefault: print_preview.ColorMode.GRAY,
-         expectedValue: true,
-         expectedHidden: false,
-         expectedManaged: true,
-       },
-       {
-         // Default defined by policy but setting is modifiable.
-         colorCap: {
-           option: [
-             {type: 'STANDARD_MONOCHROME', is_default: true},
-             {type: 'STANDARD_COLOR'}
-           ]
-         },
-         colorDefault: print_preview.ColorMode.COLOR,
-         expectedValue: true,
-         expectedHidden: false,
-         expectedManaged: false,
-       }].forEach(subtestParams => {
-        capabilities =
-            print_preview_test_utils.getCddTemplate('FooPrinter').capabilities;
-        capabilities.printer.color = subtestParams.colorCap;
-        const policies = {
-          allowedColorModes: subtestParams.colorPolicy,
-          defaultColorMode: subtestParams.colorDefault,
-        };
-        // In practice |capabilities| are always set after |policies| and
-        // observers only check for |capabilities|, so the order is important.
-        page.set('destination_.policies', policies);
-        page.set('destination_.capabilities', capabilities);
-        page.$$('print-preview-model').applyDestinationSpecificPolicies();
-        assertEquals(subtestParams.expectedManaged, page.controlsManaged_);
-        assertEquals(
-            subtestParams.expectedValue, page.getSettingValue('color'));
-        assertEquals(subtestParams.expectedHidden, colorElement.hidden);
-        if (!subtestParams.expectedHidden) {
-          const selectElement = colorElement.$$('select');
-          assertEquals(
-              subtestParams.expectedValue ? 'color' : 'bw',
-              selectElement.value);
-          assertEquals(subtestParams.expectedManaged, selectElement.disabled);
-        }
-      });
-    });
-
-    test(assert(TestNames.DuplexManaged), function() {
-      toggleMoreSettings();
-      const optionsElement = page.$$('print-preview-other-options-settings');
-      const duplexElement = optionsElement.$$('#duplex');
-
-      // Remove duplex capability.
-      let capabilities =
-          print_preview_test_utils.getCddTemplate('FooPrinter').capabilities;
-      delete capabilities.printer.duplex;
-
-      [{
-        // Policy has no effect.
-        duplexCap: {option: [{type: 'NO_DUPLEX', is_default: true}]},
-        duplexPolicy: print_preview.DuplexModeRestriction.SIMPLEX,
-        duplexDefault: print_preview.DuplexModeRestriction.SIMPLEX,
-        expectedValue: false,
-        expectedHidden: true,
-        expectedManaged: false,
-      },
-       {
-         // Policy contradicts actual capabilities and is ignored.
-         duplexCap: {option: [{type: 'NO_DUPLEX', is_default: true}]},
-         duplexPolicy: print_preview.DuplexModeRestriction.DUPLEX,
-         duplexDefault: print_preview.DuplexModeRestriction.LONG_EDGE,
-         expectedValue: false,
-         expectedHidden: true,
-         expectedManaged: false,
-       },
-       {
-         // Policy overrides default.
-         duplexCap: {
-           option: [
-             {type: 'NO_DUPLEX', is_default: true}, {type: 'LONG_EDGE'},
-             {type: 'SHORT_EDGE'}
-           ]
-         },
-         duplexPolicy: print_preview.DuplexModeRestriction.DUPLEX,
-         // Default mismatches restriction and is ignored.
-         duplexDefault: print_preview.DuplexModeRestriction.SIMPLEX,
-         expectedValue: true,
-         expectedHidden: false,
-         expectedManaged: true,
-       },
-       {
-         // Default defined by policy but setting is modifiable.
-         duplexCap: {
-           option: [
-             {type: 'NO_DUPLEX', is_default: true}, {type: 'LONG_EDGE'},
-             {type: 'SHORT_EDGE'}
-           ]
-         },
-         duplexDefault: print_preview.DuplexModeRestriction.LONG_EDGE,
-         expectedValue: true,
-         expectedHidden: false,
-         expectedManaged: false,
-       }].forEach(subtestParams => {
-        capabilities =
-            print_preview_test_utils.getCddTemplate('FooPrinter').capabilities;
-        capabilities.printer.duplex = subtestParams.duplexCap;
-        const policies = {
-          allowedDuplexModes: subtestParams.duplexPolicy,
-          defaultDuplexMode: subtestParams.duplexDefault,
-        };
-        // In practice |capabilities| are always set after |policies| and
-        // observers only check for |capabilities|, so the order is important.
-        page.set('destination_.policies', policies);
-        page.set('destination_.capabilities', capabilities);
-        page.$$('print-preview-model').applyDestinationSpecificPolicies();
-        assertEquals(subtestParams.expectedManaged, page.controlsManaged_);
-        assertEquals(
-            subtestParams.expectedValue, page.getSettingValue('duplex'));
-        assertEquals(
-            subtestParams.expectedHidden,
-            duplexElement.parentNode.parentNode.hidden);
-        if (!subtestParams.expectedHidden) {
-          assertEquals(subtestParams.expectedValue, duplexElement.checked);
-          assertEquals(subtestParams.expectedManaged, duplexElement.disabled);
-        }
-      });
-    });
-  });
-
-  return {
-    suiteName: suiteName,
-    TestNames: TestNames,
-  };
-});
diff --git a/chrome/test/data/webui/settings/autofill_page_test.js b/chrome/test/data/webui/settings/autofill_page_test.js
index f7a215b..548399f 100644
--- a/chrome/test/data/webui/settings/autofill_page_test.js
+++ b/chrome/test/data/webui/settings/autofill_page_test.js
@@ -124,10 +124,6 @@
       const expected = new PaymentsManagerExpectations();
       expected.requestedCreditCards = 1;
       expected.listeningCreditCards = 1;
-      expected.requestedLocalCreditCards = 1;
-      expected.listeningLocalCreditCards = 1;
-      expected.requestedServerCreditCards = 1;
-      expected.listeningServerCreditCards = 1;
       return expected;
     }
 
@@ -176,8 +172,6 @@
         autofillManager.assertExpectations(autofillExpectations);
 
         paymentsExpectations.listeningCreditCards = 0;
-        paymentsExpectations.listeningLocalCreditCards = 0;
-        paymentsExpectations.listeningServerCreditCards = 0;
         paymentsManager.assertExpectations(paymentsExpectations);
 
         destroyPrefs(prefs);
diff --git a/chrome/test/data/webui/settings/passwords_and_autofill_fake_data.js b/chrome/test/data/webui/settings/passwords_and_autofill_fake_data.js
index 9bf7f0a5a..a29a6d5 100644
--- a/chrome/test/data/webui/settings/passwords_and_autofill_fake_data.js
+++ b/chrome/test/data/webui/settings/passwords_and_autofill_fake_data.js
@@ -220,11 +220,7 @@
 class PaymentsManagerExpectations {
   constructor() {
     this.requestedCreditCards = 0;
-    this.requestedLocalCreditCards = 0;
-    this.requestedServerCreditCards = 0;
     this.listeningCreditCards = 0;
-    this.listeningLocalCreditCards = 0;
-    this.listeningServerCreditCards = 0;
   }
 }
 
@@ -239,15 +235,11 @@
   // Set these to have non-empty data.
   this.data = {
     creditCards: [],
-    localCreditCards: [],
-    serverCreditCards: [],
   };
 
   // Holds the last callbacks so they can be called when needed.
   this.lastCallback = {
     addCreditCardListChangedListener: null,
-    addLocalCreditCardListChangedListener: null,
-    addServerCreditCardListChangedListener: null,
   };
 }
 
@@ -259,50 +251,16 @@
   },
 
   /** @override */
-  addLocalCreditCardListChangedListener: function(listener) {
-    this.actual_.listeningLocalCreditCards++;
-    this.lastCallback.addLocalCreditCardListChangedListener = listener;
-  },
-
-  /** @override */
-  addServerCreditCardListChangedListener: function(listener) {
-    this.actual_.listeningServerCreditCards++;
-    this.lastCallback.addServerCreditCardListChangedListener = listener;
-  },
-
-  /** @override */
   removeCreditCardListChangedListener: function(listener) {
     this.actual_.listeningCreditCards--;
   },
 
   /** @override */
-  removeLocalCreditCardListChangedListener: function(listener) {
-    this.actual_.listeningLocalCreditCards--;
-  },
-
-  /** @override */
-  removeServerCreditCardListChangedListener: function(listener) {
-    this.actual_.listeningServerCreditCards--;
-  },
-
-  /** @override */
   getCreditCardList: function(callback) {
     this.actual_.requestedCreditCards++;
     callback(this.data.creditCards);
   },
 
-  /** @override */
-  getLocalCreditCardList: function(callback) {
-    this.actual_.requestedLocalCreditCards++;
-    callback(this.data.localCreditCards);
-  },
-
-  /** @override */
-  getServerCreditCardList: function(callback) {
-    this.actual_.requestedServerCreditCards++;
-    callback(this.data.serverCreditCards);
-  },
-
   /**
    * Verifies expectations.
    * @param {!PaymentsManagerExpectations} expected
@@ -310,14 +268,6 @@
   assertExpectations: function(expected) {
     const actual = this.actual_;
     assertEquals(expected.requestedCreditCards, actual.requestedCreditCards);
-    assertEquals(
-        expected.requestedLocalCreditCards, actual.requestedLocalCreditCards);
-    assertEquals(
-        expected.requestedServerCreditCards, actual.requestedServerCreditCards);
     assertEquals(expected.listeningCreditCards, actual.listeningCreditCards);
-    assertEquals(
-        expected.listeningLocalCreditCards, actual.listeningLocalCreditCards);
-    assertEquals(
-        expected.listeningServerCreditCards, actual.listeningServerCreditCards);
   },
 };
diff --git a/chrome/test/data/webui/settings/payments_section_test.js b/chrome/test/data/webui/settings/payments_section_test.js
index 774bdcb1..ccb978b 100644
--- a/chrome/test/data/webui/settings/payments_section_test.js
+++ b/chrome/test/data/webui/settings/payments_section_test.js
@@ -33,7 +33,6 @@
         isUsingSecondaryPassphrase: false,
         uploadToGoogleActive: true,
         userEmailDomainAllowed: true,
-        splitCreditCardList: false
       });
     });
 
@@ -58,29 +57,6 @@
     }
 
     /**
-     * Creates the payments autofill section for the given lists.
-     * @param {!Array<!chrome.autofillPrivate.CreditCardEntry>} localCreditCards
-     * @param {!Array<!chrome.autofillPrivate.CreditCardEntry>}
-     *     serverCreditCards
-     * @return {!Object}
-     */
-    function createSplitPaymentsSection(localCreditCards, serverCreditCards) {
-      loadTimeData.overrideValues({splitCreditCardList: true});
-
-      // Override the PaymentsManagerImpl for testing.
-      const paymentsManager = new TestPaymentsManager();
-      paymentsManager.data.localCreditCards = localCreditCards;
-      paymentsManager.data.serverCreditCards = serverCreditCards;
-      PaymentsManagerImpl.instance_ = paymentsManager;
-
-      const section = document.createElement('settings-payments-section');
-      document.body.appendChild(section);
-      Polymer.dom.flush();
-
-      return section;
-    }
-
-    /**
      * Creates the Edit Credit Card dialog.
      * @param {!chrome.autofillPrivate.CreditCardEntry} creditCardItem
      * @return {!Object}
@@ -105,37 +81,6 @@
     }
 
     /**
-     * Returns an array containing the local credit card items.
-     * @return {!Array<!chrome.autofillPrivate.CreditCardEntry>}
-     */
-    function getLocalListItems() {
-      return document.body.querySelector('settings-payments-section')
-          .$$('#localCreditCardList')
-          .shadowRoot.querySelectorAll('settings-credit-card-list-entry');
-    }
-
-    /**
-     * Returns an array containing the server credit card items.
-     * @return {!Array<!chrome.autofillPrivate.CreditCardEntry>}
-     */
-    function getServerListItems() {
-      return document.body.querySelector('settings-payments-section')
-          .$$('#serverCreditCardList')
-          .shadowRoot.querySelectorAll('settings-credit-card-list-entry');
-    }
-
-    /**
-     * Makes sure that the number of actual local and server credit cards
-     * match the given expectations.
-     * @param {number} expectedServer
-     * @param {number} expectedLocal
-     */
-    function assertCreditCards(expectedLocal, expectedServer) {
-      assertEquals(expectedLocal, getLocalListItems().length);
-      assertEquals(expectedServer, getServerListItems().length);
-    }
-
-    /**
      * Returns the shadow root of the card row from the specified card list.
      * @param {!HTMLElement} cardList
      * @return {?HTMLElement}
@@ -463,169 +408,6 @@
       assertFalse(!!rowShadowRoot.querySelector('#creditCardMenu'));
     });
 
-    test('verifyLocalCreditCardInSplitSettings', function() {
-      // Create a local credit card.
-      const localCard = FakeDataMaker.creditCardEntry();
-      assertTrue(localCard.metadata.isLocal);
-
-      const section = createSplitPaymentsSection([localCard], []);
-      assertCreditCards(1 /*expectedLocal*/, 0 /*expectedServer*/);
-
-      // Make sure the button is a dropdown and not an outlink.
-      const rowShadowRoot =
-          getCardRowShadowRoot(section.$$('#localCreditCardList'));
-      const menuButton = rowShadowRoot.querySelector('#creditCardMenu');
-      assertTrue(!!menuButton);
-      const outlinkButton =
-          rowShadowRoot.querySelector('paper-icon-button-light.icon-external');
-      assertFalse(!!outlinkButton);
-    });
-
-    test('verifyMaskedServerCreditCardInSplitSettings', function() {
-      // Create a server credit card.
-      const maskedServerCard = FakeDataMaker.creditCardEntry();
-      maskedServerCard.metadata.isLocal = false;
-      maskedServerCard.metadata.isCached = false;
-
-      const section = createSplitPaymentsSection([], [maskedServerCard]);
-      assertCreditCards(0 /*expectedLocal*/, 1 /*expectedServer*/);
-
-      // Make sure the button is an outlink and not a dropdown.
-      const rowShadowRoot =
-          getCardRowShadowRoot(section.$$('#serverCreditCardList'));
-      const menuButton = rowShadowRoot.querySelector('#creditCardMenu');
-      assertFalse(!!menuButton);
-      const outlinkButton =
-          rowShadowRoot.querySelector('paper-icon-button-light.icon-external');
-      assertTrue(!!outlinkButton);
-    });
-
-    test('verifyUnmaskedServerCreditCardInSplitSettings', function() {
-      // Create a full (unmasked) server credit card.
-      const fullServerCard = FakeDataMaker.creditCardEntry();
-      fullServerCard.metadata.isLocal = false;
-      fullServerCard.metadata.isCached = true;
-
-      const section =
-          createSplitPaymentsSection([fullServerCard], [fullServerCard]);
-
-      // Make sure the card is present in the two sections.
-      assertCreditCards(1 /*expectedLocal*/, 1 /*expectedServer*/);
-
-      // Make sure the button is a dropdown and not an outlink for both the
-      // local and server sections.
-      const localRowShadowRoot =
-          getCardRowShadowRoot(section.$$('#localCreditCardList'));
-      menuButton = localRowShadowRoot.querySelector('#creditCardMenu');
-      assertTrue(!!menuButton);
-      outlinkButton = localRowShadowRoot.querySelector(
-          'paper-icon-button-light.icon-external');
-      assertFalse(!!outlinkButton);
-
-      const serverRowShadowRoot =
-          getCardRowShadowRoot(section.$$('#serverCreditCardList'));
-      menuButton = serverRowShadowRoot.querySelector('#creditCardMenu');
-      assertTrue(!!menuButton);
-      outlinkButton = serverRowShadowRoot.querySelector(
-          'paper-icon-button-light.icon-external');
-      assertFalse(!!outlinkButton);
-    });
-
-    test('verifyLocalCreditCardMenu_SplitSettings', function() {
-      // Create a local credit card.
-      const creditCard = FakeDataMaker.creditCardEntry();
-      assertTrue(creditCard.metadata.isLocal);
-
-      const section = createSplitPaymentsSection([creditCard], []);
-      assertEquals(1, getLocalListItems().length);
-
-      // Local credit cards will show the overflow menu.
-      const localRowShadowRoot =
-          getCardRowShadowRoot(section.$$('#localCreditCardList'));
-      assertFalse(!!localRowShadowRoot.querySelector('#remoteCreditCardLink'));
-      const menuButton = localRowShadowRoot.querySelector('#creditCardMenu');
-      assertTrue(!!menuButton);
-
-      // Make sure only the two expected options are in the menu.
-      menuButton.click();
-      Polymer.dom.flush();
-      const menu = section.$.creditCardSharedMenu;
-      assertFalse(menu.querySelector('#menuEditCreditCard').hidden);
-      assertFalse(menu.querySelector('#menuRemoveCreditCard').hidden);
-      assertTrue(menu.querySelector('#menuClearCreditCard').hidden);
-      menu.close();
-      Polymer.dom.flush();
-    });
-
-    test('verifyCachedCreditCardMenu_SplitSettings', function() {
-      // Create a full (cached) credit card.
-      const fullServerCard = FakeDataMaker.creditCardEntry();
-      fullServerCard.metadata.isLocal = false;
-      fullServerCard.metadata.isCached = true;
-
-      const section =
-          createSplitPaymentsSection([fullServerCard], [fullServerCard]);
-
-      // Make sure the card is present in the two sections.
-      assertCreditCards(1 /*expectedLocal*/, 1 /*expectedServer*/);
-
-      // Make sure the button in the local section is a menu.
-      const localRowShadowRoot =
-          getCardRowShadowRoot(section.$$('#localCreditCardList'));
-      assertFalse(!!localRowShadowRoot.querySelector('#remoteCreditCardLink'));
-      menuButton = localRowShadowRoot.querySelector('#creditCardMenu');
-      assertTrue(!!menuButton);
-      outlinkButton = localRowShadowRoot.querySelector(
-          'paper-icon-button-light.icon-external');
-      assertFalse(!!outlinkButton);
-
-      // Make sure only the two expected options are in the menu.
-      menuButton.click();
-      Polymer.dom.flush();
-      menu = section.$.creditCardSharedMenu;
-      assertFalse(menu.querySelector('#menuEditCreditCard').hidden);
-      assertTrue(menu.querySelector('#menuRemoveCreditCard').hidden);
-      assertFalse(menu.querySelector('#menuClearCreditCard').hidden);
-      menu.close();
-      Polymer.dom.flush();
-
-      // Make sure the button in the local section is also a menu.
-      const serverRowShadowRoot =
-          getCardRowShadowRoot(section.$$('#serverCreditCardList'));
-      assertFalse(!!serverRowShadowRoot.querySelector('#remoteCreditCardLink'));
-      menuButton = serverRowShadowRoot.querySelector('#creditCardMenu');
-      assertTrue(!!menuButton);
-      outlinkButton = serverRowShadowRoot.querySelector(
-          'paper-icon-button-light.icon-external');
-      assertFalse(!!outlinkButton);
-
-      // Make sure only the two expected options are in the menu.
-      menuButton.click();
-      Polymer.dom.flush();
-      menu = section.$.creditCardSharedMenu;
-      assertFalse(menu.querySelector('#menuEditCreditCard').hidden);
-      assertTrue(menu.querySelector('#menuRemoveCreditCard').hidden);
-      assertFalse(menu.querySelector('#menuClearCreditCard').hidden);
-      menu.close();
-      Polymer.dom.flush();
-    });
-
-    test('verifyNotCachedCreditCardMenu_SplitSettings', function() {
-      // Create a masked (not cached) credit card.
-      const maskedCard = FakeDataMaker.creditCardEntry();
-      maskedCard.metadata.isLocal = false;
-      maskedCard.metadata.isCached = false;
-
-      const section = createSplitPaymentsSection([], [maskedCard]);
-      assertEquals(1, getServerListItems().length);
-
-      // No overflow menu when not cached.
-      const serverRowShadowRoot =
-          getCardRowShadowRoot(section.$$('#serverCreditCardList'));
-      assertTrue(!!serverRowShadowRoot.querySelector('#remoteCreditCardLink'));
-      assertFalse(!!serverRowShadowRoot.querySelector('#creditCardMenu'));
-    });
-
     test('verifyMigrationButtonNotShownIfMigrationNotEnabled', function() {
       // Mock the Google Payments account. Disable the migration experimental
       // flag. Won't show migration button.
diff --git a/chrome/test/data/webui/settings/people_page_sync_controls_test.js b/chrome/test/data/webui/settings/people_page_sync_controls_test.js
index 64a4a3d3..e7f5059 100644
--- a/chrome/test/data/webui/settings/people_page_sync_controls_test.js
+++ b/chrome/test/data/webui/settings/people_page_sync_controls_test.js
@@ -105,6 +105,40 @@
       }
       return browserProxy.whenCalled('setSyncDatatypes').then(verifyPrefs);
     });
+
+    test('SignedIn', function() {
+      // Controls are available by default.
+      assertFalse(syncControls.hidden);
+
+      syncControls
+          .syncStatus = {disabled: false, hasError: false, signedIn: true};
+      // Controls are available when signed in and there is no error.
+      assertFalse(syncControls.hidden);
+    });
+
+    test('SyncDisabled', function() {
+      syncControls
+          .syncStatus = {disabled: true, hasError: false, signedIn: true};
+      // Controls are hidden when sync is disabled.
+      assertTrue(syncControls.hidden);
+    });
+
+    test('SyncError', function() {
+      syncControls
+          .syncStatus = {disabled: false, hasError: true, signedIn: true};
+      // Controls are hidden when there is an error but it's not a
+      // passphrase error.
+      assertTrue(syncControls.hidden);
+
+      syncControls.syncStatus = {
+        disabled: false,
+        hasError: true,
+        signedIn: true,
+        statusAction: settings.StatusAction.ENTER_PASSPHRASE
+      };
+      // Controls are available when there is a passphrase error.
+      assertFalse(syncControls.hidden);
+    });
   });
 
   suite('SyncControlsSubpageTest', function() {
diff --git a/chrome/test/data/webui/welcome/nux_ntp_background_test.js b/chrome/test/data/webui/welcome/nux_ntp_background_test.js
index fafcfe13..6c7f3e7 100644
--- a/chrome/test/data/webui/welcome/nux_ntp_background_test.js
+++ b/chrome/test/data/webui/welcome/nux_ntp_background_test.js
@@ -91,14 +91,6 @@
       });
     });
 
-    test('test disabling and enabling of the next button', function() {
-      const nextButton = testElement.shadowRoot.querySelector('.action-button');
-      assertTrue(nextButton.disabled);
-      testElement.shadowRoot.querySelectorAll('.ntp-background-grid-button')[1]
-          .click();
-      assertFalse(nextButton.disabled);
-    });
-
     test('test activating a background', function() {
       const options = testElement.shadowRoot.querySelectorAll(
           '.ntp-background-grid-button');
@@ -122,5 +114,14 @@
         assertEquals(backgrounds[0].id, id);
       });
     });
+
+    test('test clearing the background when default is selected', function() {
+      // select the default option and hit 'Next'
+      const options = testElement.shadowRoot.querySelectorAll(
+          '.ntp-background-grid-button');
+      options[0].click();
+      testElement.$$('.action-button').click();
+      return testNtpBackgroundProxy.whenCalled('clearBackground');
+    });
   });
 });
diff --git a/chrome/test/data/webui/welcome/test_ntp_background_proxy.js b/chrome/test/data/webui/welcome/test_ntp_background_proxy.js
index 42bce1e2..55353de 100644
--- a/chrome/test/data/webui/welcome/test_ntp_background_proxy.js
+++ b/chrome/test/data/webui/welcome/test_ntp_background_proxy.js
@@ -6,6 +6,7 @@
 class TestNtpBackgroundProxy extends TestBrowserProxy {
   constructor() {
     super([
+      'clearBackground',
       'getBackgrounds',
       'preloadImage',
       'setBackground',
@@ -16,6 +17,11 @@
   }
 
   /** @override */
+  clearBackground() {
+    this.methodCalled('clearBackground');
+  }
+
+  /** @override */
   getBackgrounds() {
     this.methodCalled('getBackgrounds');
     return Promise.resolve(this.backgroundsList_);
diff --git a/chrome/updater/updater.cc b/chrome/updater/updater.cc
index d93c092..481f91f 100644
--- a/chrome/updater/updater.cc
+++ b/chrome/updater/updater.cc
@@ -134,7 +134,8 @@
 
 void InitLogging(const base::CommandLine& command_line) {
   logging::LoggingSettings settings;
-  settings.logging_dest = logging::LOG_TO_SYSTEM_DEBUG_LOG;
+  settings.logging_dest = logging::LOG_TO_ALL;
+  settings.log_file = FILE_PATH_LITERAL("updater.log");
   logging::InitLogging(settings);
   logging::SetLogItems(true,    // enable_process_id
                        true,    // enable_thread_id
diff --git a/chrome/updater/win/net/network.h b/chrome/updater/win/net/network.h
index d6eeb72..6dbd601 100644
--- a/chrome/updater/win/net/network.h
+++ b/chrome/updater/win/net/network.h
@@ -26,6 +26,8 @@
   ~NetworkFetcherFactory() override;
 
  private:
+  static scoped_hinternet CreateSessionHandle();
+
   THREAD_CHECKER(thread_checker_);
   scoped_hinternet session_handle_;
 
diff --git a/chrome/updater/win/net/network_fetcher.cc b/chrome/updater/win/net/network_fetcher.cc
index 676be9c..a9326703a 100644
--- a/chrome/updater/win/net/network_fetcher.cc
+++ b/chrome/updater/win/net/network_fetcher.cc
@@ -11,6 +11,7 @@
 #include "base/callback.h"
 #include "base/callback_helpers.h"
 #include "base/threading/thread_task_runner_handle.h"
+#include "base/win/windows_version.h"
 #include "chrome/updater/win/net/network.h"
 #include "chrome/updater/win/net/network_winhttp.h"
 
@@ -73,13 +74,19 @@
 }
 
 NetworkFetcherFactory::NetworkFetcherFactory()
-    : session_handle_(::WinHttpOpen(L"Chrome Updater",
-                                    WINHTTP_ACCESS_TYPE_NO_PROXY,
-                                    WINHTTP_NO_PROXY_NAME,
-                                    WINHTTP_NO_PROXY_BYPASS,
-                                    WINHTTP_FLAG_ASYNC)) {}
+    : session_handle_(CreateSessionHandle()) {}
 NetworkFetcherFactory::~NetworkFetcherFactory() = default;
 
+scoped_hinternet NetworkFetcherFactory::CreateSessionHandle() {
+  const auto* os_info = base::win::OSInfo::GetInstance();
+  const uint32_t access_type = os_info->version() >= base::win::VERSION_WIN8_1
+                                   ? WINHTTP_ACCESS_TYPE_AUTOMATIC_PROXY
+                                   : WINHTTP_ACCESS_TYPE_NO_PROXY;
+  return scoped_hinternet(
+      ::WinHttpOpen(L"Chrome Updater", access_type, WINHTTP_NO_PROXY_NAME,
+                    WINHTTP_NO_PROXY_BYPASS, WINHTTP_FLAG_ASYNC));
+}
+
 std::unique_ptr<update_client::NetworkFetcher> NetworkFetcherFactory::Create()
     const {
   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
diff --git a/chromecast/base/chromecast_switches.cc b/chromecast/base/chromecast_switches.cc
index c64a04e..4318ded6 100644
--- a/chromecast/base/chromecast_switches.cc
+++ b/chromecast/base/chromecast_switches.cc
@@ -190,6 +190,11 @@
 extern const char kCastMemoryPressureModerateFraction[] =
     "memory-pressure-moderate-fraction";
 
+// Rather than use the renderer hosted remotely in the media service, fall back
+// to the default renderer within content_renderer. Does not change the behavior
+// of the media service.
+const char kDisableMojoRenderer[] = "disable-mojo-renderer";
+
 }  // namespace switches
 
 namespace chromecast {
diff --git a/chromecast/base/chromecast_switches.h b/chromecast/base/chromecast_switches.h
index 40fec18d..9b2d1101 100644
--- a/chromecast/base/chromecast_switches.h
+++ b/chromecast/base/chromecast_switches.h
@@ -91,6 +91,8 @@
 extern const char kCastMemoryPressureCriticalFraction[];
 extern const char kCastMemoryPressureModerateFraction[];
 
+extern const char kDisableMojoRenderer[];
+
 }  // namespace switches
 
 namespace chromecast {
diff --git a/chromecast/browser/DEPS b/chromecast/browser/DEPS
index 759aefc..70336de 100644
--- a/chromecast/browser/DEPS
+++ b/chromecast/browser/DEPS
@@ -44,6 +44,7 @@
   "+services/network/public/cpp",
   "+services/service_manager/public",
   "+services/service_manager/embedder",
+  "+third_party/blink/public/common",
   "+ui/accessibility",
   "+ui/aura",
   "+ui/base",
diff --git a/chromecast/browser/cast_web_contents.h b/chromecast/browser/cast_web_contents.h
index 89f620c..32b2ed4 100644
--- a/chromecast/browser/cast_web_contents.h
+++ b/chromecast/browser/cast_web_contents.h
@@ -86,6 +86,7 @@
   struct InitParams {
     Delegate* delegate;
     bool enabled_for_dev;
+    bool use_cma_renderer;
   };
 
   // Page state for the main frame.
diff --git a/chromecast/browser/cast_web_contents_impl.cc b/chromecast/browser/cast_web_contents_impl.cc
index c89c8a7..41c27e6 100644
--- a/chromecast/browser/cast_web_contents_impl.cc
+++ b/chromecast/browser/cast_web_contents_impl.cc
@@ -8,8 +8,10 @@
 
 #include "base/bind.h"
 #include "base/threading/sequenced_task_runner_handle.h"
+#include "chromecast/base/chromecast_switches.h"
 #include "chromecast/browser/cast_browser_process.h"
 #include "chromecast/browser/devtools/remote_debugging_server.h"
+#include "chromecast/common/mojom/media_playback_options.mojom.h"
 #include "content/public/browser/navigation_entry.h"
 #include "content/public/browser/navigation_handle.h"
 #include "content/public/browser/render_frame_host.h"
@@ -19,6 +21,7 @@
 #include "content/public/common/bindings_policy.h"
 #include "net/base/net_errors.h"
 #include "services/service_manager/public/cpp/interface_provider.h"
+#include "third_party/blink/public/common/associated_interfaces/associated_interface_provider.h"
 #include "url/gurl.h"
 
 namespace chromecast {
@@ -28,7 +31,9 @@
     : web_contents_(web_contents),
       delegate_(init_params.delegate),
       page_state_(PageState::IDLE),
+      last_state_(PageState::IDLE),
       enabled_for_dev_(init_params.enabled_for_dev),
+      use_cma_renderer_(init_params.use_cma_renderer),
       remote_debugging_server_(
           shell::CastBrowserProcess::GetInstance()->remote_debugging_server()),
       closing_(false),
@@ -46,12 +51,19 @@
     LOG(INFO) << "Enabling dev console for CastWebContentsImpl";
     remote_debugging_server_->EnableWebContentsForDebugging(web_contents_);
   }
+
+  // TODO(yucliu): Change the flag name to kDisableCmaRenderer in a latter diff.
+  if (GetSwitchValueBoolean(switches::kDisableMojoRenderer, false)) {
+    use_cma_renderer_ = false;
+  }
 }
 
 CastWebContentsImpl::~CastWebContentsImpl() {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  DisableDebugging();
+  DCHECK(!notifying_) << "Do not destroy CastWebContents during observer "
+                         "notification!";
 
+  DisableDebugging();
   for (auto& observer : observer_list_) {
     observer.ResetCastWebContents();
   }
@@ -95,6 +107,7 @@
                                          ui::PAGE_TRANSITION_TYPED, "");
   UpdatePageState();
   DCHECK_EQ(PageState::LOADING, page_state_);
+  NotifyObservers();
 }
 
 void CastWebContentsImpl::ClosePage() {
@@ -118,6 +131,7 @@
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   if (stopped_) {
     UpdatePageState();
+    NotifyObservers();
     return;
   }
   last_error_ = error_code;
@@ -127,6 +141,7 @@
   DCHECK_NE(PageState::IDLE, page_state_);
   DCHECK_NE(PageState::LOADING, page_state_);
   DCHECK_NE(PageState::LOADED, page_state_);
+  NotifyObservers();
 }
 
 void CastWebContentsImpl::SetDelegate(CastWebContents::Delegate* delegate) {
@@ -208,6 +223,12 @@
   chromecast::shell::mojom::FeatureManagerPtr feature_manager_ptr;
   render_frame_host->GetRemoteInterfaces()->GetInterface(&feature_manager_ptr);
   feature_manager_ptr->ConfigureFeatures(GetRendererFeatures());
+
+  chromecast::shell::mojom::MediaPlaybackOptionsAssociatedPtr
+      media_playback_options;
+  render_frame_host->GetRemoteAssociatedInterfaces()->GetInterface(
+      &media_playback_options);
+  media_playback_options->SetUseCmaRenderer(use_cma_renderer_);
 }
 
 std::vector<chromecast::shell::mojom::FeaturePtr>
@@ -286,6 +307,7 @@
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   UpdatePageState();
   DCHECK_EQ(page_state_, PageState::LOADING);
+  NotifyObservers();
 }
 
 void CastWebContentsImpl::DidStopLoading() {
@@ -317,11 +339,12 @@
   DCHECK((previous == PageState::ERROR && page_state_ == PageState::ERROR) ||
          page_state_ == PageState::LOADED)
       << "Page is in unexpected state: " << page_state_;
+  NotifyObservers();
 }
 
 void CastWebContentsImpl::UpdatePageState() {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  PageState last_state = page_state_;
+  last_state_ = page_state_;
   if (!web_contents_) {
     DCHECK(stopped_);
     page_state_ = PageState::DESTROYED;
@@ -338,11 +361,13 @@
       page_state_ = PageState::CLOSED;
     }
   }
+}
 
+void CastWebContentsImpl::NotifyObservers() {
   if (!delegate_)
     return;
   // Don't notify if the page state didn't change.
-  if (last_state == page_state_)
+  if (last_state_ == page_state_)
     return;
   // Don't recursively notify the delegate.
   if (notifying_)
diff --git a/chromecast/browser/cast_web_contents_impl.h b/chromecast/browser/cast_web_contents_impl.h
index ae43da2..68fb0a7 100644
--- a/chromecast/browser/cast_web_contents_impl.h
+++ b/chromecast/browser/cast_web_contents_impl.h
@@ -77,6 +77,7 @@
   void WebContentsDestroyed() override;
 
   void UpdatePageState();
+  void NotifyObservers();
   void TracePageLoadBegin(const GURL& url);
   void TracePageLoadEnd(const GURL& url);
   void DisableDebugging();
@@ -86,7 +87,9 @@
   content::WebContents* web_contents_;
   Delegate* delegate_;
   PageState page_state_;
+  PageState last_state_;
   const bool enabled_for_dev_;
+  bool use_cma_renderer_;
   shell::RemoteDebuggingServer* const remote_debugging_server_;
 
   base::flat_set<std::unique_ptr<CastWebContents>> inner_contents_;
diff --git a/chromecast/browser/cast_web_view.h b/chromecast/browser/cast_web_view.h
index ef1139d..6567cd7 100644
--- a/chromecast/browser/cast_web_view.h
+++ b/chromecast/browser/cast_web_view.h
@@ -80,6 +80,9 @@
     // Whether this CastWebView is granted media access.
     bool allow_media_access = false;
 
+    // Whether this CastWebView will use CMA for media playback.
+    bool use_cma_renderer = true;
+
     // Enable development mode for this CastWebView. Whitelists certain
     // functionality for the WebContents, like remote debugging and debugging
     // interfaces.
diff --git a/chromecast/browser/cast_web_view_default.cc b/chromecast/browser/cast_web_view_default.cc
index da5b8639..f13b591 100644
--- a/chromecast/browser/cast_web_view_default.cc
+++ b/chromecast/browser/cast_web_view_default.cc
@@ -63,8 +63,9 @@
       transparent_(params.transparent),
       allow_media_access_(params.allow_media_access),
       web_contents_(CreateWebContents(browser_context_, site_instance_)),
-      cast_web_contents_(web_contents_.get(),
-                         {delegate_, params.enabled_for_dev}),
+      cast_web_contents_(
+          web_contents_.get(),
+          {delegate_, params.enabled_for_dev, params.use_cma_renderer}),
       window_(shell::CastContentWindow::Create(params.window_params)),
       resize_window_when_navigation_starts_(true) {
   DCHECK(delegate_);
diff --git a/chromecast/browser/cast_web_view_extension.cc b/chromecast/browser/cast_web_view_extension.cc
index 6f5a53f..2411c05 100644
--- a/chromecast/browser/cast_web_view_extension.cc
+++ b/chromecast/browser/cast_web_view_extension.cc
@@ -30,8 +30,9 @@
           initial_url,
           site_instance.get(),
           extensions::VIEW_TYPE_EXTENSION_POPUP)),
-      cast_web_contents_(extension_host_->host_contents(),
-                         {delegate_, params.enabled_for_dev}) {
+      cast_web_contents_(
+          extension_host_->host_contents(),
+          {delegate_, params.enabled_for_dev, params.use_cma_renderer}) {
   DCHECK(delegate_);
   content::WebContentsObserver::Observe(web_contents());
   web_contents()->GetNativeView()->SetName(params.activity_id);
diff --git a/chromecast/browser/url_request_context_factory.cc b/chromecast/browser/url_request_context_factory.cc
index cec2cc2..a8ff986 100644
--- a/chromecast/browser/url_request_context_factory.cc
+++ b/chromecast/browser/url_request_context_factory.cc
@@ -235,8 +235,7 @@
   cert_transparency_verifier_.reset(new net::MultiLogCTVerifier());
   ct_policy_enforcer_.reset(new net::DefaultCTPolicyEnforcer());
 
-  http_auth_handler_factory_ =
-      net::HttpAuthHandlerFactory::CreateDefault(host_resolver_.get());
+  http_auth_handler_factory_ = net::HttpAuthHandlerFactory::CreateDefault();
 
   // Use in-memory HttpServerProperties. Disk-based can improve performance
   // but benefit seems small (only helps 1st request to a server).
diff --git a/chromecast/common/mojom/media_playback_options.mojom b/chromecast/common/mojom/media_playback_options.mojom
index 9a8fbaad..3de812017 100644
--- a/chromecast/common/mojom/media_playback_options.mojom
+++ b/chromecast/common/mojom/media_playback_options.mojom
@@ -11,4 +11,10 @@
   SetMediaLoadingBlocked(bool blocked);
 
   SetBackgroundVideoPlaybackEnabled(bool enabled);
-};
\ No newline at end of file
+
+  // Enable CMA (MojoRenderer) for media playback.
+  // Otherwise, media playback uses RendererImpl, which is the same as other
+  // platforms. For video codec supported by v4l2 (e.g. h264), it's still
+  // accelerated by hardware. Video will be rendererd on graphics plane.
+  SetUseCmaRenderer(bool enabled);
+};
diff --git a/chromecast/media/audio/cast_audio_output_stream.cc b/chromecast/media/audio/cast_audio_output_stream.cc
index eac009c..599c64b8 100644
--- a/chromecast/media/audio/cast_audio_output_stream.cc
+++ b/chromecast/media/audio/cast_audio_output_stream.cc
@@ -5,6 +5,7 @@
 #include "chromecast/media/audio/cast_audio_output_stream.h"
 
 #include <algorithm>
+#include <limits>
 #include <string>
 #include <utility>
 
@@ -54,8 +55,6 @@
 constexpr base::TimeDelta kFadeTime = base::TimeDelta::FromMilliseconds(5);
 constexpr base::TimeDelta kMixerStartThreshold =
     base::TimeDelta::FromMilliseconds(60);
-constexpr base::TimeDelta kMixerBufferSizeInTime =
-    base::TimeDelta::FromMilliseconds(25);
 }  // namespace
 
 namespace chromecast {
@@ -466,10 +465,7 @@
       kMixerStartThreshold, audio_params_.sample_rate());
   params.set_start_threshold_frames(start_threshold_frames);
 
-  int32_t fill_size_frames = ::media::AudioTimestampHelper::TimeToFrames(
-      kMixerBufferSizeInTime, audio_params_.sample_rate());
-
-  params.set_fill_size_frames(fill_size_frames);
+  params.set_fill_size_frames(audio_params_.frames_per_buffer());
   params.set_use_fader(true);
   params.set_fade_frames(::media::AudioTimestampHelper::TimeToFrames(
       kFadeTime, audio_params_.sample_rate()));
diff --git a/chromecast/renderer/cast_media_playback_options.cc b/chromecast/renderer/cast_media_playback_options.cc
index 1e34bfe..a72b938 100644
--- a/chromecast/renderer/cast_media_playback_options.cc
+++ b/chromecast/renderer/cast_media_playback_options.cc
@@ -79,6 +79,12 @@
       renderer_media_playback_options_);
 }
 
+void CastMediaPlaybackOptions::SetUseCmaRenderer(bool enable) {
+  renderer_media_playback_options_.is_mojo_renderer_enabled = enable;
+  render_frame()->SetRenderFrameMediaPlaybackOptions(
+      renderer_media_playback_options_);
+}
+
 void CastMediaPlaybackOptions::OnMediaPlaybackOptionsAssociatedRequest(
     chromecast::shell::mojom::MediaPlaybackOptionsAssociatedRequest request) {
   bindings_.AddBinding(this, std::move(request));
diff --git a/chromecast/renderer/cast_media_playback_options.h b/chromecast/renderer/cast_media_playback_options.h
index 4ef099d9..5cc7b66 100644
--- a/chromecast/renderer/cast_media_playback_options.h
+++ b/chromecast/renderer/cast_media_playback_options.h
@@ -48,6 +48,7 @@
   // MediaPlaybackOptions implementation
   void SetMediaLoadingBlocked(bool blocked) override;
   void SetBackgroundVideoPlaybackEnabled(bool enabled) override;
+  void SetUseCmaRenderer(bool enable) override;
 
   void OnMediaPlaybackOptionsAssociatedRequest(
       chromecast::shell::mojom::MediaPlaybackOptionsAssociatedRequest request);
diff --git a/chromeos/BUILD.gn b/chromeos/BUILD.gn
index 7279b61..cf975ad0 100644
--- a/chromeos/BUILD.gn
+++ b/chromeos/BUILD.gn
@@ -26,7 +26,7 @@
     "//build/config/linux/nss:system_nss_no_ssl_config",
   ]
   public_deps = [
-    ":chromeos_constants",
+    "//chromeos/constants",
   ]
   deps = [
     ":account_manager_proto",
@@ -34,6 +34,7 @@
     "//base",
     "//base:i18n",
     "//chromeos/dbus",
+    "//chromeos/dbus/constants",
     "//components/policy/proto",
     "//google_apis",
     "//services/device/public/mojom",
@@ -107,31 +108,6 @@
   ]
 }
 
-source_set("chromeos_constants") {
-  configs += [ ":chromeos_implementation" ]
-  deps = [
-    ":chromeos_export",
-    "//base",
-    "//base:i18n",
-    "//chromeos/dbus:constants",
-    "//third_party/icu",
-  ]
-  sources = [
-    "constants/chromeos_constants.cc",
-    "constants/chromeos_constants.h",
-    "constants/chromeos_features.cc",
-    "constants/chromeos_features.h",
-    "constants/chromeos_paths.cc",
-    "constants/chromeos_paths.h",
-    "constants/chromeos_pref_names.cc",
-    "constants/chromeos_pref_names.h",
-    "constants/chromeos_switches.cc",
-    "constants/chromeos_switches.h",
-    "constants/devicetype.cc",
-    "constants/devicetype.h",
-  ]
-}
-
 # This must be a static library instead of a source set because some of the
 # files pull in things with dependencies that aren't linked in all cases.
 #
diff --git a/chromeos/attestation/BUILD.gn b/chromeos/attestation/BUILD.gn
index ad87f91..26c48e2 100644
--- a/chromeos/attestation/BUILD.gn
+++ b/chromeos/attestation/BUILD.gn
@@ -10,7 +10,6 @@
   defines = [ "IS_CHROMEOS_ATTESTATION_IMPL" ]
   deps = [
     "//base",
-    "//chromeos:chromeos_constants",
     "//chromeos/cryptohome",
     "//chromeos/dbus",
     "//chromeos/dbus:cryptohome_proto",
diff --git a/chromeos/audio/BUILD.gn b/chromeos/audio/BUILD.gn
index ba05a60d..fb3d38a 100644
--- a/chromeos/audio/BUILD.gn
+++ b/chromeos/audio/BUILD.gn
@@ -10,7 +10,7 @@
   defines = [ "IS_CHROMEOS_AUDIO_IMPL" ]
   deps = [
     "//base",
-    "//chromeos:chromeos_constants",
+    "//chromeos/constants",
     "//chromeos/dbus",
     "//components/prefs",
     "//media/base:video_facing",
@@ -35,7 +35,7 @@
   deps = [
     ":audio",
     "//base/test:test_support",
-    "//chromeos:chromeos_constants",
+    "//chromeos/constants",
     "//chromeos/dbus:test_support",
     "//components/prefs:test_support",
     "//media/base:video_facing",
diff --git a/chromeos/components/multidevice/debug_webui/BUILD.gn b/chromeos/components/multidevice/debug_webui/BUILD.gn
index 270b04a0..79b3465d 100644
--- a/chromeos/components/multidevice/debug_webui/BUILD.gn
+++ b/chromeos/components/multidevice/debug_webui/BUILD.gn
@@ -17,9 +17,9 @@
   deps = [
     "//base",
     "//base:i18n",
-    "//chromeos:chromeos_constants",
     "//chromeos/components/multidevice/logging",
     "//chromeos/components/proximity_auth",
+    "//chromeos/constants",
     "//chromeos/resources",
     "//chromeos/services/device_sync/proto",
     "//chromeos/services/device_sync/proto:util",
diff --git a/chromeos/components/proximity_auth/BUILD.gn b/chromeos/components/proximity_auth/BUILD.gn
index 1dcbe382..963f465 100644
--- a/chromeos/components/proximity_auth/BUILD.gn
+++ b/chromeos/components/proximity_auth/BUILD.gn
@@ -51,10 +51,10 @@
 
   deps = [
     "//base",
-    "//chromeos:chromeos_constants",
     "//chromeos/components/multidevice",
     "//chromeos/components/multidevice/logging",
     "//chromeos/components/proximity_auth/public/interfaces",
+    "//chromeos/constants",
     "//chromeos/dbus",
     "//chromeos/services/multidevice_setup/public/cpp",
     "//chromeos/services/multidevice_setup/public/cpp:prefs",
@@ -116,10 +116,10 @@
     ":test_support",
     "//base",
     "//base/test:test_support",
-    "//chromeos:chromeos_constants",
     "//chromeos/components/multidevice",
     "//chromeos/components/multidevice:test_support",
     "//chromeos/components/multidevice/logging",
+    "//chromeos/constants",
     "//chromeos/dbus:test_support",
     "//chromeos/services/multidevice_setup/public/cpp:prefs",
     "//chromeos/services/multidevice_setup/public/cpp:test_support",
diff --git a/chromeos/components/tether/BUILD.gn b/chromeos/components/tether/BUILD.gn
index bfbb4f3..8b9e355 100644
--- a/chromeos/components/tether/BUILD.gn
+++ b/chromeos/components/tether/BUILD.gn
@@ -116,9 +116,9 @@
 
   deps = [
     "//base",
-    "//chromeos:chromeos_constants",
     "//chromeos/components/multidevice/logging",
     "//chromeos/components/tether/proto",
+    "//chromeos/constants",
     "//chromeos/dbus",
     "//chromeos/login/login_state",
     "//chromeos/network",
@@ -202,10 +202,10 @@
   deps = [
     "//base",
     "//base/test:test_support",
-    "//chromeos:chromeos_constants",
     "//chromeos/components/multidevice",
     "//chromeos/components/multidevice:test_support",
     "//chromeos/components/tether/proto",
+    "//chromeos/constants",
     "//chromeos/network:test_support",
     "//chromeos/services/device_sync/public/cpp:test_support",
     "//chromeos/services/secure_channel/public/cpp/client:test_support",
@@ -264,10 +264,10 @@
     ":test_support",
     ":tether",
     "//base/test:test_support",
-    "//chromeos:chromeos_constants",
     "//chromeos/components/multidevice",
     "//chromeos/components/multidevice:test_support",
     "//chromeos/components/tether/proto",
+    "//chromeos/constants",
     "//chromeos/dbus:test_support",
     "//chromeos/login/login_state",
     "//chromeos/network:test_support",
diff --git a/chromeos/constants/BUILD.gn b/chromeos/constants/BUILD.gn
new file mode 100644
index 0000000..8862199
--- /dev/null
+++ b/chromeos/constants/BUILD.gn
@@ -0,0 +1,30 @@
+# Copyright 2019 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+assert(is_chromeos, "Non-Chrome-OS builds must not depend on //chromeos")
+
+component("constants") {
+  output_name = "chromeos_constants"
+  defines = [ "IS_CHROMEOS_CONSTANTS_IMPL" ]
+  deps = [
+    "//base",
+    "//base:i18n",
+    "//chromeos/dbus/constants",
+    "//third_party/icu",
+  ]
+  sources = [
+    "chromeos_constants.cc",
+    "chromeos_constants.h",
+    "chromeos_features.cc",
+    "chromeos_features.h",
+    "chromeos_paths.cc",
+    "chromeos_paths.h",
+    "chromeos_pref_names.cc",
+    "chromeos_pref_names.h",
+    "chromeos_switches.cc",
+    "chromeos_switches.h",
+    "devicetype.cc",
+    "devicetype.h",
+  ]
+}
diff --git a/chromeos/constants/chromeos_constants.h b/chromeos/constants/chromeos_constants.h
index 07aaf55d..7f07aa9 100644
--- a/chromeos/constants/chromeos_constants.h
+++ b/chromeos/constants/chromeos_constants.h
@@ -7,15 +7,19 @@
 #ifndef CHROMEOS_CONSTANTS_CHROMEOS_CONSTANTS_H_
 #define CHROMEOS_CONSTANTS_CHROMEOS_CONSTANTS_H_
 
+#include "base/component_export.h"
 #include "base/files/file_path.h"
-#include "chromeos/chromeos_export.h"
 
 namespace chromeos {
 
-CHROMEOS_EXPORT extern const base::FilePath::CharType kDriveCacheDirname[];
-CHROMEOS_EXPORT extern const base::FilePath::CharType kNssCertDbPath[];
-CHROMEOS_EXPORT extern const base::FilePath::CharType kNssDirPath[];
-CHROMEOS_EXPORT extern const base::FilePath::CharType kNssKeyDbPath[];
+COMPONENT_EXPORT(CHROMEOS_CONSTANTS)
+extern const base::FilePath::CharType kDriveCacheDirname[];
+COMPONENT_EXPORT(CHROMEOS_CONSTANTS)
+extern const base::FilePath::CharType kNssCertDbPath[];
+COMPONENT_EXPORT(CHROMEOS_CONSTANTS)
+extern const base::FilePath::CharType kNssDirPath[];
+COMPONENT_EXPORT(CHROMEOS_CONSTANTS)
+extern const base::FilePath::CharType kNssKeyDbPath[];
 
 }  // namespace chromeos
 
diff --git a/chromeos/constants/chromeos_features.h b/chromeos/constants/chromeos_features.h
index 76c5d22..09f0bc5 100644
--- a/chromeos/constants/chromeos_features.h
+++ b/chromeos/constants/chromeos_features.h
@@ -5,8 +5,8 @@
 #ifndef CHROMEOS_CONSTANTS_CHROMEOS_FEATURES_H_
 #define CHROMEOS_CONSTANTS_CHROMEOS_FEATURES_H_
 
+#include "base/component_export.h"
 #include "base/feature_list.h"
-#include "chromeos/chromeos_export.h"
 
 namespace chromeos {
 
@@ -15,24 +15,36 @@
 // All features in alphabetical order. The features should be documented
 // alongside the definition of their values in the .cc file.
 
-CHROMEOS_EXPORT extern const base::Feature kAutoScreenBrightness;
-CHROMEOS_EXPORT extern const base::Feature kCrostiniBackup;
-CHROMEOS_EXPORT extern const base::Feature kCrostiniUsbSupport;
-CHROMEOS_EXPORT extern const base::Feature kCryptAuthV2Enrollment;
-CHROMEOS_EXPORT extern const base::Feature kDiscoverApp;
-CHROMEOS_EXPORT extern const base::Feature kDriveFs;
-CHROMEOS_EXPORT extern const base::Feature kEnableMessagesWebPush;
-CHROMEOS_EXPORT extern const base::Feature kMyFilesVolume;
-CHROMEOS_EXPORT extern const base::Feature kEnableSupervisionTransitionScreens;
-CHROMEOS_EXPORT extern const base::Feature kFsNosymfollow;
-CHROMEOS_EXPORT extern const base::Feature kImeInputLogic;
-CHROMEOS_EXPORT extern const base::Feature kImeServiceConnectable;
-CHROMEOS_EXPORT extern const base::Feature kInstantTethering;
-CHROMEOS_EXPORT extern const base::Feature kVideoPlayerNativeControls;
-CHROMEOS_EXPORT extern const base::Feature kUseMessagesGoogleComDomain;
-CHROMEOS_EXPORT extern const base::Feature kUseMessagesStagingUrl;
-CHROMEOS_EXPORT extern const base::Feature kUserActivityPrediction;
-CHROMEOS_EXPORT extern const base::Feature kUserActivityPredictionMlService;
+COMPONENT_EXPORT(CHROMEOS_CONSTANTS)
+extern const base::Feature kAutoScreenBrightness;
+COMPONENT_EXPORT(CHROMEOS_CONSTANTS) extern const base::Feature kCrostiniBackup;
+COMPONENT_EXPORT(CHROMEOS_CONSTANTS)
+extern const base::Feature kCrostiniUsbSupport;
+COMPONENT_EXPORT(CHROMEOS_CONSTANTS)
+extern const base::Feature kCryptAuthV2Enrollment;
+COMPONENT_EXPORT(CHROMEOS_CONSTANTS) extern const base::Feature kDiscoverApp;
+COMPONENT_EXPORT(CHROMEOS_CONSTANTS) extern const base::Feature kDriveFs;
+COMPONENT_EXPORT(CHROMEOS_CONSTANTS)
+extern const base::Feature kEnableMessagesWebPush;
+COMPONENT_EXPORT(CHROMEOS_CONSTANTS) extern const base::Feature kMyFilesVolume;
+COMPONENT_EXPORT(CHROMEOS_CONSTANTS)
+extern const base::Feature kEnableSupervisionTransitionScreens;
+COMPONENT_EXPORT(CHROMEOS_CONSTANTS) extern const base::Feature kFsNosymfollow;
+COMPONENT_EXPORT(CHROMEOS_CONSTANTS) extern const base::Feature kImeInputLogic;
+COMPONENT_EXPORT(CHROMEOS_CONSTANTS)
+extern const base::Feature kImeServiceConnectable;
+COMPONENT_EXPORT(CHROMEOS_CONSTANTS)
+extern const base::Feature kInstantTethering;
+COMPONENT_EXPORT(CHROMEOS_CONSTANTS)
+extern const base::Feature kVideoPlayerNativeControls;
+COMPONENT_EXPORT(CHROMEOS_CONSTANTS)
+extern const base::Feature kUseMessagesGoogleComDomain;
+COMPONENT_EXPORT(CHROMEOS_CONSTANTS)
+extern const base::Feature kUseMessagesStagingUrl;
+COMPONENT_EXPORT(CHROMEOS_CONSTANTS)
+extern const base::Feature kUserActivityPrediction;
+COMPONENT_EXPORT(CHROMEOS_CONSTANTS)
+extern const base::Feature kUserActivityPredictionMlService;
 
 }  // namespace features
 
diff --git a/chromeos/constants/chromeos_paths.h b/chromeos/constants/chromeos_paths.h
index 3045185..0bf26b08 100644
--- a/chromeos/constants/chromeos_paths.h
+++ b/chromeos/constants/chromeos_paths.h
@@ -5,7 +5,7 @@
 #ifndef CHROMEOS_CONSTANTS_CHROMEOS_PATHS_H_
 #define CHROMEOS_CONSTANTS_CHROMEOS_PATHS_H_
 
-#include "chromeos/chromeos_export.h"
+#include "base/component_export.h"
 
 namespace base {
 class FilePath;
@@ -19,12 +19,12 @@
 enum {
   PATH_START = 7000,
 
-  FILE_DEFAULT_APP_ORDER,   // Full path to the json file that defines the
-                            // default app order.
-  FILE_MACHINE_INFO,        // Full path to machine hardware info file.
-  FILE_VPD,                 // Full path to VPD file.
-  FILE_UPTIME,              // Full path to the file via which the kernel
-                            // exposes the current device uptime.
+  FILE_DEFAULT_APP_ORDER,  // Full path to the json file that defines the
+                           // default app order.
+  FILE_MACHINE_INFO,       // Full path to machine hardware info file.
+  FILE_VPD,                // Full path to VPD file.
+  FILE_UPTIME,             // Full path to the file via which the kernel
+                           // exposes the current device uptime.
   FILE_UPDATE_REBOOT_NEEDED_UPTIME,  // Full path to a file in which Chrome can
                                      // store the uptime at which an update
                                      // became necessary. The file should be
@@ -57,12 +57,13 @@
 };
 
 // Call once to register the provider for the path keys defined above.
-CHROMEOS_EXPORT void RegisterPathProvider();
+COMPONENT_EXPORT(CHROMEOS_CONSTANTS) void RegisterPathProvider();
 
 // Overrides some of the paths listed above so that those files can be used
 // when not running on ChromeOS. The stubs files will be relative to
 // |stubs_dir|. It is not valid to call this when running on ChromeOS.
-CHROMEOS_EXPORT void RegisterStubPathOverrides(const base::FilePath& stubs_dir);
+COMPONENT_EXPORT(CHROMEOS_CONSTANTS)
+void RegisterStubPathOverrides(const base::FilePath& stubs_dir);
 
 }  // namespace chromeos
 
diff --git a/chromeos/constants/chromeos_pref_names.h b/chromeos/constants/chromeos_pref_names.h
index a3a6abcb..181ea3f 100644
--- a/chromeos/constants/chromeos_pref_names.h
+++ b/chromeos/constants/chromeos_pref_names.h
@@ -5,19 +5,22 @@
 #ifndef CHROMEOS_CONSTANTS_CHROMEOS_PREF_NAMES_H_
 #define CHROMEOS_CONSTANTS_CHROMEOS_PREF_NAMES_H_
 
-#include "chromeos/chromeos_export.h"
+#include "base/component_export.h"
 
 namespace chromeos {
 namespace prefs {
 
-CHROMEOS_EXPORT extern const char kAudioDevicesMute[];
-CHROMEOS_EXPORT extern const char kAudioDevicesVolumePercent[];
-CHROMEOS_EXPORT extern const char kAudioMute[];
-CHROMEOS_EXPORT extern const char kAudioOutputAllowed[];
-CHROMEOS_EXPORT extern const char kAudioVolumePercent[];
-CHROMEOS_EXPORT extern const char kAudioDevicesState[];
-CHROMEOS_EXPORT extern const char kQuirksClientLastServerCheck[];
-CHROMEOS_EXPORT extern const char kDeviceWiFiFastTransitionEnabled[];
+COMPONENT_EXPORT(CHROMEOS_CONSTANTS) extern const char kAudioDevicesMute[];
+COMPONENT_EXPORT(CHROMEOS_CONSTANTS)
+extern const char kAudioDevicesVolumePercent[];
+COMPONENT_EXPORT(CHROMEOS_CONSTANTS) extern const char kAudioMute[];
+COMPONENT_EXPORT(CHROMEOS_CONSTANTS) extern const char kAudioOutputAllowed[];
+COMPONENT_EXPORT(CHROMEOS_CONSTANTS) extern const char kAudioVolumePercent[];
+COMPONENT_EXPORT(CHROMEOS_CONSTANTS) extern const char kAudioDevicesState[];
+COMPONENT_EXPORT(CHROMEOS_CONSTANTS)
+extern const char kQuirksClientLastServerCheck[];
+COMPONENT_EXPORT(CHROMEOS_CONSTANTS)
+extern const char kDeviceWiFiFastTransitionEnabled[];
 
 }  // namespace prefs
 }  // namespace chromeos
diff --git a/chromeos/constants/chromeos_switches.cc b/chromeos/constants/chromeos_switches.cc
index 07dd60f..2fee3ea 100644
--- a/chromeos/constants/chromeos_switches.cc
+++ b/chromeos/constants/chromeos_switches.cc
@@ -50,10 +50,10 @@
                                         base::FEATURE_DISABLED_BY_DEFAULT};
 
 const base::Feature kShowSplashScreenInDemoMode{
-    "ShowSplashScreenInDemoMode", base::FEATURE_DISABLED_BY_DEFAULT};
+    "ShowSplashScreenInDemoMode", base::FEATURE_ENABLED_BY_DEFAULT};
 
 const base::Feature kSupportCountryCustomizationInDemoMode{
-    "SupportCountryCustomizationInDemoMode", base::FEATURE_DISABLED_BY_DEFAULT};
+    "SupportCountryCustomizationInDemoMode", base::FEATURE_ENABLED_BY_DEFAULT};
 
 // Please keep the order of these switches synchronized with the header file
 // (i.e. in alphabetical order).
diff --git a/chromeos/constants/chromeos_switches.h b/chromeos/constants/chromeos_switches.h
index 204021e..2169183 100644
--- a/chromeos/constants/chromeos_switches.h
+++ b/chromeos/constants/chromeos_switches.h
@@ -5,9 +5,9 @@
 #ifndef CHROMEOS_CONSTANTS_CHROMEOS_SWITCHES_H_
 #define CHROMEOS_CONSTANTS_CHROMEOS_SWITCHES_H_
 
+#include "base/component_export.h"
 #include "base/feature_list.h"
 #include "base/memory/memory_pressure_monitor_chromeos.h"
-#include "chromeos/chromeos_export.h"
 #include "chromeos/dbus/constants/dbus_switches.h"
 
 namespace chromeos {
@@ -23,207 +23,275 @@
 // see chromeos::LoginUtil::GetOffTheRecordCommandLine().)
 
 // Please keep alphabetized.
-CHROMEOS_EXPORT extern const char kAggressiveCacheDiscardThreshold[];
-CHROMEOS_EXPORT extern const char kAggressiveTabDiscardThreshold[];
-CHROMEOS_EXPORT extern const char kAggressiveThreshold[];
-CHROMEOS_EXPORT extern const char kAllowFailedPolicyFetchForTest[];
-CHROMEOS_EXPORT extern const char kAllowRAInDevMode[];
-CHROMEOS_EXPORT extern const char kAppAutoLaunched[];
-CHROMEOS_EXPORT extern const char kAppOemManifestFile[];
-CHROMEOS_EXPORT extern const char kArcAvailability[];
-CHROMEOS_EXPORT extern const char kArcAvailable[];
-CHROMEOS_EXPORT extern const char kArcDataCleanupOnStart[];
-CHROMEOS_EXPORT extern const char kArcDisableAppSync[];
-CHROMEOS_EXPORT extern const char kArcDisableLocaleSync[];
-CHROMEOS_EXPORT extern const char kArcDisablePlayAutoInstall[];
-CHROMEOS_EXPORT extern const char kArcForceCacheAppIcons[];
-CHROMEOS_EXPORT extern const char kArcForceShowOptInUi[];
-CHROMEOS_EXPORT extern const char kArcPackagesCacheMode[];
-CHROMEOS_EXPORT extern const char kArcPlayStoreAutoUpdate[];
-CHROMEOS_EXPORT extern const char kArcStartMode[];
-CHROMEOS_EXPORT extern const char kArcTransitionMigrationRequired[];
-CHROMEOS_EXPORT extern const char kArtifactsDir[];
-CHROMEOS_EXPORT extern const char kCellularFirst[];
-CHROMEOS_EXPORT extern const char kChildWallpaperLarge[];
-CHROMEOS_EXPORT extern const char kChildWallpaperSmall[];
-CHROMEOS_EXPORT extern const char kConservativeThreshold[];
-CHROMEOS_EXPORT extern const char kCrosGaiaApiV1[];
-CHROMEOS_EXPORT extern const char kCrosRegion[];
-CHROMEOS_EXPORT extern const char kCrosRegionsMode[];
-CHROMEOS_EXPORT extern const char kCrosRegionsModeHide[];
-CHROMEOS_EXPORT extern const char kCrosRegionsModeOverride[];
-CHROMEOS_EXPORT extern const char kDefaultWallpaperIsOem[];
-CHROMEOS_EXPORT extern const char kDefaultWallpaperLarge[];
-CHROMEOS_EXPORT extern const char kDefaultWallpaperSmall[];
-CHROMEOS_EXPORT extern const char kDerelictDetectionTimeout[];
-CHROMEOS_EXPORT extern const char kDerelictIdleTimeout[];
-CHROMEOS_EXPORT extern const char kDisableArcDataWipe[];
-CHROMEOS_EXPORT extern const char kDisableArcOptInVerification[];
-CHROMEOS_EXPORT extern const char kDisableCaptivePortalBypassProxy[];
-CHROMEOS_EXPORT extern const char kDisableCloudImport[];
-CHROMEOS_EXPORT extern const char kDisableDemoMode[];
-CHROMEOS_EXPORT extern const char kDisableDeviceDisabling[];
-CHROMEOS_EXPORT extern const char kDisableEncryptionMigration[];
-CHROMEOS_EXPORT extern const char kDisableEolNotification[];
-CHROMEOS_EXPORT extern const char kDisableFileManagerTouchMode[];
-CHROMEOS_EXPORT extern const char kDisableFineGrainedTimeZoneDetection[];
-CHROMEOS_EXPORT extern const char kDisableGaiaServices[];
-CHROMEOS_EXPORT extern const char kDisableHIDDetectionOnOOBE[];
-CHROMEOS_EXPORT extern const char kDisableLoginAnimations[];
-CHROMEOS_EXPORT extern const char kDisableMachineCertRequest[];
-CHROMEOS_EXPORT extern const char kDisableMtpWriteSupport[];
-CHROMEOS_EXPORT extern const char kDisableMultiDisplayLayout[];
-CHROMEOS_EXPORT extern const char kDisableNewZIPUnpacker[];
-CHROMEOS_EXPORT extern const char kDisableOfficeEditingComponentApp[];
-CHROMEOS_EXPORT extern const char kDisablePerUserTimezone[];
-CHROMEOS_EXPORT extern const char kDisablePhysicalKeyboardAutocorrect[];
-CHROMEOS_EXPORT extern const char kDisableRollbackOption[];
-CHROMEOS_EXPORT extern const char kDisableSigninFrameClientCerts[];
-CHROMEOS_EXPORT extern const char kDisableSigninFrameClientCertUserSelection[];
-CHROMEOS_EXPORT extern const char
-    kDisableSystemTimezoneAutomaticDetectionPolicy[];
-CHROMEOS_EXPORT extern const char kDisableVolumeAdjustSound[];
-CHROMEOS_EXPORT extern const char kDisableWakeOnWifi[];
-CHROMEOS_EXPORT extern const char kEnableArc[];
-CHROMEOS_EXPORT extern const char kEnableArcOobeOptinNoSkip[];
-CHROMEOS_EXPORT extern const char kEnableArcVm[];
-CHROMEOS_EXPORT extern const char kEnableCastReceiver[];
-CHROMEOS_EXPORT extern const char kEnableChromevoxDeveloperOption[];
-CHROMEOS_EXPORT extern const char kEnableConsumerKiosk[];
-CHROMEOS_EXPORT extern const char kEnableEncryptionMigration[];
-CHROMEOS_EXPORT extern const char kEnableExtensionAssetsSharing[];
-CHROMEOS_EXPORT extern const char kEnableFileManagerTouchMode[];
-CHROMEOS_EXPORT extern const char kEnableFirstRunUITransitions[];
-CHROMEOS_EXPORT extern const char kEnableMarketingOptInScreen[];
-CHROMEOS_EXPORT extern const char kEnablePhysicalKeyboardAutocorrect[];
-CHROMEOS_EXPORT extern const char kEnableRequestTabletSite[];
-CHROMEOS_EXPORT extern const char kEnableScreenshotTestingWithMode[];
-CHROMEOS_EXPORT extern const char kEnableTouchCalibrationSetting[];
-CHROMEOS_EXPORT extern const char kEnableTouchpadThreeFingerClick[];
-CHROMEOS_EXPORT extern const char kEnableVideoPlayerChromecastSupport[];
-CHROMEOS_EXPORT extern const char kEnterpriseDisableArc[];
-CHROMEOS_EXPORT extern const char kEnterpriseDisableLicenseTypeSelection[];
-CHROMEOS_EXPORT extern const char kEnterpriseEnableForcedReEnrollment[];
-CHROMEOS_EXPORT extern const char kEnterpriseEnableInitialEnrollment[];
-CHROMEOS_EXPORT extern const char kEnterpriseEnableZeroTouchEnrollment[];
-CHROMEOS_EXPORT extern const char kEnterpriseEnrollmentInitialModulus[];
-CHROMEOS_EXPORT extern const char kEnterpriseEnrollmentModulusLimit[];
-CHROMEOS_EXPORT extern const char kExternalMetricsCollectionInterval[];
-CHROMEOS_EXPORT extern const char kFirstExecAfterBoot[];
-CHROMEOS_EXPORT extern const char kFakeDriveFsLauncherChrootPath[];
-CHROMEOS_EXPORT extern const char kFakeDriveFsLauncherSocketPath[];
-CHROMEOS_EXPORT extern const char kForceDevToolsAvailable[];
-CHROMEOS_EXPORT extern const char kForceFirstRunUI[];
-CHROMEOS_EXPORT extern const char kForceHappinessTrackingSystem[];
-CHROMEOS_EXPORT extern const char kForceLoginManagerInTests[];
-CHROMEOS_EXPORT extern const char kForceSystemCompositorMode[];
-CHROMEOS_EXPORT extern const char kGoldenScreenshotsDir[];
-CHROMEOS_EXPORT extern const char kGuestSession[];
-CHROMEOS_EXPORT extern const char kGuestWallpaperLarge[];
-CHROMEOS_EXPORT extern const char kGuestWallpaperSmall[];
-CHROMEOS_EXPORT extern const char kHasChromeOSDiamondKey[];
-CHROMEOS_EXPORT extern const char kHasChromeOSKeyboard[];
-CHROMEOS_EXPORT extern const char kHideAndroidFilesInFilesApp[];
-CHROMEOS_EXPORT extern const char kHomedir[];
-CHROMEOS_EXPORT extern const char kIgnoreUserProfileMappingForTests[];
-CHROMEOS_EXPORT extern const char kLoginManager[];
-CHROMEOS_EXPORT extern const char kLoginProfile[];
-CHROMEOS_EXPORT extern const char kLoginUser[];
-CHROMEOS_EXPORT extern const char kMemoryPressureThresholds[];
-CHROMEOS_EXPORT extern const char kNaturalScrollDefault[];
-CHROMEOS_EXPORT extern const char kNeedArcMigrationPolicyCheck[];
-CHROMEOS_EXPORT extern const char kNoteTakingAppIds[];
-CHROMEOS_EXPORT extern const char kOobeForceShowScreen[];
-CHROMEOS_EXPORT extern const char kOobeGuestSession[];
-CHROMEOS_EXPORT extern const char kOobeSkipPostLogin[];
-CHROMEOS_EXPORT extern const char kOobeSkipToLogin[];
-CHROMEOS_EXPORT extern const char kOobeTimerInterval[];
-CHROMEOS_EXPORT extern const char kProfileRequiresPolicy[];
-CHROMEOS_EXPORT extern const char kRegulatoryLabelDir[];
-CHROMEOS_EXPORT extern const char kRlzPingDelay[];
-CHROMEOS_EXPORT extern const char kShelfHoverPreviews[];
-CHROMEOS_EXPORT extern const char kShowAndroidFilesInFilesApp[];
-CHROMEOS_EXPORT extern const char kShowLoginDevOverlay[];
-CHROMEOS_EXPORT extern const char kTestEncryptionMigrationUI[];
-CHROMEOS_EXPORT extern const char kTestWallpaperServer[];
-CHROMEOS_EXPORT extern const char kTetherStub[];
-CHROMEOS_EXPORT extern const char kTetherHostScansIgnoreWiredConnections[];
-CHROMEOS_EXPORT extern const char kWaitForInitialPolicyFetchForTest[];
-CHROMEOS_EXPORT extern const char kWakeOnWifiPacket[];
+COMPONENT_EXPORT(CHROMEOS_CONSTANTS)
+extern const char kAggressiveCacheDiscardThreshold[];
+COMPONENT_EXPORT(CHROMEOS_CONSTANTS)
+extern const char kAggressiveTabDiscardThreshold[];
+COMPONENT_EXPORT(CHROMEOS_CONSTANTS) extern const char kAggressiveThreshold[];
+COMPONENT_EXPORT(CHROMEOS_CONSTANTS)
+extern const char kAllowFailedPolicyFetchForTest[];
+COMPONENT_EXPORT(CHROMEOS_CONSTANTS) extern const char kAllowRAInDevMode[];
+COMPONENT_EXPORT(CHROMEOS_CONSTANTS) extern const char kAppAutoLaunched[];
+COMPONENT_EXPORT(CHROMEOS_CONSTANTS) extern const char kAppOemManifestFile[];
+COMPONENT_EXPORT(CHROMEOS_CONSTANTS) extern const char kArcAvailability[];
+COMPONENT_EXPORT(CHROMEOS_CONSTANTS) extern const char kArcAvailable[];
+COMPONENT_EXPORT(CHROMEOS_CONSTANTS) extern const char kArcDataCleanupOnStart[];
+COMPONENT_EXPORT(CHROMEOS_CONSTANTS) extern const char kArcDisableAppSync[];
+COMPONENT_EXPORT(CHROMEOS_CONSTANTS) extern const char kArcDisableLocaleSync[];
+COMPONENT_EXPORT(CHROMEOS_CONSTANTS)
+extern const char kArcDisablePlayAutoInstall[];
+COMPONENT_EXPORT(CHROMEOS_CONSTANTS) extern const char kArcForceCacheAppIcons[];
+COMPONENT_EXPORT(CHROMEOS_CONSTANTS) extern const char kArcForceShowOptInUi[];
+COMPONENT_EXPORT(CHROMEOS_CONSTANTS) extern const char kArcPackagesCacheMode[];
+COMPONENT_EXPORT(CHROMEOS_CONSTANTS)
+extern const char kArcPlayStoreAutoUpdate[];
+COMPONENT_EXPORT(CHROMEOS_CONSTANTS) extern const char kArcStartMode[];
+COMPONENT_EXPORT(CHROMEOS_CONSTANTS)
+extern const char kArcTransitionMigrationRequired[];
+COMPONENT_EXPORT(CHROMEOS_CONSTANTS) extern const char kArtifactsDir[];
+COMPONENT_EXPORT(CHROMEOS_CONSTANTS) extern const char kCellularFirst[];
+COMPONENT_EXPORT(CHROMEOS_CONSTANTS) extern const char kChildWallpaperLarge[];
+COMPONENT_EXPORT(CHROMEOS_CONSTANTS) extern const char kChildWallpaperSmall[];
+COMPONENT_EXPORT(CHROMEOS_CONSTANTS) extern const char kConservativeThreshold[];
+COMPONENT_EXPORT(CHROMEOS_CONSTANTS) extern const char kCrosGaiaApiV1[];
+COMPONENT_EXPORT(CHROMEOS_CONSTANTS) extern const char kCrosRegion[];
+COMPONENT_EXPORT(CHROMEOS_CONSTANTS) extern const char kCrosRegionsMode[];
+COMPONENT_EXPORT(CHROMEOS_CONSTANTS) extern const char kCrosRegionsModeHide[];
+COMPONENT_EXPORT(CHROMEOS_CONSTANTS)
+extern const char kCrosRegionsModeOverride[];
+COMPONENT_EXPORT(CHROMEOS_CONSTANTS) extern const char kDefaultWallpaperIsOem[];
+COMPONENT_EXPORT(CHROMEOS_CONSTANTS) extern const char kDefaultWallpaperLarge[];
+COMPONENT_EXPORT(CHROMEOS_CONSTANTS) extern const char kDefaultWallpaperSmall[];
+COMPONENT_EXPORT(CHROMEOS_CONSTANTS)
+extern const char kDerelictDetectionTimeout[];
+COMPONENT_EXPORT(CHROMEOS_CONSTANTS) extern const char kDerelictIdleTimeout[];
+COMPONENT_EXPORT(CHROMEOS_CONSTANTS) extern const char kDisableArcDataWipe[];
+COMPONENT_EXPORT(CHROMEOS_CONSTANTS)
+extern const char kDisableArcOptInVerification[];
+COMPONENT_EXPORT(CHROMEOS_CONSTANTS)
+extern const char kDisableCaptivePortalBypassProxy[];
+COMPONENT_EXPORT(CHROMEOS_CONSTANTS) extern const char kDisableCloudImport[];
+COMPONENT_EXPORT(CHROMEOS_CONSTANTS) extern const char kDisableDemoMode[];
+COMPONENT_EXPORT(CHROMEOS_CONSTANTS)
+extern const char kDisableDeviceDisabling[];
+COMPONENT_EXPORT(CHROMEOS_CONSTANTS)
+extern const char kDisableEncryptionMigration[];
+COMPONENT_EXPORT(CHROMEOS_CONSTANTS)
+extern const char kDisableEolNotification[];
+COMPONENT_EXPORT(CHROMEOS_CONSTANTS)
+extern const char kDisableFileManagerTouchMode[];
+COMPONENT_EXPORT(CHROMEOS_CONSTANTS)
+extern const char kDisableFineGrainedTimeZoneDetection[];
+COMPONENT_EXPORT(CHROMEOS_CONSTANTS) extern const char kDisableGaiaServices[];
+COMPONENT_EXPORT(CHROMEOS_CONSTANTS)
+extern const char kDisableHIDDetectionOnOOBE[];
+COMPONENT_EXPORT(CHROMEOS_CONSTANTS)
+extern const char kDisableLoginAnimations[];
+COMPONENT_EXPORT(CHROMEOS_CONSTANTS)
+extern const char kDisableMachineCertRequest[];
+COMPONENT_EXPORT(CHROMEOS_CONSTANTS)
+extern const char kDisableMtpWriteSupport[];
+COMPONENT_EXPORT(CHROMEOS_CONSTANTS)
+extern const char kDisableMultiDisplayLayout[];
+COMPONENT_EXPORT(CHROMEOS_CONSTANTS) extern const char kDisableNewZIPUnpacker[];
+COMPONENT_EXPORT(CHROMEOS_CONSTANTS)
+extern const char kDisableOfficeEditingComponentApp[];
+COMPONENT_EXPORT(CHROMEOS_CONSTANTS)
+extern const char kDisablePerUserTimezone[];
+COMPONENT_EXPORT(CHROMEOS_CONSTANTS)
+extern const char kDisablePhysicalKeyboardAutocorrect[];
+COMPONENT_EXPORT(CHROMEOS_CONSTANTS) extern const char kDisableRollbackOption[];
+COMPONENT_EXPORT(CHROMEOS_CONSTANTS)
+extern const char kDisableSigninFrameClientCerts[];
+COMPONENT_EXPORT(CHROMEOS_CONSTANTS)
+extern const char kDisableSigninFrameClientCertUserSelection[];
+COMPONENT_EXPORT(CHROMEOS_CONSTANTS)
+extern const char kDisableSystemTimezoneAutomaticDetectionPolicy[];
+COMPONENT_EXPORT(CHROMEOS_CONSTANTS)
+extern const char kDisableVolumeAdjustSound[];
+COMPONENT_EXPORT(CHROMEOS_CONSTANTS) extern const char kDisableWakeOnWifi[];
+COMPONENT_EXPORT(CHROMEOS_CONSTANTS) extern const char kEnableArc[];
+COMPONENT_EXPORT(CHROMEOS_CONSTANTS)
+extern const char kEnableArcOobeOptinNoSkip[];
+COMPONENT_EXPORT(CHROMEOS_CONSTANTS) extern const char kEnableArcVm[];
+COMPONENT_EXPORT(CHROMEOS_CONSTANTS) extern const char kEnableCastReceiver[];
+COMPONENT_EXPORT(CHROMEOS_CONSTANTS)
+extern const char kEnableChromevoxDeveloperOption[];
+COMPONENT_EXPORT(CHROMEOS_CONSTANTS) extern const char kEnableConsumerKiosk[];
+COMPONENT_EXPORT(CHROMEOS_CONSTANTS)
+extern const char kEnableEncryptionMigration[];
+COMPONENT_EXPORT(CHROMEOS_CONSTANTS)
+extern const char kEnableExtensionAssetsSharing[];
+COMPONENT_EXPORT(CHROMEOS_CONSTANTS)
+extern const char kEnableFileManagerTouchMode[];
+COMPONENT_EXPORT(CHROMEOS_CONSTANTS)
+extern const char kEnableFirstRunUITransitions[];
+COMPONENT_EXPORT(CHROMEOS_CONSTANTS)
+extern const char kEnableMarketingOptInScreen[];
+COMPONENT_EXPORT(CHROMEOS_CONSTANTS)
+extern const char kEnablePhysicalKeyboardAutocorrect[];
+COMPONENT_EXPORT(CHROMEOS_CONSTANTS)
+extern const char kEnableRequestTabletSite[];
+COMPONENT_EXPORT(CHROMEOS_CONSTANTS)
+extern const char kEnableScreenshotTestingWithMode[];
+COMPONENT_EXPORT(CHROMEOS_CONSTANTS)
+extern const char kEnableTouchCalibrationSetting[];
+COMPONENT_EXPORT(CHROMEOS_CONSTANTS)
+extern const char kEnableTouchpadThreeFingerClick[];
+COMPONENT_EXPORT(CHROMEOS_CONSTANTS)
+extern const char kEnableVideoPlayerChromecastSupport[];
+COMPONENT_EXPORT(CHROMEOS_CONSTANTS) extern const char kEnterpriseDisableArc[];
+COMPONENT_EXPORT(CHROMEOS_CONSTANTS)
+extern const char kEnterpriseDisableLicenseTypeSelection[];
+COMPONENT_EXPORT(CHROMEOS_CONSTANTS)
+extern const char kEnterpriseEnableForcedReEnrollment[];
+COMPONENT_EXPORT(CHROMEOS_CONSTANTS)
+extern const char kEnterpriseEnableInitialEnrollment[];
+COMPONENT_EXPORT(CHROMEOS_CONSTANTS)
+extern const char kEnterpriseEnableZeroTouchEnrollment[];
+COMPONENT_EXPORT(CHROMEOS_CONSTANTS)
+extern const char kEnterpriseEnrollmentInitialModulus[];
+COMPONENT_EXPORT(CHROMEOS_CONSTANTS)
+extern const char kEnterpriseEnrollmentModulusLimit[];
+COMPONENT_EXPORT(CHROMEOS_CONSTANTS)
+extern const char kExternalMetricsCollectionInterval[];
+COMPONENT_EXPORT(CHROMEOS_CONSTANTS) extern const char kFirstExecAfterBoot[];
+COMPONENT_EXPORT(CHROMEOS_CONSTANTS)
+extern const char kFakeDriveFsLauncherChrootPath[];
+COMPONENT_EXPORT(CHROMEOS_CONSTANTS)
+extern const char kFakeDriveFsLauncherSocketPath[];
+COMPONENT_EXPORT(CHROMEOS_CONSTANTS)
+extern const char kForceDevToolsAvailable[];
+COMPONENT_EXPORT(CHROMEOS_CONSTANTS) extern const char kForceFirstRunUI[];
+COMPONENT_EXPORT(CHROMEOS_CONSTANTS)
+extern const char kForceHappinessTrackingSystem[];
+COMPONENT_EXPORT(CHROMEOS_CONSTANTS)
+extern const char kForceLoginManagerInTests[];
+COMPONENT_EXPORT(CHROMEOS_CONSTANTS)
+extern const char kForceSystemCompositorMode[];
+COMPONENT_EXPORT(CHROMEOS_CONSTANTS) extern const char kGoldenScreenshotsDir[];
+COMPONENT_EXPORT(CHROMEOS_CONSTANTS) extern const char kGuestSession[];
+COMPONENT_EXPORT(CHROMEOS_CONSTANTS) extern const char kGuestWallpaperLarge[];
+COMPONENT_EXPORT(CHROMEOS_CONSTANTS) extern const char kGuestWallpaperSmall[];
+COMPONENT_EXPORT(CHROMEOS_CONSTANTS) extern const char kHasChromeOSDiamondKey[];
+COMPONENT_EXPORT(CHROMEOS_CONSTANTS) extern const char kHasChromeOSKeyboard[];
+COMPONENT_EXPORT(CHROMEOS_CONSTANTS)
+extern const char kHideAndroidFilesInFilesApp[];
+COMPONENT_EXPORT(CHROMEOS_CONSTANTS) extern const char kHomedir[];
+COMPONENT_EXPORT(CHROMEOS_CONSTANTS)
+extern const char kIgnoreUserProfileMappingForTests[];
+COMPONENT_EXPORT(CHROMEOS_CONSTANTS) extern const char kLoginManager[];
+COMPONENT_EXPORT(CHROMEOS_CONSTANTS) extern const char kLoginProfile[];
+COMPONENT_EXPORT(CHROMEOS_CONSTANTS) extern const char kLoginUser[];
+COMPONENT_EXPORT(CHROMEOS_CONSTANTS)
+extern const char kMemoryPressureThresholds[];
+COMPONENT_EXPORT(CHROMEOS_CONSTANTS) extern const char kNaturalScrollDefault[];
+COMPONENT_EXPORT(CHROMEOS_CONSTANTS)
+extern const char kNeedArcMigrationPolicyCheck[];
+COMPONENT_EXPORT(CHROMEOS_CONSTANTS) extern const char kNoteTakingAppIds[];
+COMPONENT_EXPORT(CHROMEOS_CONSTANTS) extern const char kOobeForceShowScreen[];
+COMPONENT_EXPORT(CHROMEOS_CONSTANTS) extern const char kOobeGuestSession[];
+COMPONENT_EXPORT(CHROMEOS_CONSTANTS) extern const char kOobeSkipPostLogin[];
+COMPONENT_EXPORT(CHROMEOS_CONSTANTS) extern const char kOobeSkipToLogin[];
+COMPONENT_EXPORT(CHROMEOS_CONSTANTS) extern const char kOobeTimerInterval[];
+COMPONENT_EXPORT(CHROMEOS_CONSTANTS) extern const char kProfileRequiresPolicy[];
+COMPONENT_EXPORT(CHROMEOS_CONSTANTS) extern const char kRegulatoryLabelDir[];
+COMPONENT_EXPORT(CHROMEOS_CONSTANTS) extern const char kRlzPingDelay[];
+COMPONENT_EXPORT(CHROMEOS_CONSTANTS) extern const char kShelfHoverPreviews[];
+COMPONENT_EXPORT(CHROMEOS_CONSTANTS)
+extern const char kShowAndroidFilesInFilesApp[];
+COMPONENT_EXPORT(CHROMEOS_CONSTANTS) extern const char kShowLoginDevOverlay[];
+COMPONENT_EXPORT(CHROMEOS_CONSTANTS)
+extern const char kTestEncryptionMigrationUI[];
+COMPONENT_EXPORT(CHROMEOS_CONSTANTS) extern const char kTestWallpaperServer[];
+COMPONENT_EXPORT(CHROMEOS_CONSTANTS) extern const char kTetherStub[];
+COMPONENT_EXPORT(CHROMEOS_CONSTANTS)
+extern const char kTetherHostScansIgnoreWiredConnections[];
+COMPONENT_EXPORT(CHROMEOS_CONSTANTS)
+extern const char kWaitForInitialPolicyFetchForTest[];
+COMPONENT_EXPORT(CHROMEOS_CONSTANTS) extern const char kWakeOnWifiPacket[];
 
 // Controls whether to enable Chrome OS Account Manager.
-CHROMEOS_EXPORT extern const base::Feature kAccountManager;
+COMPONENT_EXPORT(CHROMEOS_CONSTANTS) extern const base::Feature kAccountManager;
 
 // Controls whether to enable Google Assistant feature.
-CHROMEOS_EXPORT extern const base::Feature kAssistantFeature;
+COMPONENT_EXPORT(CHROMEOS_CONSTANTS)
+extern const base::Feature kAssistantFeature;
 
 // Controls whether to show the system tray language toggle in Demo Mode.
-CHROMEOS_EXPORT extern const base::Feature kShowLanguageToggleInDemoMode;
+COMPONENT_EXPORT(CHROMEOS_CONSTANTS)
+extern const base::Feature kShowLanguageToggleInDemoMode;
 
 // Controls whether to show the Play Store icon in Demo Mode.
-CHROMEOS_EXPORT extern const base::Feature kShowPlayInDemoMode;
+COMPONENT_EXPORT(CHROMEOS_CONSTANTS)
+extern const base::Feature kShowPlayInDemoMode;
 
 // Controls whether to show a static splash screen instead of the user pods
 // before demo sessions log in.
-CHROMEOS_EXPORT extern const base::Feature kShowSplashScreenInDemoMode;
+COMPONENT_EXPORT(CHROMEOS_CONSTANTS)
+extern const base::Feature kShowSplashScreenInDemoMode;
 
 // Controls whether to support country-level customization in Demo Mode.
-CHROMEOS_EXPORT extern const base::Feature
-    kSupportCountryCustomizationInDemoMode;
+COMPONENT_EXPORT(CHROMEOS_CONSTANTS)
+extern const base::Feature kSupportCountryCustomizationInDemoMode;
 
 // Returns true if the system should wake in response to wifi traffic.
-CHROMEOS_EXPORT bool WakeOnWifiEnabled();
+COMPONENT_EXPORT(CHROMEOS_CONSTANTS) bool WakeOnWifiEnabled();
 
 // Returns true if memory pressure handling is enabled.
-CHROMEOS_EXPORT bool MemoryPressureHandlingEnabled();
+COMPONENT_EXPORT(CHROMEOS_CONSTANTS) bool MemoryPressureHandlingEnabled();
 
 // Returns thresholds for determining if the system is under memory pressure.
-CHROMEOS_EXPORT base::chromeos::MemoryPressureMonitor::MemoryPressureThresholds
+COMPONENT_EXPORT(CHROMEOS_CONSTANTS)
+base::chromeos::MemoryPressureMonitor::MemoryPressureThresholds
 GetMemoryPressureThresholds();
 
 // Returns true if flags are set indicating that stored user keys are being
 // converted to GAIA IDs.
-CHROMEOS_EXPORT bool IsGaiaIdMigrationStarted();
+COMPONENT_EXPORT(CHROMEOS_CONSTANTS) bool IsGaiaIdMigrationStarted();
 
 // Returns true if this is a Cellular First device.
-CHROMEOS_EXPORT bool IsCellularFirstDevice();
+COMPONENT_EXPORT(CHROMEOS_CONSTANTS) bool IsCellularFirstDevice();
 
 // Returns true if Chrome OS Account Manager is enabled.
-CHROMEOS_EXPORT bool IsAccountManagerEnabled();
+COMPONENT_EXPORT(CHROMEOS_CONSTANTS) bool IsAccountManagerEnabled();
 
 // Returns true if Google Assistant flags are enabled.
-CHROMEOS_EXPORT bool IsAssistantFlagsEnabled();
+COMPONENT_EXPORT(CHROMEOS_CONSTANTS) bool IsAssistantFlagsEnabled();
 
 // Returns true if Google Assistant is enabled.
-CHROMEOS_EXPORT bool IsAssistantEnabled();
+COMPONENT_EXPORT(CHROMEOS_CONSTANTS) bool IsAssistantEnabled();
 
 // Returns true if client certificate authentication for the sign-in frame on
 // the Chrome OS sign-in screen is enabled.
-CHROMEOS_EXPORT bool IsSigninFrameClientCertsEnabled();
+COMPONENT_EXPORT(CHROMEOS_CONSTANTS) bool IsSigninFrameClientCertsEnabled();
 
 // Returns true if user selection of certificates is enabled for the sign-in
 // frame on the Chrome OS sign-in screen.
-CHROMEOS_EXPORT bool IsSigninFrameClientCertUserSelectionEnabled();
+COMPONENT_EXPORT(CHROMEOS_CONSTANTS)
+bool IsSigninFrameClientCertUserSelectionEnabled();
 
 // Returns true if we should show window previews when hovering over an app
 // on the shelf.
-CHROMEOS_EXPORT bool ShouldShowShelfHoverPreviews();
+COMPONENT_EXPORT(CHROMEOS_CONSTANTS) bool ShouldShowShelfHoverPreviews();
 
 // Returns true if Instant Tethering should support hosts which use the
 // background advertisement model.
-CHROMEOS_EXPORT bool IsInstantTetheringBackgroundAdvertisingSupported();
+COMPONENT_EXPORT(CHROMEOS_CONSTANTS)
+bool IsInstantTetheringBackgroundAdvertisingSupported();
 
 // Returns true if the Chromebook should ignore its wired connections when
 // deciding whether to run scans for tethering hosts. Should be used only for
 // testing.
-CHROMEOS_EXPORT bool ShouldTetherHostScansIgnoreWiredConnections();
+COMPONENT_EXPORT(CHROMEOS_CONSTANTS)
+bool ShouldTetherHostScansIgnoreWiredConnections();
 
 // Returns true if Play Store should be available in Demo Mode.
 // TODO(michaelpg): Remove after M71 branch to re-enable Play Store by default.
-CHROMEOS_EXPORT bool ShouldShowPlayStoreInDemoMode();
+COMPONENT_EXPORT(CHROMEOS_CONSTANTS) bool ShouldShowPlayStoreInDemoMode();
 
 // Returns true if we should skip all other OOBE pages after user login.
-CHROMEOS_EXPORT bool ShouldSkipOobePostLogin();
+COMPONENT_EXPORT(CHROMEOS_CONSTANTS) bool ShouldSkipOobePostLogin();
 
 }  // namespace switches
 }  // namespace chromeos
diff --git a/chromeos/constants/devicetype.h b/chromeos/constants/devicetype.h
index f94c373..d01ac729 100644
--- a/chromeos/constants/devicetype.h
+++ b/chromeos/constants/devicetype.h
@@ -5,7 +5,7 @@
 #ifndef CHROMEOS_CONSTANTS_DEVICETYPE_H_
 #define CHROMEOS_CONSTANTS_DEVICETYPE_H_
 
-#include "chromeos/chromeos_export.h"
+#include "base/component_export.h"
 
 namespace chromeos {
 
@@ -18,7 +18,7 @@
 };
 
 // Returns the current device type, eg, Chromebook, Chromebox.
-CHROMEOS_EXPORT DeviceType GetDeviceType();
+COMPONENT_EXPORT(CHROMEOS_CONSTANTS) DeviceType GetDeviceType();
 
 }  // namespace chromeos
 
diff --git a/chromeos/dbus/BUILD.gn b/chromeos/dbus/BUILD.gn
index 3724abd..97fb58f 100644
--- a/chromeos/dbus/BUILD.gn
+++ b/chromeos/dbus/BUILD.gn
@@ -11,7 +11,6 @@
   output_name = "chromeos_dbus"  # Avoid conflict with //dbus
   defines = [ "IS_CHROMEOS_DBUS_IMPL" ]
   public_deps = [
-    ":constants",
     "//dbus",
   ]
   deps = [
@@ -33,6 +32,7 @@
     ":smbprovider_proto",
     ":vm_applications_apps_proto",
     "//base",
+    "//chromeos/dbus/constants",
     "//components/account_id",
     "//components/device_event_log",
     "//components/policy:cloud_policy_proto_generated_compile",
@@ -236,24 +236,12 @@
   ]
 }
 
-source_set("constants") {
-  defines = [ "IS_CHROMEOS_DBUS_IMPL" ]
-  deps = [
-    "//base",
-  ]
-  sources = [
-    "constants/dbus_paths.cc",
-    "constants/dbus_paths.h",
-    "constants/dbus_switches.cc",
-    "constants/dbus_switches.h",
-  ]
-}
-
 source_set("test_support") {
   testonly = true
   configs += [ "//build/config/linux/dbus" ]
   public_deps = [
     ":dbus",
+    "//chromeos/dbus/constants",
     "//dbus",
     "//dbus:test_support",
   ]
diff --git a/chromeos/dbus/constants/BUILD.gn b/chromeos/dbus/constants/BUILD.gn
new file mode 100644
index 0000000..1df6c29
--- /dev/null
+++ b/chromeos/dbus/constants/BUILD.gn
@@ -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.
+
+assert(is_chromeos, "Non-Chrome-OS builds must not depend on //chromeos")
+
+component("constants") {
+  output_name = "chromeos_dbus_constants"
+  defines = [ "IS_CHROMEOS_DBUS_CONSTANTS_IMPL" ]
+  deps = [
+    "//base",
+  ]
+  sources = [
+    "dbus_paths.cc",
+    "dbus_paths.h",
+    "dbus_switches.cc",
+    "dbus_switches.h",
+  ]
+}
diff --git a/chromeos/dbus/constants/dbus_paths.h b/chromeos/dbus/constants/dbus_paths.h
index 695e6d2..ffc89db1 100644
--- a/chromeos/dbus/constants/dbus_paths.h
+++ b/chromeos/dbus/constants/dbus_paths.h
@@ -29,12 +29,12 @@
 };
 
 // Call once to register the provider for the path keys defined above.
-COMPONENT_EXPORT(CHROMEOS_DBUS) void RegisterPathProvider();
+COMPONENT_EXPORT(CHROMEOS_DBUS_CONSTANTS) void RegisterPathProvider();
 
 // Overrides some of the paths listed above so that those files can be used
 // when not running on ChromeOS. The stubs files will be relative to
 // |stubs_dir|. It is not valid to call this when running on ChromeOS.
-COMPONENT_EXPORT(CHROMEOS_DBUS)
+COMPONENT_EXPORT(CHROMEOS_DBUS_CONSTANTS)
 void RegisterStubPathOverrides(const base::FilePath& stubs_dir);
 
 }  // namespace dbus_paths
diff --git a/chromeos/dbus/constants/dbus_switches.h b/chromeos/dbus/constants/dbus_switches.h
index 163054e..838e21c 100644
--- a/chromeos/dbus/constants/dbus_switches.h
+++ b/chromeos/dbus/constants/dbus_switches.h
@@ -10,12 +10,18 @@
 namespace chromeos {
 namespace switches {
 
-COMPONENT_EXPORT(CHROMEOS_DBUS) extern const char kAttestationServer[];
-COMPONENT_EXPORT(CHROMEOS_DBUS) extern const char kDbusStub[];
-COMPONENT_EXPORT(CHROMEOS_DBUS) extern const char kFakeOobeConfiguration[];
-COMPONENT_EXPORT(CHROMEOS_DBUS) extern const char kShillStub[];
-COMPONENT_EXPORT(CHROMEOS_DBUS) extern const char kSmsTestMessages[];
-COMPONENT_EXPORT(CHROMEOS_DBUS) extern const char kSystemDevMode[];
+COMPONENT_EXPORT(CHROMEOS_DBUS_CONSTANTS)
+extern const char kAttestationServer[];
+COMPONENT_EXPORT(CHROMEOS_DBUS_CONSTANTS)
+extern const char kDbusStub[];
+COMPONENT_EXPORT(CHROMEOS_DBUS_CONSTANTS)
+extern const char kFakeOobeConfiguration[];
+COMPONENT_EXPORT(CHROMEOS_DBUS_CONSTANTS)
+extern const char kShillStub[];
+COMPONENT_EXPORT(CHROMEOS_DBUS_CONSTANTS)
+extern const char kSmsTestMessages[];
+COMPONENT_EXPORT(CHROMEOS_DBUS_CONSTANTS)
+extern const char kSystemDevMode[];
 
 }  // namespace switches
 }  // namespace chromeos
diff --git a/chromeos/disks/BUILD.gn b/chromeos/disks/BUILD.gn
index 2cee8ce2..90b73ced 100644
--- a/chromeos/disks/BUILD.gn
+++ b/chromeos/disks/BUILD.gn
@@ -10,7 +10,7 @@
   defines = [ "IS_CHROMEOS_DISKS_IMPL" ]
   deps = [
     "//base",
-    "//chromeos:chromeos_constants",
+    "//chromeos/constants",
     "//chromeos/dbus",
     "//chromeos/dbus:power_manager_proto",
   ]
@@ -49,7 +49,7 @@
     ":disks",
     ":test_support",
     "//base/test:test_support",
-    "//chromeos:chromeos_constants",
+    "//chromeos/constants",
     "//chromeos/dbus:power_manager_proto",
     "//chromeos/dbus:test_support",
     "//testing/gmock",
diff --git a/chromeos/login/auth/BUILD.gn b/chromeos/login/auth/BUILD.gn
index 07d3cdbd..0b9f737 100644
--- a/chromeos/login/auth/BUILD.gn
+++ b/chromeos/login/auth/BUILD.gn
@@ -15,7 +15,7 @@
   deps = [
     "//base",
     "//base:i18n",
-    "//chromeos:chromeos_constants",
+    "//chromeos/constants",
     "//chromeos/cryptohome",
     "//chromeos/dbus",
     "//chromeos/dbus:authpolicy_proto",
@@ -110,7 +110,6 @@
     ":auth",
     "//base",
     "//base:i18n",
-    "//chromeos:chromeos_constants",
     "//chromeos/dbus:authpolicy_proto",
     "//chromeos/dbus:test_support",
     "//testing/gmock",
diff --git a/chromeos/login/login_state/BUILD.gn b/chromeos/login/login_state/BUILD.gn
index 45d0563..5f01e7a5 100644
--- a/chromeos/login/login_state/BUILD.gn
+++ b/chromeos/login/login_state/BUILD.gn
@@ -8,7 +8,7 @@
   defines = [ "IS_LOGIN_STATE_IMPL" ]
   deps = [
     "//base",
-    "//chromeos:chromeos_constants",
+    "//chromeos/constants",
     "//components/device_event_log",
   ]
   sources = [
@@ -37,7 +37,7 @@
   deps = [
     ":login_state",
     "//base",
-    "//chromeos:chromeos_constants",
+    "//chromeos/constants",
     "//testing/gmock",
     "//testing/gtest",
   ]
diff --git a/chromeos/network/BUILD.gn b/chromeos/network/BUILD.gn
index 7855b048..5b968ef 100644
--- a/chromeos/network/BUILD.gn
+++ b/chromeos/network/BUILD.gn
@@ -13,7 +13,7 @@
   deps = [
     "//base",
     "//base:i18n",
-    "//chromeos:chromeos_constants",
+    "//chromeos/constants",
     "//chromeos/dbus",
     "//chromeos/login/login_state",
     "//components/account_id",
@@ -201,8 +201,8 @@
     "//base",
     "//base:i18n",
     "//base/test:test_support",
-    "//chromeos:chromeos_constants",
     "//chromeos:test_utils",
+    "//chromeos/constants",
     "//chromeos/dbus:test_support",
     "//chromeos/login/login_state",
     "//components/onc",
diff --git a/chromeos/services/assistant/BUILD.gn b/chromeos/services/assistant/BUILD.gn
index 1de270d..1db06b300 100644
--- a/chromeos/services/assistant/BUILD.gn
+++ b/chromeos/services/assistant/BUILD.gn
@@ -33,9 +33,9 @@
   deps = [
     "//base",
     "//build/util:webkit_version",
-    "//chromeos:chromeos_constants",
     "//chromeos/assistant:buildflags",
     "//chromeos/audio",
+    "//chromeos/constants",
     "//chromeos/dbus",
     "//chromeos/dbus:power_manager_proto",
     "//components/account_id",
diff --git a/chromeos/settings/BUILD.gn b/chromeos/settings/BUILD.gn
index a527b56..12d1ff9f 100644
--- a/chromeos/settings/BUILD.gn
+++ b/chromeos/settings/BUILD.gn
@@ -11,7 +11,7 @@
   deps = [
     "//base",
     "//base:i18n",
-    "//chromeos:chromeos_constants",
+    "//chromeos/constants",
     "//chromeos/login/login_state",
     "//third_party/icu",
   ]
@@ -36,7 +36,6 @@
     "//base",
     "//base:i18n",
     "//base/test:test_support",
-    "//chromeos:chromeos_constants",
     "//testing/gmock",
     "//testing/gtest",
     "//third_party/icu",
diff --git a/chromeos/tpm/BUILD.gn b/chromeos/tpm/BUILD.gn
index 2956e3bc..2b5de0f 100644
--- a/chromeos/tpm/BUILD.gn
+++ b/chromeos/tpm/BUILD.gn
@@ -13,6 +13,7 @@
     "//chromeos/cryptohome",
     "//chromeos/dbus",
     "//chromeos/dbus:cryptohome_proto",
+    "//chromeos/dbus/constants",
     "//chromeos/login/login_state",
     "//components/account_id",
     "//components/policy/core/common:common_constants",
@@ -37,10 +38,10 @@
   deps = [
     ":tpm",
     "//base/test:test_support",
-    "//chromeos:chromeos_constants",
     "//chromeos/cryptohome:test_support",
     "//chromeos/dbus:cryptohome_proto",
     "//chromeos/dbus:test_support",
+    "//chromeos/dbus/constants",
     "//components/policy/proto",
     "//google_apis",
     "//testing/gtest",
diff --git a/components/arc/BUILD.gn b/components/arc/BUILD.gn
index 11017d0ba..40d9969 100644
--- a/components/arc/BUILD.gn
+++ b/components/arc/BUILD.gn
@@ -82,8 +82,8 @@
     "//ash:ash",
     "//ash/public/cpp",
     "//base",
-    "//chromeos:chromeos_constants",
     "//chromeos/audio",
+    "//chromeos/constants",
     "//chromeos/dbus",
     "//chromeos/dbus:login_manager_proto",
     "//chromeos/dbus:power_manager_proto",
@@ -165,7 +165,7 @@
   deps = [
     "//ash/public/cpp",
     "//base",
-    "//chromeos:chromeos_constants",
+    "//chromeos/constants",
     "//chromeos/dbus",
     "//ui/aura",
   ]
@@ -203,7 +203,7 @@
   deps = [
     "//ash/public/cpp",
     "//base",
-    "//chromeos:chromeos_constants",
+    "//chromeos/constants",
     "//chromeos/cryptohome",
     "//chromeos/dbus",
     "//chromeos/dbus:login_manager_proto",
@@ -345,7 +345,7 @@
     "//ash/public/cpp",
     "//base",
     "//base/test:test_support",
-    "//chromeos:chromeos_constants",
+    "//chromeos/constants",
     "//chromeos/cryptohome:test_support",
     "//chromeos/dbus:power_manager_proto",
     "//chromeos/dbus:test_support",
diff --git a/components/autofill/android/BUILD.gn b/components/autofill/android/BUILD.gn
index 3dae3f5..bd2ac36 100644
--- a/components/autofill/android/BUILD.gn
+++ b/components/autofill/android/BUILD.gn
@@ -74,6 +74,7 @@
 
 java_cpp_enum("autofill_core_browser_java_enums") {
   sources = [
+    "../core/browser/accessory_sheet_data.h",
     "../core/browser/popup_item_ids.h",
   ]
 }
diff --git a/components/autofill/core/browser/accessory_sheet_data.cc b/components/autofill/core/browser/accessory_sheet_data.cc
index dc0eb25b..75cce7f 100644
--- a/components/autofill/core/browser/accessory_sheet_data.cc
+++ b/components/autofill/core/browser/accessory_sheet_data.cc
@@ -67,8 +67,9 @@
   return display_text_ == fc.display_text_;
 }
 
-AccessorySheetData::AccessorySheetData(const base::string16& title)
-    : title_(title) {}
+AccessorySheetData::AccessorySheetData(FallbackSheetType sheet_type,
+                                       const base::string16& title)
+    : sheet_type_(sheet_type), title_(title) {}
 
 AccessorySheetData::AccessorySheetData(const AccessorySheetData& data) =
     default;
diff --git a/components/autofill/core/browser/accessory_sheet_data.h b/components/autofill/core/browser/accessory_sheet_data.h
index 5e439d7..384079b8 100644
--- a/components/autofill/core/browser/accessory_sheet_data.h
+++ b/components/autofill/core/browser/accessory_sheet_data.h
@@ -87,14 +87,20 @@
   base::string16 display_text_;
 };
 
+// GENERATED_JAVA_ENUM_PACKAGE: (
+//   org.chromium.chrome.browser.autofill.keyboard_accessory)
+enum class FallbackSheetType {
+  // Indicates the data type to which an AccessorySheetData object corresponds.
+  PASSWORD,
+  CREDIT_CARD
+};
+
 // Represents the contents of a bottom sheet tab below the keyboard accessory,
 // which can correspond to passwords, credit cards, or profiles data.
-//
-// TODO(crbug.com/902425): Add a field to indicate if this corresponds to
-//                         password, profile, or credit card data.
 class AccessorySheetData {
  public:
-  explicit AccessorySheetData(const base::string16& title);
+  explicit AccessorySheetData(FallbackSheetType sheet_type,
+                              const base::string16& title);
   AccessorySheetData(const AccessorySheetData& data);
   AccessorySheetData(AccessorySheetData&& data);
 
@@ -104,6 +110,7 @@
   AccessorySheetData& operator=(AccessorySheetData&& data);
 
   const base::string16& title() const { return title_; }
+  FallbackSheetType get_sheet_type() const { return sheet_type_; }
 
   void add_user_info(UserInfo user_info) {
     user_info_list_.emplace_back(std::move(user_info));
@@ -126,6 +133,7 @@
   bool operator==(const AccessorySheetData& data) const;
 
  private:
+  FallbackSheetType sheet_type_;
   base::string16 title_;
   std::vector<UserInfo> user_info_list_;
   std::vector<FooterCommand> footer_commands_;
diff --git a/components/autofill/core/browser/local_card_migration_manager.cc b/components/autofill/core/browser/local_card_migration_manager.cc
index ccb9c3d7..2a2cb4c4 100644
--- a/components/autofill/core/browser/local_card_migration_manager.cc
+++ b/components/autofill/core/browser/local_card_migration_manager.cc
@@ -10,6 +10,7 @@
 #include <vector>
 
 #include "base/bind.h"
+#include "base/metrics/histogram_functions.h"
 #include "base/stl_util.h"
 #include "base/strings/utf_string_conversions.h"
 #include "components/autofill/core/browser/autofill_client.h"
@@ -150,10 +151,22 @@
   AutofillMetrics::LogLocalCardMigrationPromptMetric(
       local_card_migration_origin_, AutofillMetrics::MAIN_DIALOG_ACCEPTED);
 
+  // Log number of LocalCardMigration strikes when migration was accepted.
+  if (base::FeatureList::IsEnabled(
+          features::kAutofillSaveCreditCardUsesStrikeSystemV2) &&
+      base::FeatureList::IsEnabled(
+          features::kAutofillLocalCardMigrationUsesStrikeSystemV2)) {
+    base::UmaHistogramCounts1000(
+        "Autofill.StrikeDatabase.StrikesPresentWhenLocalCardMigrationAccepted",
+        GetLocalCardMigrationStrikeDatabase()->GetStrikes());
+  }
+
   // If there are cards which aren't selected, add 3 strikes to
   // LocalCardMigrationStrikeDatabase.
   if (base::FeatureList::IsEnabled(
           features::kAutofillSaveCreditCardUsesStrikeSystemV2) &&
+      base::FeatureList::IsEnabled(
+          features::kAutofillLocalCardMigrationUsesStrikeSystemV2) &&
       (selected_card_guids.size() < migratable_credit_cards_.size())) {
     GetLocalCardMigrationStrikeDatabase()->AddStrikes(
         LocalCardMigrationStrikeDatabase::
diff --git a/components/autofill/core/browser/local_card_migration_manager_unittest.cc b/components/autofill/core/browser/local_card_migration_manager_unittest.cc
index 983d810..d9099992 100644
--- a/components/autofill/core/browser/local_card_migration_manager_unittest.cc
+++ b/components/autofill/core/browser/local_card_migration_manager_unittest.cc
@@ -1181,4 +1181,42 @@
   EXPECT_EQ(local_card_migration_strike_database.GetStrikes(), 3);
 }
 
+// When local card migration is accepted, UMA metrics for LocalCardMigration
+// strike count is logged.
+TEST_F(LocalCardMigrationManagerTest, MigrateCreditCard_StrikeCountUMALogged) {
+  scoped_feature_list_.InitWithFeatures(
+      // Enabled
+      {features::kAutofillCreditCardLocalCardMigration,
+       features::kAutofillSaveCreditCardUsesStrikeSystemV2,
+       features::kAutofillLocalCardMigrationUsesStrikeSystemV2},
+      // Disabled
+      {});
+
+  AddLocalCreditCard(personal_data_, "Flo Master", "4111111111111111", "11",
+                     test::NextYear().c_str(), "1", "guid1");
+  AddLocalCreditCard(personal_data_, "Flo Master", "5454545454545454", "11",
+                     test::NextYear().c_str(), "1", "guid2");
+  autofill_client_.GetPrefs()->SetDouble(prefs::kAutofillBillingCustomerNumber,
+                                         12345);
+  local_card_migration_manager_->GetMigratableCreditCards();
+
+  // Add 4 LocalCardMigration strikes.
+  LocalCardMigrationStrikeDatabase local_card_migration_strike_database =
+      LocalCardMigrationStrikeDatabase(strike_database_);
+  local_card_migration_strike_database.AddStrikes(4);
+  EXPECT_EQ(local_card_migration_strike_database.GetStrikes(), 4);
+
+  base::HistogramTester histogram_tester;
+
+  // Select the cards.
+  autofill_client_.set_migration_card_selections(
+      std::vector<std::string>{"guid1", "guid2"});
+  local_card_migration_manager_->AttemptToOfferLocalCardMigration(true);
+
+  // Verify that the strike count was logged when card migration accepted.
+  histogram_tester.ExpectBucketCount(
+      "Autofill.StrikeDatabase.StrikesPresentWhenLocalCardMigrationAccepted", 4,
+      1);
+}
+
 }  // namespace autofill
diff --git a/components/autofill/core/browser/phone_number_i18n.cc b/components/autofill/core/browser/phone_number_i18n.cc
index 85cf047..956eed00 100644
--- a/components/autofill/core/browser/phone_number_i18n.cc
+++ b/components/autofill/core/browser/phone_number_i18n.cc
@@ -329,6 +329,15 @@
   return base::UTF8ToUTF16(phone);
 }
 
+std::string FormatPhoneNationallyForDisplay(const std::string& phone_number,
+                                            const std::string& country_code) {
+  if (IsValidPhoneNumber(phone_number, country_code)) {
+    return FormatPhoneNumber(phone_number, country_code,
+                             PhoneNumberUtil::PhoneNumberFormat::NATIONAL);
+  }
+  return phone_number;
+}
+
 std::string FormatPhoneForDisplay(const std::string& phone_number,
                                   const std::string& country_code) {
   if (IsValidPhoneNumber(phone_number, country_code)) {
diff --git a/components/autofill/core/browser/phone_number_i18n.h b/components/autofill/core/browser/phone_number_i18n.h
index 5f6f767..8def9448 100644
--- a/components/autofill/core/browser/phone_number_i18n.h
+++ b/components/autofill/core/browser/phone_number_i18n.h
@@ -90,12 +90,18 @@
                        const std::string& app_locale);
 
 // Returns the phone number from the given |profile| formatted for display.
-// If it's valid number for the country of profile, or for the |locale| given
-// as a fall back, return the number in internaional format; otherwise return
+// If it's a valid number for the profile's country or for the |locale| given
+// as a fallback, returns the number in international format; otherwise returns
 // the raw number string from profile.
 base::string16 GetFormattedPhoneNumberForDisplay(const AutofillProfile& profile,
                                                  const std::string& locale);
 
+// Returns |phone_number| in i18n::phonenumbers::PhoneNumberUtil::
+// PhoneNumberFormat::NATIONAL format if the number is valid for
+// |country_code|. Otherwise, returns the given |phone_number|.
+std::string FormatPhoneNationallyForDisplay(const std::string& phone_number,
+                                            const std::string& country_code);
+
 // Formats the given number |phone_number| to
 // i18n::phonenumbers::PhoneNumberUtil::PhoneNumberFormat::INTERNATIONAL format
 // by using i18n::phonenumbers::PhoneNumberUtil::Format.
diff --git a/components/autofill/core/browser/strike_database.cc b/components/autofill/core/browser/strike_database.cc
index 46a41fb3..4100250 100644
--- a/components/autofill/core/browser/strike_database.cc
+++ b/components/autofill/core/browser/strike_database.cc
@@ -217,4 +217,8 @@
   strike_map_cache_[key] = data;
 }
 
+std::string StrikeDatabase::GetPrefixFromKey(const std::string& key) {
+  return key.substr(0, key.find(kKeyDeliminator));
+}
+
 }  // namespace autofill
diff --git a/components/autofill/core/browser/strike_database.h b/components/autofill/core/browser/strike_database.h
index 1352dcd..8b8915d 100644
--- a/components/autofill/core/browser/strike_database.h
+++ b/components/autofill/core/browser/strike_database.h
@@ -157,6 +157,9 @@
   // Sets the entry for |key| in |strike_map_cache_| to |data|.
   void UpdateCache(const std::string& key, const StrikeData& data);
 
+  // Extracts per-project prefix from |key|.
+  std::string GetPrefixFromKey(const std::string& key);
+
   base::WeakPtrFactory<StrikeDatabase> weak_ptr_factory_;
 };
 
diff --git a/components/autofill/core/browser/strike_database_integrator_base.cc b/components/autofill/core/browser/strike_database_integrator_base.cc
index e3f3fd48..5b04261 100644
--- a/components/autofill/core/browser/strike_database_integrator_base.cc
+++ b/components/autofill/core/browser/strike_database_integrator_base.cc
@@ -78,8 +78,13 @@
     if (AutofillClock::Now().ToDeltaSinceWindowsEpoch().InMicroseconds() -
             entry.second.last_update_timestamp() >
         GetExpiryTimeMicros()) {
-      if (strike_database_->GetStrikes(entry.first) > 0)
+      if (strike_database_->GetStrikes(entry.first) > 0) {
         expired_keys.push_back(entry.first);
+        base::UmaHistogramCounts1000(
+            "Autofill.StrikeDatabase.StrikesPresentWhenStrikeExpired." +
+                strike_database_->GetPrefixFromKey(entry.first),
+            strike_database_->GetStrikes(entry.first));
+      }
     }
   }
   for (std::string key : expired_keys) {
diff --git a/components/autofill/core/browser/strike_database_integrator_base.h b/components/autofill/core/browser/strike_database_integrator_base.h
index 75229c5..3e1bf565 100644
--- a/components/autofill/core/browser/strike_database_integrator_base.h
+++ b/components/autofill/core/browser/strike_database_integrator_base.h
@@ -66,6 +66,8 @@
                            GetKeyForStrikeDatabaseIntegratorUniqueIdTest);
   FRIEND_TEST_ALL_PREFIXES(StrikeDatabaseIntegratorTestStrikeDatabaseTest,
                            RemoveExpiredStrikesUniqueIdTest);
+  FRIEND_TEST_ALL_PREFIXES(StrikeDatabaseIntegratorTestStrikeDatabaseTest,
+                           RemoveExpiredStrikesTestLogsUMA);
   friend class SaveCardInfobarEGTestHelper;
   friend class StrikeDatabaseTest;
   friend class StrikeDatabaseTester;
diff --git a/components/autofill/core/browser/strike_database_integrator_test_strike_database_unittest.cc b/components/autofill/core/browser/strike_database_integrator_test_strike_database_unittest.cc
index dee8a266..a6e961b 100644
--- a/components/autofill/core/browser/strike_database_integrator_test_strike_database_unittest.cc
+++ b/components/autofill/core/browser/strike_database_integrator_test_strike_database_unittest.cc
@@ -123,6 +123,50 @@
 }
 
 TEST_F(StrikeDatabaseIntegratorTestStrikeDatabaseTest,
+       RemoveExpiredStrikesTestLogsUMA) {
+  autofill::TestAutofillClock test_clock;
+  test_clock.SetNow(AutofillClock::Now());
+  strike_database_.AddStrikes(2);
+  EXPECT_EQ(2, strike_database_.GetStrikes());
+
+  // Advance clock to past expiry time.
+  test_clock.Advance(base::TimeDelta::FromMicroseconds(
+      strike_database_.GetExpiryTimeMicros() + 1));
+
+  // One strike should be removed.
+  strike_database_.RemoveExpiredStrikes();
+  EXPECT_EQ(1, strike_database_.GetStrikes());
+
+  // Strike count is past the max limit.
+  strike_database_.AddStrikes(10);
+  EXPECT_EQ(11, strike_database_.GetStrikes());
+
+  // Advance clock to past expiry time.
+  test_clock.Advance(base::TimeDelta::FromMicroseconds(
+      strike_database_.GetExpiryTimeMicros() + 1));
+
+  // Strike count should be one less than the max limit.
+  strike_database_.RemoveExpiredStrikes();
+  EXPECT_EQ(5, strike_database_.GetStrikes());
+
+  std::vector<base::Bucket> buckets = GetHistogramTester()->GetAllSamples(
+      "Autofill.StrikeDatabase.StrikesPresentWhenStrikeExpired."
+      "StrikeDatabaseIntegratorTest");
+  // There should be two buckets, for strike counts of 2 and 11.
+  ASSERT_EQ(2U, buckets.size());
+  // Bucket for 2 strikes should have count of 1.
+  GetHistogramTester()->ExpectBucketCount(
+      "Autofill.StrikeDatabase.StrikesPresentWhenStrikeExpired."
+      "StrikeDatabaseIntegratorTest",
+      2, 1);
+  // Bucket for 11 strikes should have count of 1.
+  GetHistogramTester()->ExpectBucketCount(
+      "Autofill.StrikeDatabase.StrikesPresentWhenStrikeExpired."
+      "StrikeDatabaseIntegratorTest",
+      11, 1);
+}
+
+TEST_F(StrikeDatabaseIntegratorTestStrikeDatabaseTest,
        GetKeyForStrikeDatabaseIntegratorUniqueIdTest) {
   strike_database_.SetUniqueIdsRequired(true);
   const std::string unique_id = "1234";
diff --git a/components/autofill/core/browser/webdata/autofill_profile_sync_bridge.cc b/components/autofill/core/browser/webdata/autofill_profile_sync_bridge.cc
index 7acd59b..a5047b4 100644
--- a/components/autofill/core/browser/webdata/autofill_profile_sync_bridge.cc
+++ b/components/autofill/core/browser/webdata/autofill_profile_sync_bridge.cc
@@ -49,20 +49,6 @@
 // Address to this variable used as the user data key.
 static int kAutofillProfileSyncBridgeUserDataKey = 0;
 
-// TODO(crbug.com/904390): Remove when the investigation is over.
-base::Optional<AutofillProfile> FindLocalProfileByStorageKey(
-    const std::string& storage_key,
-    AutofillTable* table) {
-  std::vector<std::unique_ptr<AutofillProfile>> local_profiles;
-  table->GetAutofillProfiles(&local_profiles);
-  for (const auto& local_profile : local_profiles) {
-    if (storage_key == GetStorageKeyFromAutofillProfile(*local_profile)) {
-      return *local_profile;
-    }
-  }
-  return base::nullopt;
-}
-
 }  // namespace
 
 // static
@@ -232,19 +218,18 @@
           GetAutofillTable(), syncer::AUTOFILL_PROFILE);
 
   // TODO(crbug.com/904390): Remove when the investigation is over.
-  base::Optional<AutofillProfile> local_profile;
-  if (change.type() == AutofillProfileChange::REMOVE) {
-    local_profile =
-        FindLocalProfileByStorageKey(change.key(), GetAutofillTable());
-  } else {
-    local_profile = *change.data_model();
-  }
-  std::vector<std::unique_ptr<AutofillProfile>> server_profiles;
-  GetAutofillTable()->GetServerProfiles(&server_profiles);
   bool is_converted_from_server = false;
-  if (local_profile != base::nullopt) {
+  if (change.type() == AutofillProfileChange::REMOVE) {
+    // The profile is not available any more so we cannot compare its value,
+    // instead we use a rougher test based on the id - whether it is a local
+    // GUID or a server id. As a result, it has a different semantics compared
+    // to AddOrUpdate.
+    is_converted_from_server = !base::IsValidGUID(change.key());
+  } else {
+    std::vector<std::unique_ptr<AutofillProfile>> server_profiles;
+    GetAutofillTable()->GetServerProfiles(&server_profiles);
     is_converted_from_server = IsLocalProfileEqualToServerProfile(
-        server_profiles, *local_profile, app_locale_);
+        server_profiles, *change.data_model(), app_locale_);
   }
 
   switch (change.type()) {
@@ -271,13 +256,10 @@
       change_processor()->Delete(change.key(), metadata_change_list.get());
 
       // TODO(crbug.com/904390): Remove when the investigation is over.
-      if (local_profile != base::nullopt) {
-        // Report only if we delete an existing entity.
-        ReportAutofillProfileDeleteOrigin(
-            is_converted_from_server
-                ? AutofillProfileSyncChangeOrigin::kConvertedLocal
-                : AutofillProfileSyncChangeOrigin::kTrulyLocal);
-      }
+      ReportAutofillProfileDeleteOrigin(
+          is_converted_from_server
+              ? AutofillProfileSyncChangeOrigin::kConvertedLocal
+              : AutofillProfileSyncChangeOrigin::kTrulyLocal);
       break;
     case AutofillProfileChange::EXPIRE:
       // EXPIRE changes are not being issued for profiles.
diff --git a/components/autofill/core/browser/webdata/autofill_profile_syncable_service.cc b/components/autofill/core/browser/webdata/autofill_profile_syncable_service.cc
index f3c90a6d..8022c6a 100644
--- a/components/autofill/core/browser/webdata/autofill_profile_syncable_service.cc
+++ b/components/autofill/core/browser/webdata/autofill_profile_syncable_service.cc
@@ -614,21 +614,21 @@
   }
 
   // TODO(crbug.com/904390): Remove when the investigation is over.
-  const AutofillProfile* local_profile = nullptr;
-  if (change.type() == AutofillProfileChange::REMOVE) {
-    if (profiles_map_.find(change.key()) != profiles_map_.end()) {
-      local_profile = profiles_map_[change.key()];
-    }
-  } else {
-    local_profile = change.data_model();
-  }
   bool is_converted_from_server = false;
-  // |webdata_backend_| may be null in unit-tests.
-  if (local_profile != nullptr && webdata_backend_ != nullptr) {
-    std::vector<std::unique_ptr<AutofillProfile>> server_profiles;
-    GetAutofillTable()->GetServerProfiles(&server_profiles);
-    is_converted_from_server = IsLocalProfileEqualToServerProfile(
-        server_profiles, *local_profile, app_locale_);
+  if (change.type() == AutofillProfileChange::REMOVE) {
+    // The profile is not available any more so we cannot compare its value,
+    // instead we use a rougher test based on the id - whether it is a local
+    // GUID or a server id. As a result, it has a different semantics compared
+    // to AddOrUpdate.
+    is_converted_from_server = !base::IsValidGUID(change.key());
+  } else {
+    // |webdata_backend_|, used by GetAutofillTable() may be null in unit-tests.
+    if (webdata_backend_ != nullptr) {
+      std::vector<std::unique_ptr<AutofillProfile>> server_profiles;
+      GetAutofillTable()->GetServerProfiles(&server_profiles);
+      is_converted_from_server = IsLocalProfileEqualToServerProfile(
+          server_profiles, *change.data_model(), app_locale_);
+    }
   }
 
   syncer::SyncChangeList new_changes;
diff --git a/components/autofill/core/browser/webdata/autofill_sync_bridge_test_util.cc b/components/autofill/core/browser/webdata/autofill_sync_bridge_test_util.cc
index 593ce4b..5a9777eb 100644
--- a/components/autofill/core/browser/webdata/autofill_sync_bridge_test_util.cc
+++ b/components/autofill/core/browser/webdata/autofill_sync_bridge_test_util.cc
@@ -58,4 +58,4 @@
   return wallet_specifics;
 }
 
-}  // namespace autofill
\ No newline at end of file
+}  // namespace autofill
diff --git a/components/autofill/core/browser/webdata/autofill_wallet_metadata_sync_bridge_unittest.cc b/components/autofill/core/browser/webdata/autofill_wallet_metadata_sync_bridge_unittest.cc
index 693881d..a932237 100644
--- a/components/autofill/core/browser/webdata/autofill_wallet_metadata_sync_bridge_unittest.cc
+++ b/components/autofill/core/browser/webdata/autofill_wallet_metadata_sync_bridge_unittest.cc
@@ -81,6 +81,9 @@
 const char kAddr1SyncTag[] = "address-YWRkcjHvv74=";
 const char kCard1SyncTag[] = "card-Y2FyZDHvv74=";
 
+const char kLocalAddr1ServerId[] = "e171e3ed-858a-4dd5-9bf3-8517f14ba5fc";
+const char kLocalAddr2ServerId[] = "fa232b9a-f248-4e5a-8d76-d46f821c0c5f";
+
 const char kLocaleString[] = "en-US";
 const base::Time kJune2017 = base::Time::FromDoubleT(1497552271);
 
@@ -103,17 +106,18 @@
       WalletMetadataSpecifics::CARD, specifics_id);
 }
 
-WalletMetadataSpecifics CreateWalletMetadataSpecificsForAddressWithUseStats(
+WalletMetadataSpecifics CreateWalletMetadataSpecificsForAddressWithDetails(
     const std::string& specifics_id,
     size_t use_count,
-    int64_t use_date) {
+    int64_t use_date,
+    bool has_converted = false) {
   WalletMetadataSpecifics specifics;
   specifics.set_id(specifics_id);
   specifics.set_type(WalletMetadataSpecifics::ADDRESS);
   specifics.set_use_count(use_count);
   specifics.set_use_date(use_date);
-  // Set the default value according to the constructor of AutofillProfile.
-  specifics.set_address_has_converted(false);
+  // False is the default value according to the constructor of AutofillProfile.
+  specifics.set_address_has_converted(has_converted);
   return specifics;
 }
 
@@ -121,22 +125,24 @@
     const std::string& specifics_id) {
   // Set default values according to the constructor of AutofillProfile (the
   // clock value is overrided by TestAutofillClock in the test fixture).
-  return CreateWalletMetadataSpecificsForAddressWithUseStats(
+  return CreateWalletMetadataSpecificsForAddressWithDetails(
       specifics_id, /*use_count=*/1,
       /*use_date=*/UseDateToProtoValue(kJune2017));
 }
 
-WalletMetadataSpecifics CreateWalletMetadataSpecificsForCardWithUseStats(
+WalletMetadataSpecifics CreateWalletMetadataSpecificsForCardWithDetails(
     const std::string& specifics_id,
     size_t use_count,
-    int64_t use_date) {
+    int64_t use_date,
+    const std::string& billing_address_id = "") {
   WalletMetadataSpecifics specifics;
   specifics.set_id(specifics_id);
   specifics.set_type(WalletMetadataSpecifics::CARD);
   specifics.set_use_count(use_count);
   specifics.set_use_date(use_date);
-  // Set the default value according to the constructor of AutofillProfile.
-  specifics.set_card_billing_address_id("");
+  // "" is the default value according to the constructor of AutofillProfile;
+  // this field is Base64 encoded in the protobuf.
+  specifics.set_card_billing_address_id(GetBase64EncodedId(billing_address_id));
   return specifics;
 }
 
@@ -144,29 +150,49 @@
     const std::string& specifics_id) {
   // Set default values according to the constructor of AutofillProfile (the
   // clock value is overrided by TestAutofillClock in the test fixture).
-  return CreateWalletMetadataSpecificsForCardWithUseStats(
+  return CreateWalletMetadataSpecificsForCardWithDetails(
       specifics_id, /*use_count=*/1,
       /*use_date=*/UseDateToProtoValue(kJune2017));
 }
 
-AutofillProfile CreateServerProfileWithUseStats(const std::string& server_id,
-                                                size_t use_count,
-                                                int64_t use_date) {
+AutofillProfile CreateServerProfileWithDetails(const std::string& server_id,
+                                               size_t use_count,
+                                               int64_t use_date,
+                                               bool has_converted = false) {
   AutofillProfile profile = CreateServerProfile(server_id);
   profile.set_use_count(use_count);
   profile.set_use_date(UseDateFromProtoValue(use_date));
+  profile.set_has_converted(has_converted);
   return profile;
 }
 
-CreditCard CreateServerCreditCardWithUseStats(const std::string& server_id,
-                                              size_t use_count,
-                                              int64_t use_date) {
+CreditCard CreateServerCreditCardWithDetails(
+    const std::string& server_id,
+    size_t use_count,
+    int64_t use_date,
+    const std::string& billing_address_id = "") {
   CreditCard card = CreateServerCreditCard(server_id);
   card.set_use_count(use_count);
   card.set_use_date(UseDateFromProtoValue(use_date));
+  card.set_billing_address_id(billing_address_id);
   return card;
 }
 
+AutofillProfile CreateServerProfileFromSpecifics(
+    const WalletMetadataSpecifics& specifics) {
+  return CreateServerProfileWithDetails(
+      GetBase64DecodedId(specifics.id()), specifics.use_count(),
+      specifics.use_date(), specifics.address_has_converted());
+}
+
+CreditCard CreateServerCreditCardFromSpecifics(
+    const WalletMetadataSpecifics& specifics) {
+  return CreateServerCreditCardWithDetails(
+      GetBase64DecodedId(specifics.id()), specifics.use_count(),
+      specifics.use_date(),
+      GetBase64DecodedId(specifics.card_billing_address_id()));
+}
+
 void ExtractWalletMetadataSpecificsFromDataBatch(
     std::unique_ptr<DataBatch> batch,
     std::vector<WalletMetadataSpecifics>* output) {
@@ -182,7 +208,7 @@
   output << "[id: " << specifics.id()
          << ", type: " << static_cast<int>(specifics.type())
          << ", use_count: " << specifics.use_count()
-         << ", use_date: " << UseDateFromProtoValue(specifics.use_date())
+         << ", use_date: " << specifics.use_date()
          << ", card_billing_address_id: "
          << (specifics.has_card_billing_address_id()
                  ? specifics.card_billing_address_id()
@@ -256,11 +282,47 @@
     mock_processor_.DelegateCallsByDefaultTo(real_processor_.get());
   }
 
-  void ResetBridge() {
+  void ResetBridge(bool initial_sync_done = true) {
+    sync_pb::ModelTypeState model_type_state;
+    model_type_state.set_initial_sync_done(initial_sync_done);
+    EXPECT_TRUE(table()->UpdateModelTypeState(syncer::AUTOFILL_WALLET_METADATA,
+                                              model_type_state));
     bridge_.reset(new AutofillWalletMetadataSyncBridge(
         mock_processor_.CreateForwardingProcessor(), &backend_));
   }
 
+  void StartSyncing(
+      const std::vector<WalletMetadataSpecifics>& remote_data = {}) {
+    base::RunLoop loop;
+    syncer::DataTypeActivationRequest request;
+    request.error_handler = base::DoNothing();
+    real_processor_->OnSyncStarting(
+        request,
+        base::BindLambdaForTesting(
+            [&loop](std::unique_ptr<syncer::DataTypeActivationResponse>) {
+              loop.Quit();
+            }));
+    loop.Run();
+
+    ReceiveUpdates(remote_data);
+  }
+
+  void ReceiveUpdates(
+      const std::vector<WalletMetadataSpecifics>& remote_data = {}) {
+    // Make sure each update has an updated response version so that it does not
+    // get filtered out as reflection by the processor.
+    ++response_version;
+    // After this update initial sync is for sure done.
+    sync_pb::ModelTypeState state;
+    state.set_initial_sync_done(true);
+
+    syncer::UpdateResponseDataList updates;
+    for (const WalletMetadataSpecifics& specifics : remote_data) {
+      updates.push_back(SpecificsToUpdateResponse(specifics));
+    }
+    real_processor_->OnUpdateReceived(state, updates);
+  }
+
   EntityData SpecificsToEntity(const WalletMetadataSpecifics& specifics) {
     EntityData data;
     *data.specifics.mutable_wallet_metadata() = specifics;
@@ -269,6 +331,14 @@
     return data;
   }
 
+  syncer::UpdateResponseData SpecificsToUpdateResponse(
+      const WalletMetadataSpecifics& specifics) {
+    syncer::UpdateResponseData data;
+    data.entity = SpecificsToEntity(specifics).PassToPtr();
+    data.response_version = response_version;
+    return data;
+  }
+
   std::vector<WalletMetadataSpecifics> GetAllLocalData() {
     std::vector<WalletMetadataSpecifics> data;
     // Perform an async call synchronously for testing.
@@ -321,6 +391,7 @@
   MockAutofillWebDataBackend* backend() { return &backend_; }
 
  private:
+  int response_version = 0;
   autofill::TestAutofillClock test_clock_;
   ScopedTempDir temp_dir_;
   base::test::ScopedTaskEnvironment scoped_task_environment_;
@@ -449,15 +520,15 @@
 // local metadata is updated.
 TEST_F(AutofillWalletMetadataSyncBridgeTest,
        DontSendLowerValueToServerOnUpdate) {
-  table()->SetServerProfiles({CreateServerProfileWithUseStats(
+  table()->SetServerProfiles({CreateServerProfileWithDetails(
       kAddr1ServerId, /*use_count=*/2, /*use_date=*/5)});
-  table()->SetServerCreditCards({CreateServerCreditCardWithUseStats(
+  table()->SetServerCreditCards({CreateServerCreditCardWithDetails(
       kCard1ServerId, /*use_count=*/3, /*use_date=*/6)});
   ResetBridge();
 
-  AutofillProfile updated_profile = CreateServerProfileWithUseStats(
+  AutofillProfile updated_profile = CreateServerProfileWithDetails(
       kAddr1ServerId, /*use_count=*/1, /*use_date=*/4);
-  CreditCard updated_card = CreateServerCreditCardWithUseStats(
+  CreditCard updated_card = CreateServerCreditCardWithDetails(
       kCard1ServerId, /*use_count=*/2, /*use_date=*/5);
 
   EXPECT_CALL(mock_processor(), Put(_, _, _)).Times(0);
@@ -472,9 +543,9 @@
   EXPECT_THAT(
       GetAllLocalDataInclRestart(),
       UnorderedElementsAre(
-          EqualsSpecifics(CreateWalletMetadataSpecificsForAddressWithUseStats(
+          EqualsSpecifics(CreateWalletMetadataSpecificsForAddressWithDetails(
               kAddr1SpecificsId, /*use_count=*/2, /*use_date=*/5)),
-          EqualsSpecifics(CreateWalletMetadataSpecificsForCardWithUseStats(
+          EqualsSpecifics(CreateWalletMetadataSpecificsForCardWithDetails(
               kCard1SpecificsId, /*use_count=*/3, /*use_date=*/6))));
 }
 
@@ -484,15 +555,15 @@
 // for data that is already there).
 TEST_F(AutofillWalletMetadataSyncBridgeTest,
        DontSendLowerValueToServerOnCreation) {
-  table()->SetServerProfiles({CreateServerProfileWithUseStats(
+  table()->SetServerProfiles({CreateServerProfileWithDetails(
       kAddr1ServerId, /*use_count=*/2, /*use_date=*/5)});
-  table()->SetServerCreditCards({CreateServerCreditCardWithUseStats(
+  table()->SetServerCreditCards({CreateServerCreditCardWithDetails(
       kCard1ServerId, /*use_count=*/3, /*use_date=*/6)});
   ResetBridge();
 
-  AutofillProfile updated_profile = CreateServerProfileWithUseStats(
+  AutofillProfile updated_profile = CreateServerProfileWithDetails(
       kAddr1ServerId, /*use_count=*/1, /*use_date=*/4);
-  CreditCard updated_card = CreateServerCreditCardWithUseStats(
+  CreditCard updated_card = CreateServerCreditCardWithDetails(
       kCard1ServerId, /*use_count=*/2, /*use_date=*/5);
 
   EXPECT_CALL(mock_processor(), Put(_, _, _)).Times(0);
@@ -507,9 +578,9 @@
   EXPECT_THAT(
       GetAllLocalDataInclRestart(),
       UnorderedElementsAre(
-          EqualsSpecifics(CreateWalletMetadataSpecificsForAddressWithUseStats(
+          EqualsSpecifics(CreateWalletMetadataSpecificsForAddressWithDetails(
               kAddr1SpecificsId, /*use_count=*/2, /*use_date=*/5)),
-          EqualsSpecifics(CreateWalletMetadataSpecificsForCardWithUseStats(
+          EqualsSpecifics(CreateWalletMetadataSpecificsForCardWithDetails(
               kCard1SpecificsId, /*use_count=*/3, /*use_date=*/6))));
 }
 
@@ -517,22 +588,22 @@
 // metadata is updated.
 TEST_F(AutofillWalletMetadataSyncBridgeTest,
        SendHigherValuesToServerOnLocalUpdate) {
-  table()->SetServerProfiles({CreateServerProfileWithUseStats(
+  table()->SetServerProfiles({CreateServerProfileWithDetails(
       kAddr1ServerId, /*use_count=*/1, /*use_date=*/2)});
-  table()->SetServerCreditCards({CreateServerCreditCardWithUseStats(
+  table()->SetServerCreditCards({CreateServerCreditCardWithDetails(
       kCard1ServerId, /*use_count=*/3, /*use_date=*/4)});
   ResetBridge();
 
-  AutofillProfile updated_profile = CreateServerProfileWithUseStats(
+  AutofillProfile updated_profile = CreateServerProfileWithDetails(
       kAddr1ServerId, /*use_count=*/10, /*use_date=*/20);
-  CreditCard updated_card = CreateServerCreditCardWithUseStats(
+  CreditCard updated_card = CreateServerCreditCardWithDetails(
       kCard1ServerId, /*use_count=*/30, /*use_date=*/40);
 
   WalletMetadataSpecifics expected_profile_specifics =
-      CreateWalletMetadataSpecificsForAddressWithUseStats(
+      CreateWalletMetadataSpecificsForAddressWithDetails(
           kAddr1SpecificsId, /*use_count=*/10, /*use_date=*/20);
   WalletMetadataSpecifics expected_card_specifics =
-      CreateWalletMetadataSpecificsForCardWithUseStats(
+      CreateWalletMetadataSpecificsForCardWithDetails(
           kCard1SpecificsId, /*use_count=*/30, /*use_date=*/40);
 
   EXPECT_CALL(
@@ -557,16 +628,16 @@
 TEST_F(AutofillWalletMetadataSyncBridgeTest,
        SendNewDataToServerOnLocalAddition) {
   ResetBridge();
-  AutofillProfile new_profile = CreateServerProfileWithUseStats(
+  AutofillProfile new_profile = CreateServerProfileWithDetails(
       kAddr1ServerId, /*use_count=*/10, /*use_date=*/20);
-  CreditCard new_card = CreateServerCreditCardWithUseStats(
+  CreditCard new_card = CreateServerCreditCardWithDetails(
       kCard1ServerId, /*use_count=*/30, /*use_date=*/40);
 
   WalletMetadataSpecifics expected_profile_specifics =
-      CreateWalletMetadataSpecificsForAddressWithUseStats(
+      CreateWalletMetadataSpecificsForAddressWithDetails(
           kAddr1SpecificsId, /*use_count=*/10, /*use_date=*/20);
   WalletMetadataSpecifics expected_card_specifics =
-      CreateWalletMetadataSpecificsForCardWithUseStats(
+      CreateWalletMetadataSpecificsForCardWithDetails(
           kCard1SpecificsId, /*use_count=*/30, /*use_date=*/40);
 
   EXPECT_CALL(
@@ -591,16 +662,16 @@
 // recreates metadata that may get deleted in the mean-time).
 TEST_F(AutofillWalletMetadataSyncBridgeTest, SendNewDataToServerOnLocalUpdate) {
   ResetBridge();
-  AutofillProfile new_profile = CreateServerProfileWithUseStats(
+  AutofillProfile new_profile = CreateServerProfileWithDetails(
       kAddr1ServerId, /*use_count=*/10, /*use_date=*/20);
-  CreditCard new_card = CreateServerCreditCardWithUseStats(
+  CreditCard new_card = CreateServerCreditCardWithDetails(
       kCard1ServerId, /*use_count=*/30, /*use_date=*/40);
 
   WalletMetadataSpecifics expected_profile_specifics =
-      CreateWalletMetadataSpecificsForAddressWithUseStats(
+      CreateWalletMetadataSpecificsForAddressWithDetails(
           kAddr1SpecificsId, /*use_count=*/10, /*use_date=*/20);
   WalletMetadataSpecifics expected_card_specifics =
-      CreateWalletMetadataSpecificsForCardWithUseStats(
+      CreateWalletMetadataSpecificsForCardWithDetails(
           kCard1SpecificsId, /*use_count=*/30, /*use_date=*/40);
 
   EXPECT_CALL(
@@ -623,9 +694,9 @@
 // Verify that one-off deletion of existing metadata is sent to the sync server.
 TEST_F(AutofillWalletMetadataSyncBridgeTest,
        DeleteExistingDataFromServerOnLocalDeletion) {
-  AutofillProfile existing_profile = CreateServerProfileWithUseStats(
+  AutofillProfile existing_profile = CreateServerProfileWithDetails(
       kAddr1ServerId, /*use_count=*/10, /*use_date=*/20);
-  CreditCard existing_card = CreateServerCreditCardWithUseStats(
+  CreditCard existing_card = CreateServerCreditCardWithDetails(
       kCard1ServerId, /*use_count=*/30, /*use_date=*/40);
   table()->SetServerProfiles({existing_profile});
   table()->SetServerCreditCards({existing_card});
@@ -646,9 +717,9 @@
 // Verify that deletion of non-existing metadata is not sent to the sync server.
 TEST_F(AutofillWalletMetadataSyncBridgeTest,
        DoNotDeleteNonExistingDataFromServerOnLocalDeletion) {
-  AutofillProfile existing_profile = CreateServerProfileWithUseStats(
+  AutofillProfile existing_profile = CreateServerProfileWithDetails(
       kAddr1ServerId, /*use_count=*/10, /*use_date=*/20);
-  CreditCard existing_card = CreateServerCreditCardWithUseStats(
+  CreditCard existing_card = CreateServerCreditCardWithDetails(
       kCard1ServerId, /*use_count=*/30, /*use_date=*/40);
   // Save only data and not metadata.
   table()->SetServerAddressesData({existing_profile});
@@ -659,7 +730,6 @@
   ASSERT_THAT(GetAllLocalDataInclRestart(), IsEmpty());
 
   EXPECT_CALL(mock_processor(), Delete(_, _)).Times(0);
-  EXPECT_CALL(mock_processor(), Delete(_, _)).Times(0);
 
   bridge()->AutofillProfileChanged(AutofillProfileChange(
       AutofillProfileChange::REMOVE, existing_profile.server_id(), nullptr));
@@ -670,4 +740,639 @@
   EXPECT_THAT(GetAllLocalDataInclRestart(), IsEmpty());
 }
 
+enum RemoteChangesMode {
+  INITIAL_SYNC_ADD,  // Initial sync -> ADD changes.
+  LATER_SYNC_ADD,    // Later sync; the client receives the data for the first
+                     // time -> ADD changes.
+  LATER_SYNC_UPDATE  // Later sync; the client has received the data before ->
+                     // UPDATE changes.
+};
+
+// Parametrized fixture for tests that apply in the same way for all
+// RemoteChangesModes.
+class AutofillWalletMetadataSyncBridgeRemoteChangesTest
+    : public testing::WithParamInterface<RemoteChangesMode>,
+      public AutofillWalletMetadataSyncBridgeTest {
+ public:
+  AutofillWalletMetadataSyncBridgeRemoteChangesTest() {}
+  ~AutofillWalletMetadataSyncBridgeRemoteChangesTest() override {}
+
+  void ResetBridgeWithPotentialInitialSync(
+      const std::vector<WalletMetadataSpecifics>& remote_data) {
+    AutofillWalletMetadataSyncBridgeTest::ResetBridge(
+        /*initial_sync_done=*/GetParam() != INITIAL_SYNC_ADD);
+
+    if (GetParam() == LATER_SYNC_UPDATE) {
+      StartSyncing(remote_data);
+    }
+  }
+
+  void ReceivePotentiallyInitialUpdates(
+      const std::vector<WalletMetadataSpecifics>& remote_data = {}) {
+    if (GetParam() != LATER_SYNC_UPDATE) {
+      StartSyncing(remote_data);
+    } else {
+      AutofillWalletMetadataSyncBridgeTest::ReceiveUpdates(remote_data);
+    }
+  }
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(AutofillWalletMetadataSyncBridgeRemoteChangesTest);
+};
+
+// No upstream communication or local DB change happens if the server sends an
+// empty update.
+TEST_P(AutofillWalletMetadataSyncBridgeRemoteChangesTest, EmptyUpdateIgnored) {
+  EXPECT_CALL(mock_processor(), Delete(_, _)).Times(0);
+  EXPECT_CALL(mock_processor(), Put(_, _, _)).Times(0);
+
+  ResetBridgeWithPotentialInitialSync({});
+  ReceivePotentiallyInitialUpdates({});
+
+  EXPECT_THAT(GetAllLocalDataInclRestart(), IsEmpty());
+}
+
+// No upstream communication or local DB change happens if the server sends the
+// same data as we have locally.
+TEST_P(AutofillWalletMetadataSyncBridgeRemoteChangesTest, SameDataIgnored) {
+  WalletMetadataSpecifics profile =
+      CreateWalletMetadataSpecificsForAddressWithDetails(
+          kAddr1SpecificsId, /*use_count=*/2, /*use_date=*/5);
+  WalletMetadataSpecifics card =
+      CreateWalletMetadataSpecificsForCardWithDetails(
+          kCard1SpecificsId, /*use_count=*/3, /*use_date=*/6);
+
+  table()->SetServerProfiles({CreateServerProfileFromSpecifics(profile)});
+  table()->SetServerCreditCards({CreateServerCreditCardFromSpecifics(card)});
+  ResetBridgeWithPotentialInitialSync({profile, card});
+
+  EXPECT_CALL(mock_processor(), Delete(_, _)).Times(0);
+  EXPECT_CALL(mock_processor(), Put(_, _, _)).Times(0);
+
+  ReceivePotentiallyInitialUpdates({profile, card});
+
+  EXPECT_THAT(
+      GetAllLocalDataInclRestart(),
+      UnorderedElementsAre(EqualsSpecifics(profile), EqualsSpecifics(card)));
+}
+
+// Tests that if the remote use stats are higher / newer, they should win over
+// local stats.
+TEST_P(AutofillWalletMetadataSyncBridgeRemoteChangesTest,
+       Conflict_PreferHigherValues_RemoteWins) {
+  WalletMetadataSpecifics profile =
+      CreateWalletMetadataSpecificsForAddressWithDetails(
+          kAddr1SpecificsId, /*use_count=*/2, /*use_date=*/5);
+  WalletMetadataSpecifics card =
+      CreateWalletMetadataSpecificsForCardWithDetails(
+          kCard1SpecificsId, /*use_count=*/3, /*use_date=*/6);
+
+  table()->SetServerProfiles({CreateServerProfileFromSpecifics(profile)});
+  table()->SetServerCreditCards({CreateServerCreditCardFromSpecifics(card)});
+  ResetBridgeWithPotentialInitialSync({profile, card});
+
+  WalletMetadataSpecifics updated_remote_profile =
+      CreateWalletMetadataSpecificsForAddressWithDetails(
+          kAddr1SpecificsId, /*use_count=*/20, /*use_date=*/50);
+  WalletMetadataSpecifics updated_remote_card =
+      CreateWalletMetadataSpecificsForCardWithDetails(
+          kCard1SpecificsId, /*use_count=*/30, /*use_date=*/60);
+
+  EXPECT_CALL(mock_processor(), Delete(_, _)).Times(0);
+  EXPECT_CALL(mock_processor(), Put(_, _, _)).Times(0);
+
+  ReceivePotentiallyInitialUpdates(
+      {updated_remote_profile, updated_remote_card});
+
+  EXPECT_THAT(GetAllLocalDataInclRestart(),
+              UnorderedElementsAre(EqualsSpecifics(updated_remote_profile),
+                                   EqualsSpecifics(updated_remote_card)));
+}
+
+// Tests that if the local use stats are higher / newer, they should win over
+// remote stats.
+TEST_P(AutofillWalletMetadataSyncBridgeRemoteChangesTest,
+       Conflict_PreferHigherValues_LocalWins) {
+  WalletMetadataSpecifics profile =
+      CreateWalletMetadataSpecificsForAddressWithDetails(
+          kAddr1SpecificsId, /*use_count=*/20, /*use_date=*/50);
+  WalletMetadataSpecifics card =
+      CreateWalletMetadataSpecificsForCardWithDetails(
+          kCard1SpecificsId, /*use_count=*/30, /*use_date=*/60);
+
+  table()->SetServerProfiles({CreateServerProfileFromSpecifics(profile)});
+  table()->SetServerCreditCards({CreateServerCreditCardFromSpecifics(card)});
+  ResetBridgeWithPotentialInitialSync({profile, card});
+
+  WalletMetadataSpecifics updated_remote_profile =
+      CreateWalletMetadataSpecificsForAddressWithDetails(
+          kAddr1SpecificsId, /*use_count=*/2, /*use_date=*/5);
+  WalletMetadataSpecifics updated_remote_card =
+      CreateWalletMetadataSpecificsForCardWithDetails(
+          kCard1SpecificsId, /*use_count=*/3, /*use_date=*/6);
+
+  EXPECT_CALL(mock_processor(), Delete(_, _)).Times(0);
+  EXPECT_CALL(mock_processor(),
+              Put(kAddr1StorageKey, HasSpecifics(profile), _));
+  EXPECT_CALL(mock_processor(), Put(kCard1StorageKey, HasSpecifics(card), _));
+
+  ReceivePotentiallyInitialUpdates(
+      {updated_remote_profile, updated_remote_card});
+
+  EXPECT_THAT(
+      GetAllLocalDataInclRestart(),
+      UnorderedElementsAre(EqualsSpecifics(profile), EqualsSpecifics(card)));
+}
+
+// Tests that the conflicts are resolved component-wise (a higher use_count is
+// taken from local data, a newer use_data is taken from remote data).
+TEST_P(AutofillWalletMetadataSyncBridgeRemoteChangesTest,
+       Conflict_PreferHigherValues_BothWin1) {
+  WalletMetadataSpecifics profile =
+      CreateWalletMetadataSpecificsForAddressWithDetails(
+          kAddr1SpecificsId, /*use_count=*/20, /*use_date=*/5);
+  WalletMetadataSpecifics card =
+      CreateWalletMetadataSpecificsForCardWithDetails(
+          kCard1SpecificsId, /*use_count=*/30, /*use_date=*/6);
+
+  table()->SetServerProfiles({CreateServerProfileFromSpecifics(profile)});
+  table()->SetServerCreditCards({CreateServerCreditCardFromSpecifics(card)});
+  ResetBridgeWithPotentialInitialSync({profile, card});
+
+  WalletMetadataSpecifics updated_remote_profile =
+      CreateWalletMetadataSpecificsForAddressWithDetails(
+          kAddr1SpecificsId, /*use_count=*/2, /*use_date=*/50);
+  WalletMetadataSpecifics updated_remote_card =
+      CreateWalletMetadataSpecificsForCardWithDetails(
+          kCard1SpecificsId, /*use_count=*/3, /*use_date=*/60);
+
+  WalletMetadataSpecifics merged_profile =
+      CreateWalletMetadataSpecificsForAddressWithDetails(
+          kAddr1SpecificsId, /*use_count=*/20, /*use_date=*/50);
+  WalletMetadataSpecifics merged_card =
+      CreateWalletMetadataSpecificsForCardWithDetails(
+          kCard1SpecificsId, /*use_count=*/30, /*use_date=*/60);
+
+  EXPECT_CALL(mock_processor(), Delete(_, _)).Times(0);
+  EXPECT_CALL(mock_processor(),
+              Put(kAddr1StorageKey, HasSpecifics(merged_profile), _));
+  EXPECT_CALL(mock_processor(),
+              Put(kCard1StorageKey, HasSpecifics(merged_card), _));
+
+  ReceivePotentiallyInitialUpdates(
+      {updated_remote_profile, updated_remote_card});
+
+  EXPECT_THAT(GetAllLocalDataInclRestart(),
+              UnorderedElementsAre(EqualsSpecifics(merged_profile),
+                                   EqualsSpecifics(merged_card)));
+}
+
+// Tests that the conflicts are resolved component-wise, like the previous test,
+// only the other way around (a higher use_count is taken from remote data, a
+// newer use_data is taken from local data).
+TEST_P(AutofillWalletMetadataSyncBridgeRemoteChangesTest,
+       Conflict_PreferHigherValues_BothWin2) {
+  WalletMetadataSpecifics profile =
+      CreateWalletMetadataSpecificsForAddressWithDetails(
+          kAddr1SpecificsId, /*use_count=*/2, /*use_date=*/50);
+  WalletMetadataSpecifics card =
+      CreateWalletMetadataSpecificsForCardWithDetails(
+          kCard1SpecificsId, /*use_count=*/3, /*use_date=*/60);
+
+  table()->SetServerProfiles({CreateServerProfileFromSpecifics(profile)});
+  table()->SetServerCreditCards({CreateServerCreditCardFromSpecifics(card)});
+  ResetBridgeWithPotentialInitialSync({profile, card});
+
+  WalletMetadataSpecifics updated_remote_profile =
+      CreateWalletMetadataSpecificsForAddressWithDetails(
+          kAddr1SpecificsId, /*use_count=*/20, /*use_date=*/5);
+  WalletMetadataSpecifics updated_remote_card =
+      CreateWalletMetadataSpecificsForCardWithDetails(
+          kCard1SpecificsId, /*use_count=*/30, /*use_date=*/6);
+
+  WalletMetadataSpecifics merged_profile =
+      CreateWalletMetadataSpecificsForAddressWithDetails(
+          kAddr1SpecificsId, /*use_count=*/20, /*use_date=*/50);
+  WalletMetadataSpecifics merged_card =
+      CreateWalletMetadataSpecificsForCardWithDetails(
+          kCard1SpecificsId, /*use_count=*/30, /*use_date=*/60);
+
+  EXPECT_CALL(mock_processor(), Delete(_, _)).Times(0);
+  EXPECT_CALL(mock_processor(),
+              Put(kAddr1StorageKey, HasSpecifics(merged_profile), _));
+  EXPECT_CALL(mock_processor(),
+              Put(kCard1StorageKey, HasSpecifics(merged_card), _));
+
+  ReceivePotentiallyInitialUpdates(
+      {updated_remote_profile, updated_remote_card});
+
+  EXPECT_THAT(GetAllLocalDataInclRestart(),
+              UnorderedElementsAre(EqualsSpecifics(merged_profile),
+                                   EqualsSpecifics(merged_card)));
+}
+
+// No merge logic is applied if local data has initial use_count (=1). In this
+// situation, we just take over the remote entity completely.
+TEST_P(AutofillWalletMetadataSyncBridgeRemoteChangesTest,
+       Conflict_PreferRemoteIfLocalHasInitialUseCount) {
+  WalletMetadataSpecifics profile =
+      CreateWalletMetadataSpecificsForAddressWithDetails(
+          kAddr1SpecificsId, /*use_count=*/1, /*use_date=*/50);
+  WalletMetadataSpecifics card =
+      CreateWalletMetadataSpecificsForCardWithDetails(
+          kCard1SpecificsId, /*use_count=*/1, /*use_date=*/60);
+
+  table()->SetServerProfiles({CreateServerProfileFromSpecifics(profile)});
+  table()->SetServerCreditCards({CreateServerCreditCardFromSpecifics(card)});
+  ResetBridgeWithPotentialInitialSync({profile, card});
+
+  WalletMetadataSpecifics updated_remote_profile =
+      CreateWalletMetadataSpecificsForAddressWithDetails(
+          kAddr1SpecificsId, /*use_count=*/20, /*use_date=*/5);
+  WalletMetadataSpecifics updated_remote_card =
+      CreateWalletMetadataSpecificsForCardWithDetails(
+          kCard1SpecificsId, /*use_count=*/30, /*use_date=*/6);
+
+  EXPECT_CALL(mock_processor(), Delete(_, _)).Times(0);
+  EXPECT_CALL(mock_processor(), Put(_, _, _)).Times(0);
+
+  ReceivePotentiallyInitialUpdates(
+      {updated_remote_profile, updated_remote_card});
+
+  EXPECT_THAT(GetAllLocalDataInclRestart(),
+              UnorderedElementsAre(EqualsSpecifics(updated_remote_profile),
+                                   EqualsSpecifics(updated_remote_card)));
+}
+
+// Tests that with a conflict in billing_address_id, we prefer an ID of a local
+// profile over an ID of a server profile. In this test, the preferred ID is in
+// the remote update that we need to store locally.
+TEST_P(AutofillWalletMetadataSyncBridgeRemoteChangesTest,
+       Conflict_Card_PreferLocalBillingAddressId_RemoteWins) {
+  WalletMetadataSpecifics card =
+      CreateWalletMetadataSpecificsForCardWithDetails(
+          kCard1SpecificsId, /*use_count=*/3, /*use_date=*/6,
+          /*billing_address_id=*/kAddr1ServerId);
+
+  table()->SetServerCreditCards({CreateServerCreditCardFromSpecifics(card)});
+  ResetBridgeWithPotentialInitialSync({card});
+
+  WalletMetadataSpecifics updated_remote_card =
+      CreateWalletMetadataSpecificsForCardWithDetails(
+          kCard1SpecificsId, /*use_count=*/3, /*use_date=*/6,
+          /*billing_address_id=*/kLocalAddr1ServerId);
+
+  EXPECT_CALL(mock_processor(), Delete(_, _)).Times(0);
+  EXPECT_CALL(mock_processor(), Put(_, _, _)).Times(0);
+
+  ReceivePotentiallyInitialUpdates({updated_remote_card});
+
+  EXPECT_THAT(GetAllLocalDataInclRestart(),
+              UnorderedElementsAre(EqualsSpecifics(updated_remote_card)));
+}
+
+// Tests that with a conflict in billing_address_id, we prefer an ID of a local
+// profile over an ID of a server profile. In this test, the preferred ID is in
+// the local data that we need to upstream.
+TEST_P(AutofillWalletMetadataSyncBridgeRemoteChangesTest,
+       Conflict_Card_PreferLocalBillingAddressId_LocalWins) {
+  WalletMetadataSpecifics card =
+      CreateWalletMetadataSpecificsForCardWithDetails(
+          kCard1SpecificsId, /*use_count=*/3, /*use_date=*/6,
+          /*billing_address_id=*/kLocalAddr1ServerId);
+
+  table()->SetServerCreditCards({CreateServerCreditCardFromSpecifics(card)});
+  ResetBridgeWithPotentialInitialSync({card});
+
+  WalletMetadataSpecifics updated_remote_card =
+      CreateWalletMetadataSpecificsForCardWithDetails(
+          kCard1SpecificsId, /*use_count=*/3, /*use_date=*/6,
+          /*billing_address_id=*/kAddr1ServerId);
+
+  EXPECT_CALL(mock_processor(), Delete(_, _)).Times(0);
+  EXPECT_CALL(mock_processor(), Put(kCard1StorageKey, HasSpecifics(card), _));
+
+  ReceivePotentiallyInitialUpdates({updated_remote_card});
+
+  EXPECT_THAT(GetAllLocalDataInclRestart(),
+              UnorderedElementsAre(EqualsSpecifics(card)));
+}
+
+// Tests that if both addresses have billing address ids of local profiles, we
+// prefer the one from the most recently used entity. In this test, it is the
+// remote entity.
+TEST_P(AutofillWalletMetadataSyncBridgeRemoteChangesTest,
+       Conflict_Card_PreferNewerBillingAddressOutOfLocalIds_RemoteWins) {
+  WalletMetadataSpecifics card =
+      CreateWalletMetadataSpecificsForCardWithDetails(
+          kCard1SpecificsId, /*use_count=*/3, /*use_date=*/6,
+          /*billing_address_id=*/kLocalAddr1ServerId);
+
+  table()->SetServerCreditCards({CreateServerCreditCardFromSpecifics(card)});
+  ResetBridgeWithPotentialInitialSync({card});
+
+  WalletMetadataSpecifics updated_remote_card =
+      CreateWalletMetadataSpecificsForCardWithDetails(
+          kCard1SpecificsId, /*use_count=*/3, /*use_date=*/60,
+          /*billing_address_id=*/kLocalAddr2ServerId);
+
+  EXPECT_CALL(mock_processor(), Delete(_, _)).Times(0);
+  EXPECT_CALL(mock_processor(), Put(_, _, _)).Times(0);
+
+  ReceivePotentiallyInitialUpdates({updated_remote_card});
+
+  EXPECT_THAT(GetAllLocalDataInclRestart(),
+              UnorderedElementsAre(EqualsSpecifics(updated_remote_card)));
+}
+
+// Tests that if both addresses have billing address ids of local profiles, we
+// prefer the one from the most recently used entity. In this test, it is the
+// local entity.
+TEST_P(AutofillWalletMetadataSyncBridgeRemoteChangesTest,
+       Conflict_Card_PreferNewerBillingAddressOutOfLocalIds_LocalWins) {
+  WalletMetadataSpecifics card =
+      CreateWalletMetadataSpecificsForCardWithDetails(
+          kCard1SpecificsId, /*use_count=*/3, /*use_date=*/60,
+          /*billing_address_id=*/kLocalAddr1ServerId);
+
+  table()->SetServerCreditCards({CreateServerCreditCardFromSpecifics(card)});
+  ResetBridgeWithPotentialInitialSync({card});
+
+  WalletMetadataSpecifics updated_remote_card =
+      CreateWalletMetadataSpecificsForCardWithDetails(
+          kCard1SpecificsId, /*use_count=*/3, /*use_date=*/6,
+          /*billing_address_id=*/kLocalAddr2ServerId);
+
+  EXPECT_CALL(mock_processor(), Delete(_, _)).Times(0);
+  EXPECT_CALL(mock_processor(), Put(kCard1StorageKey, HasSpecifics(card), _));
+
+  ReceivePotentiallyInitialUpdates({updated_remote_card});
+
+  EXPECT_THAT(GetAllLocalDataInclRestart(),
+              UnorderedElementsAre(EqualsSpecifics(card)));
+}
+
+// Tests that if both addresses have billing address ids of server profiles, we
+// prefer the one from the most recently used entity. In this test, it is the
+// remote entity.
+TEST_P(AutofillWalletMetadataSyncBridgeRemoteChangesTest,
+       Conflict_Card_PreferNewerBillingAddressOutOfServerIds_RemoteWins) {
+  WalletMetadataSpecifics card =
+      CreateWalletMetadataSpecificsForCardWithDetails(
+          kCard1SpecificsId, /*use_count=*/3, /*use_date=*/6,
+          /*billing_address_id=*/kAddr1ServerId);
+
+  table()->SetServerCreditCards({CreateServerCreditCardFromSpecifics(card)});
+  ResetBridgeWithPotentialInitialSync({card});
+
+  WalletMetadataSpecifics updated_remote_card =
+      CreateWalletMetadataSpecificsForCardWithDetails(
+          kCard1SpecificsId, /*use_count=*/3, /*use_date=*/60,
+          /*billing_address_id=*/kAddr2ServerId);
+
+  EXPECT_CALL(mock_processor(), Delete(_, _)).Times(0);
+  EXPECT_CALL(mock_processor(), Put(_, _, _)).Times(0);
+
+  ReceivePotentiallyInitialUpdates({updated_remote_card});
+
+  EXPECT_THAT(GetAllLocalDataInclRestart(),
+              UnorderedElementsAre(EqualsSpecifics(updated_remote_card)));
+}
+
+// Tests that if both addresses have billing address ids of server profiles, we
+// prefer the one from the most recently used entity. In this test, it is the
+// local entity.
+TEST_P(AutofillWalletMetadataSyncBridgeRemoteChangesTest,
+       Conflict_Card_PreferNewerBillingAddressOutOfServerIds_LocalWins) {
+  WalletMetadataSpecifics card =
+      CreateWalletMetadataSpecificsForCardWithDetails(
+          kCard1SpecificsId, /*use_count=*/3, /*use_date=*/60,
+          /*billing_address_id=*/kAddr1ServerId);
+
+  table()->SetServerCreditCards({CreateServerCreditCardFromSpecifics(card)});
+  ResetBridgeWithPotentialInitialSync({card});
+
+  WalletMetadataSpecifics updated_remote_card =
+      CreateWalletMetadataSpecificsForCardWithDetails(
+          kCard1SpecificsId, /*use_count=*/3, /*use_date=*/6,
+          /*billing_address_id=*/kAddr2ServerId);
+
+  EXPECT_CALL(mock_processor(), Delete(_, _)).Times(0);
+  EXPECT_CALL(mock_processor(), Put(kCard1StorageKey, HasSpecifics(card), _));
+
+  ReceivePotentiallyInitialUpdates({updated_remote_card});
+
+  EXPECT_THAT(GetAllLocalDataInclRestart(),
+              UnorderedElementsAre(EqualsSpecifics(card)));
+}
+
+// Tests that the conflict resolution happens component-wise. To avoid
+// combinatorial explosion, this only tests when both have billing address ids
+// of server profiles, one entity is more recently used but the other entity has
+// a higher use_count. We should pick the billing_address_id of the newer one
+// but have the use_count updated to the maximum as well. In this test, the
+// remote entity is the more recently used.
+TEST_P(AutofillWalletMetadataSyncBridgeRemoteChangesTest,
+       Conflict_Card_PreferNewerBillingAddressOutOfServerIds_BothWin1) {
+  WalletMetadataSpecifics card =
+      CreateWalletMetadataSpecificsForCardWithDetails(
+          kCard1SpecificsId, /*use_count=*/30, /*use_date=*/6,
+          /*billing_address_id=*/kAddr1ServerId);
+
+  table()->SetServerCreditCards({CreateServerCreditCardFromSpecifics(card)});
+  ResetBridgeWithPotentialInitialSync({card});
+
+  WalletMetadataSpecifics updated_remote_card =
+      CreateWalletMetadataSpecificsForCardWithDetails(
+          kCard1SpecificsId, /*use_count=*/3, /*use_date=*/60,
+          /*billing_address_id=*/kAddr2ServerId);
+
+  WalletMetadataSpecifics merged_card =
+      CreateWalletMetadataSpecificsForCardWithDetails(
+          kCard1SpecificsId, /*use_count=*/30, /*use_date=*/60,
+          /*billing_address_id=*/kAddr2ServerId);
+
+  EXPECT_CALL(mock_processor(), Delete(_, _)).Times(0);
+  EXPECT_CALL(mock_processor(),
+              Put(kCard1StorageKey, HasSpecifics(merged_card), _));
+
+  ReceivePotentiallyInitialUpdates({updated_remote_card});
+
+  EXPECT_THAT(GetAllLocalDataInclRestart(),
+              UnorderedElementsAre(EqualsSpecifics(merged_card)));
+}
+
+// Tests that the conflict resolution happens component-wise. To avoid
+// combinatorial explosion, this only tests when both have billing address ids
+// of server profiles, one entity is more recently used but the other entity has
+// a higher use_count. We should pick the billing_address_id of the newer one
+// but have the use_count updated to the maximum as well. In this test, the
+// local entity is the more recently used.
+TEST_P(AutofillWalletMetadataSyncBridgeRemoteChangesTest,
+       Conflict_Card_PreferNewerBillingAddressOutOfServerIds_BothWin2) {
+  WalletMetadataSpecifics card =
+      CreateWalletMetadataSpecificsForCardWithDetails(
+          kCard1SpecificsId, /*use_count=*/3, /*use_date=*/60,
+          /*billing_address_id=*/kAddr1ServerId);
+
+  table()->SetServerCreditCards({CreateServerCreditCardFromSpecifics(card)});
+  ResetBridgeWithPotentialInitialSync({card});
+
+  WalletMetadataSpecifics updated_remote_card =
+      CreateWalletMetadataSpecificsForCardWithDetails(
+          kCard1SpecificsId, /*use_count=*/30, /*use_date=*/6,
+          /*billing_address_id=*/kAddr2ServerId);
+
+  WalletMetadataSpecifics merged_card =
+      CreateWalletMetadataSpecificsForCardWithDetails(
+          kCard1SpecificsId, /*use_count=*/30, /*use_date=*/60,
+          /*billing_address_id=*/kAddr1ServerId);
+
+  EXPECT_CALL(mock_processor(), Delete(_, _)).Times(0);
+  EXPECT_CALL(mock_processor(),
+              Put(kCard1StorageKey, HasSpecifics(merged_card), _));
+
+  ReceivePotentiallyInitialUpdates({updated_remote_card});
+
+  EXPECT_THAT(GetAllLocalDataInclRestart(),
+              UnorderedElementsAre(EqualsSpecifics(merged_card)));
+}
+
+// Tests that if the has_converted bit differs, we always end up with the true
+// value. This test has the remote entity converted which should get updated in
+// the local DB.
+TEST_P(AutofillWalletMetadataSyncBridgeRemoteChangesTest,
+       Conflict_Address_HasConverted_RemoteWins) {
+  WalletMetadataSpecifics profile =
+      CreateWalletMetadataSpecificsForAddressWithDetails(
+          kAddr1SpecificsId, /*use_count=*/1, /*use_date=*/50,
+          /*has_converted=*/false);
+
+  table()->SetServerProfiles({CreateServerProfileFromSpecifics(profile)});
+  ResetBridgeWithPotentialInitialSync({profile});
+
+  WalletMetadataSpecifics updated_remote_profile =
+      CreateWalletMetadataSpecificsForAddressWithDetails(
+          kAddr1SpecificsId, /*use_count=*/1, /*use_date=*/50,
+          /*has_converted=*/true);
+
+  EXPECT_CALL(mock_processor(), Delete(_, _)).Times(0);
+  EXPECT_CALL(mock_processor(), Put(_, _, _)).Times(0);
+
+  ReceivePotentiallyInitialUpdates({updated_remote_profile});
+
+  EXPECT_THAT(GetAllLocalDataInclRestart(),
+              UnorderedElementsAre(EqualsSpecifics(updated_remote_profile)));
+}
+
+// Tests that if the has_converted bit differs, we always end up with the true
+// value. This test has the local entity converted which should get upstreamed.
+TEST_P(AutofillWalletMetadataSyncBridgeRemoteChangesTest,
+       Conflict_Address_HasConverted_LocalWins) {
+  WalletMetadataSpecifics profile =
+      CreateWalletMetadataSpecificsForAddressWithDetails(
+          kAddr1SpecificsId, /*use_count=*/1, /*use_date=*/50,
+          /*has_converted=*/true);
+
+  table()->SetServerProfiles({CreateServerProfileFromSpecifics(profile)});
+  ResetBridgeWithPotentialInitialSync({profile});
+
+  WalletMetadataSpecifics updated_remote_profile =
+      CreateWalletMetadataSpecificsForAddressWithDetails(
+          kAddr1SpecificsId, /*use_count=*/1, /*use_date=*/50,
+          /*has_converted=*/false);
+
+  EXPECT_CALL(mock_processor(), Delete(_, _)).Times(0);
+  EXPECT_CALL(mock_processor(),
+              Put(kAddr1StorageKey, HasSpecifics(profile), _));
+
+  ReceivePotentiallyInitialUpdates({updated_remote_profile});
+
+  EXPECT_THAT(GetAllLocalDataInclRestart(),
+              UnorderedElementsAre(EqualsSpecifics(profile)));
+}
+
+// Tests that the conflict resolution happens component-wise. If one entity
+// has_converted but the other entity has higher use_count, we should end up
+// with an entity that has_converted and has the higher use_count. This test has
+// the remote entity converted.
+TEST_P(AutofillWalletMetadataSyncBridgeRemoteChangesTest,
+       Conflict_Address_HasConverted_BothWin1) {
+  WalletMetadataSpecifics profile =
+      CreateWalletMetadataSpecificsForAddressWithDetails(
+          kAddr1SpecificsId, /*use_count=*/10, /*use_date=*/50,
+          /*has_converted=*/false);
+
+  table()->SetServerProfiles({CreateServerProfileFromSpecifics(profile)});
+  ResetBridgeWithPotentialInitialSync({profile});
+
+  WalletMetadataSpecifics updated_remote_profile =
+      CreateWalletMetadataSpecificsForAddressWithDetails(
+          kAddr1SpecificsId, /*use_count=*/1, /*use_date=*/50,
+          /*has_converted=*/true);
+
+  WalletMetadataSpecifics merged_profile =
+      CreateWalletMetadataSpecificsForAddressWithDetails(
+          kAddr1SpecificsId, /*use_count=*/10, /*use_date=*/50,
+          /*has_converted=*/true);
+
+  EXPECT_CALL(mock_processor(), Delete(_, _)).Times(0);
+  EXPECT_CALL(mock_processor(),
+              Put(kAddr1StorageKey, HasSpecifics(merged_profile), _));
+
+  ReceivePotentiallyInitialUpdates({updated_remote_profile});
+
+  EXPECT_THAT(GetAllLocalDataInclRestart(),
+              UnorderedElementsAre(EqualsSpecifics(merged_profile)));
+}
+
+// Tests that the conflict resolution happens component-wise. If one entity
+// has_converted but the other entity has higher use_count, we should end up
+// with an entity that has_converted and has the higher use_count. This test has
+// the local entity converted.
+TEST_P(AutofillWalletMetadataSyncBridgeRemoteChangesTest,
+       Conflict_Address_HasConverted_BothWin2) {
+  WalletMetadataSpecifics profile =
+      CreateWalletMetadataSpecificsForAddressWithDetails(
+          kAddr1SpecificsId, /*use_count=*/1, /*use_date=*/50,
+          /*has_converted=*/true);
+
+  table()->SetServerProfiles({CreateServerProfileFromSpecifics(profile)});
+  ResetBridgeWithPotentialInitialSync({profile});
+
+  WalletMetadataSpecifics updated_remote_profile =
+      CreateWalletMetadataSpecificsForAddressWithDetails(
+          kAddr1SpecificsId, /*use_count=*/10, /*use_date=*/50,
+          /*has_converted=*/false);
+
+  WalletMetadataSpecifics merged_profile =
+      CreateWalletMetadataSpecificsForAddressWithDetails(
+          kAddr1SpecificsId, /*use_count=*/10, /*use_date=*/50,
+          /*has_converted=*/true);
+
+  EXPECT_CALL(mock_processor(), Delete(_, _)).Times(0);
+  EXPECT_CALL(mock_processor(),
+              Put(kAddr1StorageKey, HasSpecifics(merged_profile), _));
+
+  ReceivePotentiallyInitialUpdates({updated_remote_profile});
+
+  EXPECT_THAT(GetAllLocalDataInclRestart(),
+              UnorderedElementsAre(EqualsSpecifics(merged_profile)));
+}
+
+INSTANTIATE_TEST_SUITE_P(,
+                         AutofillWalletMetadataSyncBridgeRemoteChangesTest,
+                         ::testing::Values(INITIAL_SYNC_ADD,
+                                           LATER_SYNC_ADD,
+                                           LATER_SYNC_UPDATE));
+
 }  // namespace autofill
+
+namespace sync_pb {
+
+// Makes the GMock matchers print out a readable version of the protobuf.
+void PrintTo(const WalletMetadataSpecifics& specifics, std::ostream* os) {
+  *os << autofill::WalletMetadataSpecificsAsDebugString(specifics);
+}
+
+}  // namespace sync_pb
diff --git a/components/autofill_assistant/browser/controller.cc b/components/autofill_assistant/browser/controller.cc
index 6b44fca7..b0df135 100644
--- a/components/autofill_assistant/browser/controller.cc
+++ b/components/autofill_assistant/browser/controller.cc
@@ -12,6 +12,7 @@
 #include "base/strings/string_util.h"
 #include "base/strings/utf_string_conversions.h"
 #include "base/task/post_task.h"
+#include "base/time/tick_clock.h"
 #include "base/values.h"
 #include "components/autofill_assistant/browser/metrics.h"
 #include "components/autofill_assistant/browser/protocol_utils.h"
@@ -34,12 +35,9 @@
     base::TimeDelta::FromSeconds(2);
 
 // Number of script checks to run after a call to StartPeriodicScriptChecks.
+// This limit does not apply when in autostart mode.
 static constexpr int kPeriodicScriptCheckCount = 10;
 
-// Maximum number of script checks we should do before failing when trying to
-// autostart.
-static constexpr int kAutostartCheckCountLimit = 5;
-
 // The initial progress to set when autostarting and showing the "Loading..."
 // message.
 static constexpr int kAutostartInitialProgress = 10;
@@ -57,11 +55,13 @@
 static const char* const kTrueValue = "true";
 }  // namespace
 
-Controller::Controller(content::WebContents* web_contents, Client* client)
+Controller::Controller(content::WebContents* web_contents,
+                       Client* client,
+                       const base::TickClock* tick_clock)
     : content::WebContentsObserver(web_contents),
       client_(client),
-      weak_ptr_factory_(this) {
-}
+      tick_clock_(tick_clock),
+      weak_ptr_factory_(this) {}
 
 Controller::~Controller() = default;
 
@@ -231,6 +231,7 @@
 
   DCHECK_NE(state_, AutofillAssistantState::STOPPED)
       << "Unexpected transition from " << state_ << " to " << state;
+  DVLOG(2) << __func__ << ": " << state_ << " -> " << state;
 
   state_ = state;
   GetUiController()->OnStateChanged(state);
@@ -281,21 +282,26 @@
 }
 
 void Controller::OnPeriodicScriptCheck() {
-  if (periodic_script_check_count_ <= 0) {
+  if (periodic_script_check_count_ > 0) {
+    periodic_script_check_count_--;
+  }
+
+  if (periodic_script_check_count_ <= 0 && !allow_autostart_) {
     DCHECK_EQ(0, periodic_script_check_count_);
     periodic_script_check_scheduled_ = false;
     return;
   }
 
-  if (should_fail_after_checking_scripts_ &&
-      ++total_script_check_count_ >= kAutostartCheckCountLimit) {
-    should_fail_after_checking_scripts_ = false;
-    OnFatalError(l10n_util::GetStringUTF8(IDS_AUTOFILL_ASSISTANT_DEFAULT_ERROR),
-                 Metrics::AUTOSTART_TIMEOUT);
+  if (allow_autostart_ && !autostart_timeout_script_path_.empty() &&
+      tick_clock_->NowTicks() >= absolute_autostart_timeout_) {
+    DVLOG(1) << __func__ << " giving up waiting on autostart.";
+    std::string script_path = autostart_timeout_script_path_;
+    autostart_timeout_script_path_.clear();
+    periodic_script_check_scheduled_ = false;
+    ExecuteScript(script_path, state_);
     return;
   }
 
-  periodic_script_check_count_--;
   script_tracker()->CheckScripts(kPeriodicScriptCheckInterval);
   base::PostDelayedTaskWithTraits(
       FROM_HERE, {content::BrowserThread::UI},
@@ -322,9 +328,8 @@
     return;
   }
 
-  std::vector<std::unique_ptr<Script>> scripts;
-  bool parse_result = ProtocolUtils::ParseScripts(response, &scripts);
-  if (!parse_result) {
+  SupportsScriptResponseProto response_proto;
+  if (!response_proto.ParseFromString(response)) {
     DVLOG(2) << __func__ << " from " << script_domain_ << " returned "
              << "unparseable response";
     OnFatalError(l10n_util::GetStringUTF8(IDS_AUTOFILL_ASSISTANT_DEFAULT_ERROR),
@@ -332,11 +337,23 @@
     return;
   }
 
+  std::vector<std::unique_ptr<Script>> scripts;
+  for (const auto& script_proto : response_proto.scripts()) {
+    ProtocolUtils::AddScript(script_proto, &scripts);
+  }
   if (scripts.empty()) {
     OnNoRunnableScripts();
     return;
   }
 
+  if (allow_autostart_) {
+    autostart_timeout_script_path_ =
+        response_proto.script_timeout_error().script_path();
+    autostart_timeout_ = base::TimeDelta::FromMilliseconds(
+        response_proto.script_timeout_error().timeout_ms());
+    absolute_autostart_timeout_ = tick_clock_->NowTicks() + autostart_timeout_;
+  }
+
   DVLOG(2) << __func__ << " from " << script_domain_ << " returned "
            << scripts.size() << " scripts";
   script_tracker()->SetScripts(std::move(scripts));
@@ -344,7 +361,8 @@
   StartPeriodicScriptChecks();
 }
 
-void Controller::ExecuteScript(const std::string& script_path) {
+void Controller::ExecuteScript(const std::string& script_path,
+                               AutofillAssistantState end_state) {
   DCHECK(!script_tracker()->running());
   EnterState(AutofillAssistantState::RUNNING);
 
@@ -358,12 +376,14 @@
   // TODO(crbug.com/806868): Consider making ClearRunnableScripts part of
   // ExecuteScripts to simplify the controller.
   script_tracker()->ExecuteScript(
-      script_path, base::BindOnce(&Controller::OnScriptExecuted,
-                                  // script_tracker_ is owned by Controller.
-                                  base::Unretained(this), script_path));
+      script_path,
+      base::BindOnce(&Controller::OnScriptExecuted,
+                     // script_tracker_ is owned by Controller.
+                     base::Unretained(this), script_path, end_state));
 }
 
 void Controller::OnScriptExecuted(const std::string& script_path,
+                                  AutofillAssistantState end_state,
                                   const ScriptExecutor::Result& result) {
   if (!result.success) {
     DVLOG(1) << "Failed to execute script " << script_path;
@@ -414,19 +434,19 @@
       DVLOG(1) << "Unexpected value for at_end: " << result.at_end;
       break;
   }
-  EnterState(AutofillAssistantState::PROMPT);
+  EnterState(end_state);
   GetOrCheckScripts();
 }
 
 bool Controller::MaybeAutostartScript(
     const std::vector<ScriptHandle>& runnable_scripts) {
-  // We want to g through all runnable autostart interrupts first, one at a
+  // We want to go through all runnable autostart interrupts first, one at a
   // time. To do that, always run highest priority autostartable interrupt from
   // runnable_script, which is ordered by priority.
   for (const auto& script : runnable_scripts) {
     if (script.autostart && script.interrupt) {
       std::string script_path = script.path;
-      ExecuteScript(script_path);
+      ExecuteScript(script_path, state_);
       // making a copy of script.path is necessary, as ExecuteScript clears
       // runnable_scripts, so script.path will not survive until the end of
       // ExecuteScript.
@@ -448,14 +468,19 @@
       }
     }
     if (autostart_count == 1) {
-      allow_autostart_ = false;
-      ExecuteScript(autostart_path);
+      DisableAutostart();
+      ExecuteScript(autostart_path, AutofillAssistantState::PROMPT);
       return true;
     }
   }
   return false;
 }
 
+void Controller::DisableAutostart() {
+  allow_autostart_ = false;
+  autostart_timeout_script_path_.clear();
+}
+
 void Controller::OnGetCookie(bool has_cookie) {
   if (has_cookie) {
     // This code is only active with the experiment parameter.
@@ -479,7 +504,6 @@
 void Controller::FinishStart() {
   started_ = true;
   if (allow_autostart_) {
-    should_fail_after_checking_scripts_ = true;
     MaybeSetInitialDetails();
     SetStatusMessage(
         l10n_util::GetStringFUTF8(IDS_AUTOFILL_ASSISTANT_LOADING,
@@ -539,11 +563,7 @@
 
 void Controller::OnScriptSelected(const std::string& script_path) {
   DCHECK(!script_path.empty());
-
-  // This is a script selected from the UI, so it should disable autostart.
-  allow_autostart_ = false;
-
-  ExecuteScript(script_path);
+  ExecuteScript(script_path, AutofillAssistantState::PROMPT);
 }
 
 void Controller::UpdateTouchableArea() {
@@ -634,10 +654,6 @@
   if (script_tracker()->running() || state_ == AutofillAssistantState::STOPPED)
     return;
 
-  if (!runnable_scripts.empty()) {
-    should_fail_after_checking_scripts_ = false;
-  }
-
   if (MaybeAutostartScript(runnable_scripts)) {
     return;
   }
@@ -665,11 +681,15 @@
   }
   SetDefaultChipType(chips.get());
 
-  if (allow_autostart_) {
+  if (chips->empty() && state_ == AutofillAssistantState::STARTING) {
+    // Continue waiting
+    return;
+  }
+
+  if (allow_autostart_ ||
+      state_ == AutofillAssistantState::AUTOSTART_FALLBACK_PROMPT) {
     // Autostart was expected, but only non-autostartable scripts were found.
-    //
-    // TODO(crbug.com/806868): Consider the case where no non-autostartable
-    // scripts were found.
+    DisableAutostart();
     EnterState(AutofillAssistantState::AUTOSTART_FALLBACK_PROMPT);
   } else {
     EnterState(AutofillAssistantState::PROMPT);
diff --git a/components/autofill_assistant/browser/controller.h b/components/autofill_assistant/browser/controller.h
index 4dd15c70..ba8e478 100644
--- a/components/autofill_assistant/browser/controller.h
+++ b/components/autofill_assistant/browser/controller.h
@@ -31,6 +31,10 @@
 class WebContents;
 }  // namespace content
 
+namespace base {
+class TickClock;
+}  // namespace base
+
 namespace autofill_assistant {
 class ControllerTest;
 
@@ -42,9 +46,11 @@
                    public ScriptTracker::Listener,
                    private content::WebContentsObserver {
  public:
-  // |web_contents| and |client| must remain valid for the lifetime of the
-  // instance.
-  Controller(content::WebContents* web_contents, Client* client);
+  // |web_contents|, |client| and |tick_clock| must remain valid for the
+  // lifetime of the instance.
+  Controller(content::WebContents* web_contents,
+             Client* client,
+             const base::TickClock* tick_clock);
   ~Controller() override;
 
   // Returns true if the controller is in a state where UI is necessary.
@@ -120,8 +126,13 @@
 
   void GetOrCheckScripts();
   void OnGetScripts(const GURL& url, bool result, const std::string& response);
-  void ExecuteScript(const std::string& script_path);
+
+  // Execute |script_path| and, if execution succeeds, enter |end_state| and
+  // call |on_success|.
+  void ExecuteScript(const std::string& script_path,
+                     AutofillAssistantState end_state);
   void OnScriptExecuted(const std::string& script_path,
+                        AutofillAssistantState end_state,
                         const ScriptExecutor::Result& result);
 
   // Check script preconditions every few seconds for a certain number of times.
@@ -137,6 +148,8 @@
   // right. Returns true if a script was auto-started.
   bool MaybeAutostartScript(const std::vector<ScriptHandle>& runnable_scripts);
 
+  void DisableAutostart();
+
   // Autofill Assistant cookie logic.
   //
   // On startup of the controller we set a cookie. If a cookie already existed
@@ -175,6 +188,7 @@
   ScriptTracker* script_tracker();
 
   Client* const client_;
+  const base::TickClock* const tick_clock_;
 
   // Lazily instantiate in GetWebController().
   std::unique_ptr<WebController> web_controller_;
@@ -200,11 +214,18 @@
 
   // Number of remaining periodic checks.
   int periodic_script_check_count_ = 0;
-  int total_script_check_count_ = 0;
 
-  // Whether we should hide the overlay and show an error message after a first
-  // unsuccessful round of preconditions checking.
-  bool should_fail_after_checking_scripts_ = false;
+  // Run this script if no scripts become autostartable after
+  // absolute_autostart_timeout.
+  //
+  // Ignored unless |allow_autostart_| is true.
+  std::string autostart_timeout_script_path_;
+
+  // How long to wait for an autostartable script before failing.
+  base::TimeDelta autostart_timeout_;
+
+  // Ticks at which we'll have reached |autostart_timeout_|.
+  base::TimeTicks absolute_autostart_timeout_;
 
   // Area of the screen that corresponds to the current set of touchable
   // elements.
diff --git a/components/autofill_assistant/browser/controller_unittest.cc b/components/autofill_assistant/browser/controller_unittest.cc
index 9cd97e21..7aa8aad 100644
--- a/components/autofill_assistant/browser/controller_unittest.cc
+++ b/components/autofill_assistant/browser/controller_unittest.cc
@@ -13,7 +13,8 @@
 #include "components/autofill_assistant/browser/mock_ui_controller.h"
 #include "components/autofill_assistant/browser/mock_web_controller.h"
 #include "components/autofill_assistant/browser/service.h"
-#include "content/public/test/test_renderer_host.h"
+#include "content/public/test/test_browser_context.h"
+#include "content/public/test/test_browser_thread_bundle.h"
 #include "content/public/test/web_contents_tester.h"
 #include "testing/gmock/include/gmock/gmock.h"
 
@@ -68,20 +69,25 @@
 
 }  // namespace
 
-class ControllerTest : public content::RenderViewHostTestHarness {
+class ControllerTest : public testing::Test {
  public:
-  ControllerTest() : fake_client_(&mock_ui_controller_) {}
+  ControllerTest()
+      : thread_bundle_(
+            base::test::ScopedTaskEnvironment::MainThreadType::UI_MOCK_TIME),
+        web_contents_(
+            content::WebContentsTester::CreateTestWebContents(&browser_context_,
+                                                              nullptr)),
+        fake_client_(&mock_ui_controller_) {}
   ~ControllerTest() override {}
 
   void SetUp() override {
-    content::RenderViewHostTestHarness::SetUp();
-
     auto web_controller = std::make_unique<NiceMock<MockWebController>>();
     mock_web_controller_ = web_controller.get();
     auto service = std::make_unique<NiceMock<MockService>>();
     mock_service_ = service.get();
 
-    controller_ = std::make_unique<Controller>(web_contents(), &fake_client_);
+    controller_ = std::make_unique<Controller>(
+        web_contents_.get(), &fake_client_, thread_bundle_.GetMockTickClock());
     controller_->SetWebControllerAndServiceForTest(std::move(web_controller),
                                                    std::move(service));
 
@@ -101,15 +107,8 @@
           states_.emplace_back(state);
         }));
 
-    tester_ = content::WebContentsTester::For(web_contents());
-  }
-
-  void TearDown() override {
-    // Controller must be deleted before the WebContents, owned by
-    // RenderViewHostTestHarness. In production, this is guaranteed by
-    // autofill_assistant::ClientAndroid, which owns Controller.
-    controller_.reset();
-    content::RenderViewHostTestHarness::TearDown();
+    ON_CALL(*mock_web_controller_, OnElementCheck(_, _, _))
+        .WillByDefault(RunOnceCallback<2>(false));
   }
 
  protected:
@@ -145,7 +144,8 @@
   }
 
   void SetLastCommittedUrl(const GURL& url) {
-    tester_->SetLastCommittedURL(url);
+    content::WebContentsTester::For(web_contents_.get())
+        ->SetLastCommittedURL(url);
   }
 
   // Updates the current url of the controller and forces a refresh, without
@@ -179,12 +179,17 @@
 
   UiDelegate* GetUiDelegate() { return controller_.get(); }
 
+  // |thread_bundle_| must be the first field, to make sure that everything runs
+  // in the same task environment.
+  content::TestBrowserThreadBundle thread_bundle_;
+  content::TestBrowserContext browser_context_;
+  std::unique_ptr<content::WebContents> web_contents_;
+  base::TimeTicks now_;
   std::vector<AutofillAssistantState> states_;
   MockService* mock_service_;
   MockWebController* mock_web_controller_;
   NiceMock<FakeClient> fake_client_;
   NiceMock<MockUiController> mock_ui_controller_;
-  content::WebContentsTester* tester_;
 
   std::unique_ptr<Controller> controller_;
 };
@@ -218,6 +223,38 @@
               ElementsAre(Field(&Chip::text, StrEq("script1"))));
 }
 
+TEST_F(ControllerTest, NoScripts) {
+  SupportsScriptResponseProto empty;
+  SetNextScriptResponse(empty);
+
+  Start("http://a.example.com/path");
+  EXPECT_EQ(AutofillAssistantState::STOPPED, controller_->GetState());
+}
+
+TEST_F(ControllerTest, NoRelevantScripts) {
+  SupportsScriptResponseProto script_response;
+  AddRunnableScript(&script_response, "no_match")
+      ->mutable_presentation()
+      ->mutable_precondition()
+      ->add_domain("http://otherdomain.com");
+  SetNextScriptResponse(script_response);
+
+  Start("http://a.example.com/path");
+  EXPECT_EQ(AutofillAssistantState::STOPPED, controller_->GetState());
+}
+
+TEST_F(ControllerTest, NoRelevantScriptYet) {
+  SupportsScriptResponseProto script_response;
+  AddRunnableScript(&script_response, "no_match_yet")
+      ->mutable_presentation()
+      ->mutable_precondition()
+      ->add_elements_exist()
+      ->add_selectors("#element");
+  SetNextScriptResponse(script_response);
+
+  Start("http://a.example.com/path");
+  EXPECT_EQ(AutofillAssistantState::STARTING, controller_->GetState());
+}
 TEST_F(ControllerTest, ReportPromptAndSuggestionsChanged) {
   SupportsScriptResponseProto script_response;
   AddRunnableScript(&script_response, "script1");
@@ -565,4 +602,100 @@
   SimulateWebContentsFocused();  // must not call ShowUI
 }
 
+TEST_F(ControllerTest, KeepCheckingForElement) {
+  SupportsScriptResponseProto script_response;
+  AddRunnableScript(&script_response, "no_match_yet")
+      ->mutable_presentation()
+      ->mutable_precondition()
+      ->add_elements_exist()
+      ->add_selectors("#element");
+  SetNextScriptResponse(script_response);
+
+  Start("http://a.example.com/path");
+  // No scripts yet; the element doesn't exit.
+  EXPECT_EQ(AutofillAssistantState::STARTING, controller_->GetState());
+
+  for (int i = 0; i < 3; i++) {
+    thread_bundle_.FastForwardBy(base::TimeDelta::FromSeconds(1));
+    EXPECT_EQ(AutofillAssistantState::STARTING, controller_->GetState());
+  }
+
+  EXPECT_CALL(*mock_web_controller_, OnElementCheck(_, _, _))
+      .WillRepeatedly(RunOnceCallback<2>(true));
+  thread_bundle_.FastForwardBy(base::TimeDelta::FromSeconds(1));
+
+  EXPECT_EQ(AutofillAssistantState::AUTOSTART_FALLBACK_PROMPT,
+            controller_->GetState());
+}
+
+TEST_F(ControllerTest, ScriptTimeoutError) {
+  // Wait for #element to show up for will_never_match. After 25s, execute the
+  // script on_timeout_error.
+  SupportsScriptResponseProto script_response;
+  AddRunnableScript(&script_response, "will_never_match")
+      ->mutable_presentation()
+      ->mutable_precondition()
+      ->add_elements_exist()
+      ->add_selectors("#element");
+  script_response.mutable_script_timeout_error()->set_timeout_ms(30000);
+  script_response.mutable_script_timeout_error()->set_script_path(
+      "on_timeout_error");
+  SetNextScriptResponse(script_response);
+
+  // on_timeout_error stops everything with a custom error message.
+  ActionsResponseProto on_timeout_error;
+  on_timeout_error.add_actions()->mutable_tell()->set_message("I give up");
+  on_timeout_error.add_actions()->mutable_stop();
+  std::string on_timeout_error_str;
+  on_timeout_error.SerializeToString(&on_timeout_error_str);
+  EXPECT_CALL(*mock_service_,
+              OnGetActions(StrEq("on_timeout_error"), _, _, _, _, _))
+      .WillOnce(RunOnceCallback<5>(true, on_timeout_error_str));
+
+  Start("http://a.example.com/path");
+  for (int i = 0; i < 30; i++) {
+    EXPECT_EQ(AutofillAssistantState::STARTING, controller_->GetState());
+    thread_bundle_.FastForwardBy(base::TimeDelta::FromSeconds(1));
+  }
+  EXPECT_EQ(AutofillAssistantState::STOPPED, controller_->GetState());
+  EXPECT_EQ("I give up", controller_->GetStatusMessage());
+}
+
+TEST_F(ControllerTest, ScriptTimeoutWarning) {
+  // Wait for #element to show up for will_never_match. After 10s, execute the
+  // script on_timeout_error.
+  SupportsScriptResponseProto script_response;
+  AddRunnableScript(&script_response, "will_never_match")
+      ->mutable_presentation()
+      ->mutable_precondition()
+      ->add_elements_exist()
+      ->add_selectors("#element");
+  script_response.mutable_script_timeout_error()->set_timeout_ms(4000);
+  script_response.mutable_script_timeout_error()->set_script_path(
+      "on_timeout_error");
+  SetNextScriptResponse(script_response);
+
+  // on_timeout_error displays an error message and terminates
+  ActionsResponseProto on_timeout_error;
+  on_timeout_error.add_actions()->mutable_tell()->set_message("This is slow");
+  std::string on_timeout_error_str;
+  on_timeout_error.SerializeToString(&on_timeout_error_str);
+  EXPECT_CALL(*mock_service_,
+              OnGetActions(StrEq("on_timeout_error"), _, _, _, _, _))
+      .WillOnce(RunOnceCallback<5>(true, on_timeout_error_str));
+
+  Start("http://a.example.com/path");
+
+  // Warning after 4s, script succeeds and the client continues to wait.
+  for (int i = 0; i < 4; i++) {
+    EXPECT_EQ(AutofillAssistantState::STARTING, controller_->GetState());
+    thread_bundle_.FastForwardBy(base::TimeDelta::FromSeconds(1));
+  }
+  EXPECT_EQ(AutofillAssistantState::STARTING, controller_->GetState());
+  EXPECT_EQ("This is slow", controller_->GetStatusMessage());
+  for (int i = 0; i < 10; i++) {
+    EXPECT_EQ(AutofillAssistantState::STARTING, controller_->GetState());
+    thread_bundle_.FastForwardBy(base::TimeDelta::FromSeconds(1));
+  }
+}
 }  // namespace autofill_assistant
diff --git a/components/autofill_assistant/browser/protocol_utils.cc b/components/autofill_assistant/browser/protocol_utils.cc
index d5e88c5..88e4ac52 100644
--- a/components/autofill_assistant/browser/protocol_utils.cc
+++ b/components/autofill_assistant/browser/protocol_utils.cc
@@ -66,26 +66,6 @@
 }
 
 // static
-bool ProtocolUtils::ParseScripts(
-    const std::string& response,
-    std::vector<std::unique_ptr<Script>>* scripts) {
-  DCHECK(scripts);
-
-  SupportsScriptResponseProto response_proto;
-  if (!response_proto.ParseFromString(response)) {
-    LOG(ERROR) << "Failed to parse getting assistant scripts response.";
-    return false;
-  }
-
-  scripts->clear();
-  for (const auto& script_proto : response_proto.scripts()) {
-    ProtocolUtils::AddScript(script_proto, scripts);
-  }
-
-  return true;
-}
-
-// static
 void ProtocolUtils::AddScript(const SupportedScriptProto& script_proto,
                               std::vector<std::unique_ptr<Script>>* scripts) {
   auto script = std::make_unique<Script>();
diff --git a/components/autofill_assistant/browser/protocol_utils.h b/components/autofill_assistant/browser/protocol_utils.h
index aa977cc..0bc04f4 100644
--- a/components/autofill_assistant/browser/protocol_utils.h
+++ b/components/autofill_assistant/browser/protocol_utils.h
@@ -28,18 +28,6 @@
       const std::map<std::string, std::string>& parameters,
       const ClientContextProto& client_context);
 
-  using Scripts = std::map<Script*, std::unique_ptr<Script>>;
-  // Parse assistant scripts from the given |response|, which should not be an
-  // empty string.
-  //
-  // Parsed assistant scripts are returned through |scripts|, which should not
-  // be nullptr. Returned scripts are guaranteed to be fully initialized, and
-  // have a name, path and precondition.
-  //
-  // Return false if parse failed, otherwise return true.
-  static bool ParseScripts(const std::string& response,
-                           std::vector<std::unique_ptr<Script>>* scripts);
-
   // Convert |script_proto| to a script struct and if the script is valid, add
   // it to |scripts|.
   static void AddScript(const SupportedScriptProto& script_proto,
diff --git a/components/autofill_assistant/browser/protocol_utils_unittest.cc b/components/autofill_assistant/browser/protocol_utils_unittest.cc
index 185cf717..74037423 100644
--- a/components/autofill_assistant/browser/protocol_utils_unittest.cc
+++ b/components/autofill_assistant/browser/protocol_utils_unittest.cc
@@ -28,48 +28,49 @@
   EXPECT_EQ("v", context.chrome().chrome_version());
 }
 
-TEST(ProtocolUtilsTest, NoScripts) {
+TEST(ProtocolUtilsTest, ScriptMissingPath) {
+  SupportedScriptProto script;
+  script.mutable_presentation()->set_name("missing path");
   std::vector<std::unique_ptr<Script>> scripts;
-  EXPECT_TRUE(ProtocolUtils::ParseScripts("", &scripts));
+  ProtocolUtils::AddScript(script, &scripts);
+
   EXPECT_THAT(scripts, IsEmpty());
 }
 
-TEST(ProtocolUtilsTest, SomeInvalidScripts) {
-  SupportsScriptResponseProto proto;
-
-  // 2 Invalid scripts, 1 valid one, with no preconditions.
-  proto.add_scripts()->mutable_presentation()->set_name("missing path");
-  proto.add_scripts()->set_path("missing name");
-  SupportedScriptProto* script = proto.add_scripts();
-  script->set_path("ok");
-  script->mutable_presentation()->set_name("ok name");
-
-  // Only the valid script is returned.
+TEST(ProtocolUtilsTest, ScriptMissingName) {
+  SupportedScriptProto script;
+  script.set_path("missing name");
   std::vector<std::unique_ptr<Script>> scripts;
-  std::string proto_str;
-  proto.SerializeToString(&proto_str);
-  EXPECT_TRUE(ProtocolUtils::ParseScripts(proto_str, &scripts));
+  ProtocolUtils::AddScript(script, &scripts);
+
+  EXPECT_THAT(scripts, IsEmpty());
+}
+
+TEST(ProtocolUtilsTest, MinimalValidScript) {
+  SupportedScriptProto script;
+  script.set_path("path");
+  script.mutable_presentation()->set_name("name");
+  std::vector<std::unique_ptr<Script>> scripts;
+  ProtocolUtils::AddScript(script, &scripts);
+
   ASSERT_THAT(scripts, SizeIs(1));
-  EXPECT_EQ("ok", scripts[0]->handle.path);
-  EXPECT_EQ("ok name", scripts[0]->handle.name);
+  EXPECT_EQ("path", scripts[0]->handle.path);
+  EXPECT_EQ("name", scripts[0]->handle.name);
   EXPECT_NE(nullptr, scripts[0]->precondition);
 }
 
 TEST(ProtocolUtilsTest, OneFullyFeaturedScript) {
-  SupportsScriptResponseProto proto;
-
-  SupportedScriptProto* script = proto.add_scripts();
-  script->set_path("path");
-  auto* presentation = script->mutable_presentation();
+  SupportedScriptProto script_proto;
+  script_proto.set_path("path");
+  auto* presentation = script_proto.mutable_presentation();
   presentation->set_name("name");
   presentation->set_autostart(true);
   presentation->set_initial_prompt("prompt");
   presentation->mutable_precondition()->add_domain("www.example.com");
 
   std::vector<std::unique_ptr<Script>> scripts;
-  std::string proto_str;
-  proto.SerializeToString(&proto_str);
-  EXPECT_TRUE(ProtocolUtils::ParseScripts(proto_str, &scripts));
+  ProtocolUtils::AddScript(script_proto, &scripts);
+
   ASSERT_THAT(scripts, SizeIs(1));
   EXPECT_EQ("path", scripts[0]->handle.path);
   EXPECT_EQ("name", scripts[0]->handle.name);
@@ -79,20 +80,16 @@
 }
 
 TEST(ProtocolUtilsTest, AllowInterruptsWithNoName) {
-  SupportsScriptResponseProto proto;
-
-  SupportedScriptProto* script = proto.add_scripts();
-  script->set_path("path");
-  auto* presentation = script->mutable_presentation();
+  SupportedScriptProto script_proto;
+  script_proto.set_path("path");
+  auto* presentation = script_proto.mutable_presentation();
   presentation->set_autostart(true);
   presentation->set_initial_prompt("prompt");
   presentation->set_interrupt(true);
   presentation->mutable_precondition()->add_domain("www.example.com");
 
   std::vector<std::unique_ptr<Script>> scripts;
-  std::string proto_str;
-  proto.SerializeToString(&proto_str);
-  EXPECT_TRUE(ProtocolUtils::ParseScripts(proto_str, &scripts));
+  ProtocolUtils::AddScript(script_proto, &scripts);
   ASSERT_THAT(scripts, SizeIs(1));
   EXPECT_EQ("path", scripts[0]->handle.path);
   EXPECT_EQ("", scripts[0]->handle.name);
diff --git a/components/autofill_assistant/browser/script_tracker_unittest.cc b/components/autofill_assistant/browser/script_tracker_unittest.cc
index 5ab7382..9e4cbad 100644
--- a/components/autofill_assistant/browser/script_tracker_unittest.cc
+++ b/components/autofill_assistant/browser/script_tracker_unittest.cc
@@ -67,20 +67,24 @@
 
   void OnNoRunnableScripts() override { no_runnable_scripts_anymore_++; }
 
-  void SetAndCheckScripts(const SupportsScriptResponseProto& response) {
-    std::string response_str;
-    response.SerializeToString(&response_str);
+  void SetAndCheckScripts() {
     std::vector<std::unique_ptr<Script>> scripts;
-    ProtocolUtils::ParseScripts(response_str, &scripts);
+    for (const auto& script_proto : scripts_proto_) {
+      ProtocolUtils::AddScript(*script_proto, &scripts);
+    }
     tracker_.SetScripts(std::move(scripts));
     tracker_.CheckScripts(base::TimeDelta::FromSeconds(0));
   }
 
-  static SupportedScriptProto* AddScript(SupportsScriptResponseProto* response,
-                                         const std::string& name,
-                                         const std::string& path,
-                                         const std::string& selector) {
-    SupportedScriptProto* script = response->add_scripts();
+  SupportedScriptProto* AddScript() {
+    scripts_proto_.emplace_back(new SupportedScriptProto);
+    return scripts_proto_.back().get();
+  }
+
+  SupportedScriptProto* AddScript(const std::string& name,
+                                  const std::string& path,
+                                  const std::string& selector) {
+    SupportedScriptProto* script = AddScript();
     script->set_path(path);
     script->mutable_presentation()->set_name(name);
     if (!selector.empty()) {
@@ -129,6 +133,7 @@
   std::vector<ScriptHandle> runnable_scripts_;
   FakeScriptExecutorDelegate delegate_;
   ScriptTracker tracker_;
+  std::vector<std::unique_ptr<SupportedScriptProto>> scripts_proto_;
 };
 
 TEST_F(ScriptTrackerTest, NoScripts) {
@@ -140,11 +145,9 @@
 }
 
 TEST_F(ScriptTrackerTest, SomeRunnableScripts) {
-  SupportsScriptResponseProto scripts;
-  AddScript(&scripts, "not runnable name", "not runnable path",
-            "does_not_exist");
-  AddScript(&scripts, "runnable name", "runnable path", "exists");
-  SetAndCheckScripts(scripts);
+  AddScript("not runnable name", "not runnable path", "does_not_exist");
+  AddScript("runnable name", "runnable path", "exists");
+  SetAndCheckScripts();
 
   EXPECT_EQ(1, runnable_scripts_changed_);
   ASSERT_THAT(runnable_scripts(), SizeIs(1));
@@ -154,19 +157,18 @@
 }
 
 TEST_F(ScriptTrackerTest, DoNotCheckInterruptWithNoName) {
-  SupportsScriptResponseProto scripts;
 
   // The interrupt's preconditions would all be met, but it won't be reported
   // since it doesn't have a name.
-  auto* no_name = AddScript(&scripts, "", "path1", "exists");
+  auto* no_name = AddScript("", "path1", "exists");
   no_name->mutable_presentation()->set_interrupt(true);
 
   // The interrupt's preconditions are met and it will be reported as a normal
   // script.
-  auto* with_name = AddScript(&scripts, "with name", "path2", "exists");
+  auto* with_name = AddScript("with name", "path2", "exists");
   with_name->mutable_presentation()->set_interrupt(true);
 
-  SetAndCheckScripts(scripts);
+  SetAndCheckScripts();
 
   EXPECT_EQ(1, runnable_scripts_changed_);
   ASSERT_THAT(runnable_scripts(), SizeIs(1));
@@ -174,93 +176,87 @@
 }
 
 TEST_F(ScriptTrackerTest, ReportInterruptToAutostart) {
-  SupportsScriptResponseProto scripts;
 
   // The interrupt's preconditions are met and it will be reported as runnable
   // for autostart.
-  auto* autostart = AddScript(&scripts, "", "path2", "exists");
+  auto* autostart = AddScript("", "path2", "exists");
   autostart->mutable_presentation()->set_interrupt(true);
   autostart->mutable_presentation()->set_autostart(true);
-  SetAndCheckScripts(scripts);
+  SetAndCheckScripts();
 
   EXPECT_EQ(1, runnable_scripts_changed_);
   ASSERT_THAT(runnable_scripts(), SizeIs(1));
 }
 
 TEST_F(ScriptTrackerTest, OrderScriptsByPriority) {
-  SupportsScriptResponseProto scripts;
-
-  SupportedScriptProto* a = scripts.add_scripts();
+  SupportedScriptProto* a = AddScript();
   a->set_path("a");
   a->mutable_presentation()->set_name("a");
   a->mutable_presentation()->set_priority(2);
 
-  SupportedScriptProto* b = scripts.add_scripts();
+  SupportedScriptProto* b = AddScript();
   b->set_path("b");
   b->mutable_presentation()->set_name("b");
   b->mutable_presentation()->set_priority(3);
 
-  SupportedScriptProto* c = scripts.add_scripts();
+  SupportedScriptProto* c = AddScript();
   c->set_path("c");
   c->mutable_presentation()->set_name("c");
   c->mutable_presentation()->set_priority(1);
-  SetAndCheckScripts(scripts);
+
+  SetAndCheckScripts();
 
   ASSERT_THAT(runnable_script_paths(), ElementsAre("c", "a", "b"));
 }
 
 TEST_F(ScriptTrackerTest, NewScriptChangesNothing) {
-  SupportsScriptResponseProto scripts;
-  AddScript(&scripts, "runnable name", "runnable path", "exists");
-  SetAndCheckScripts(scripts);
+  AddScript("runnable name", "runnable path", "exists");
+  SetAndCheckScripts();
   EXPECT_EQ(1, runnable_scripts_changed_);
-  SetAndCheckScripts(scripts);
+  SetAndCheckScripts();
   EXPECT_EQ(1, runnable_scripts_changed_);
 }
 
 TEST_F(ScriptTrackerTest, NewScriptClearsRunnable) {
-  SupportsScriptResponseProto scripts;
-  AddScript(&scripts, "runnable name", "runnable path", "exists");
-  SetAndCheckScripts(scripts);
+  AddScript("runnable name", "runnable path", "exists");
+  SetAndCheckScripts();
   EXPECT_EQ(1, runnable_scripts_changed_);
   EXPECT_THAT(runnable_scripts(), SizeIs(1));
 
-  SetAndCheckScripts(SupportsScriptResponseProto::default_instance());
+  scripts_proto_.clear();
+  SetAndCheckScripts();
   EXPECT_EQ(2, runnable_scripts_changed_);
   EXPECT_THAT(runnable_scripts(), IsEmpty());
 }
 
 TEST_F(ScriptTrackerTest, NewScriptAddsRunnable) {
-  SupportsScriptResponseProto scripts;
-  AddScript(&scripts, "runnable name", "runnable path", "exists");
-  SetAndCheckScripts(scripts);
+  AddScript("runnable name", "runnable path", "exists");
+  SetAndCheckScripts();
   EXPECT_EQ(1, runnable_scripts_changed_);
   EXPECT_THAT(runnable_scripts(), SizeIs(1));
 
-  AddScript(&scripts, "new runnable name", "new runnable path", "exists");
-  SetAndCheckScripts(scripts);
+  AddScript("new runnable name", "new runnable path", "exists");
+  SetAndCheckScripts();
   EXPECT_EQ(2, runnable_scripts_changed_);
   EXPECT_THAT(runnable_scripts(), SizeIs(2));
 }
 
 TEST_F(ScriptTrackerTest, NewScriptChangesRunnable) {
-  SupportsScriptResponseProto scripts;
-  AddScript(&scripts, "runnable name", "runnable path", "exists");
-  SetAndCheckScripts(scripts);
+  AddScript("runnable name", "runnable path", "exists");
+  SetAndCheckScripts();
   EXPECT_EQ(1, runnable_scripts_changed_);
   EXPECT_THAT(runnable_scripts(), SizeIs(1));
 
-  scripts.clear_scripts();
-  AddScript(&scripts, "new runnable name", "new runnable path", "exists");
-  SetAndCheckScripts(scripts);
+  scripts_proto_.clear();
+  AddScript("new runnable name", "new runnable path", "exists");
+  SetAndCheckScripts();
   EXPECT_EQ(2, runnable_scripts_changed_);
 }
 
 TEST_F(ScriptTrackerTest, CheckScriptsAgainAfterScriptEnd) {
-  SupportsScriptResponseProto scripts;
-  AddScript(&scripts, "script 1", "script1", "exists");
-  AddScript(&scripts, "script 2", "script2", "exists");
-  SetAndCheckScripts(scripts);
+  AddScript("script 1", "script1", "exists");
+  AddScript("script 2", "script2", "exists");
+  SetAndCheckScripts();
 
   // Both scripts are runnable
   EXPECT_EQ(1, runnable_scripts_changed_);
@@ -287,9 +283,8 @@
       OnElementCheck(kExistenceCheck, Eq(Selector({"maybe_exists"})), _))
       .WillOnce(RunOnceCallback<2>(false));
 
-  SupportsScriptResponseProto scripts;
-  AddScript(&scripts, "script name", "script path", "maybe_exists");
-  SetAndCheckScripts(scripts);
+  AddScript("script name", "script path", "maybe_exists");
+  SetAndCheckScripts();
 
   // No scripts are runnable.
   EXPECT_THAT(runnable_scripts(), IsEmpty());
@@ -307,9 +302,8 @@
 
 TEST_F(ScriptTrackerTest, UpdateScriptList) {
   // 1. Initialize runnable scripts with a single valid script.
-  SupportsScriptResponseProto scripts;
-  AddScript(&scripts, "runnable name", "runnable path", "exists");
-  SetAndCheckScripts(scripts);
+  AddScript("runnable name", "runnable path", "exists");
+  SetAndCheckScripts();
 
   EXPECT_EQ(1, runnable_scripts_changed_);
   ASSERT_THAT(runnable_scripts(), SizeIs(1));
@@ -321,9 +315,9 @@
   actions_response.add_actions()->mutable_tell()->set_message("hi");
 
   *actions_response.mutable_update_script_list()->add_scripts() =
-      *AddScript(&scripts, "update name", "update path", "exists");
+      *AddScript("update name", "update path", "exists");
   *actions_response.mutable_update_script_list()->add_scripts() =
-      *AddScript(&scripts, "update name 2", "update path 2", "exists");
+      *AddScript("update name 2", "update path 2", "exists");
 
   EXPECT_CALL(mock_service_,
               OnGetActions(StrEq("runnable name"), _, _, _, _, _))
@@ -348,11 +342,9 @@
 
 TEST_F(ScriptTrackerTest, UpdateScriptListFromInterrupt) {
   // 1. Initialize runnable scripts with a single valid interrupt script.
-  SupportsScriptResponseProto scripts;
-  auto* script =
-      AddScript(&scripts, "runnable name", "runnable path", "exists");
+  auto* script = AddScript("runnable name", "runnable path", "exists");
   script->mutable_presentation()->set_interrupt(true);
-  SetAndCheckScripts(scripts);
+  SetAndCheckScripts();
 
   EXPECT_EQ(1, runnable_scripts_changed_);
   ASSERT_THAT(runnable_scripts(), SizeIs(1));
@@ -365,9 +357,9 @@
   actions_response.add_actions()->mutable_tell()->set_message("hi");
 
   *actions_response.mutable_update_script_list()->add_scripts() =
-      *AddScript(&scripts, "update name", "update path", "exists");
+      *AddScript("update name", "update path", "exists");
   *actions_response.mutable_update_script_list()->add_scripts() =
-      *AddScript(&scripts, "update name 2", "update path 2", "exists");
+      *AddScript("update name 2", "update path 2", "exists");
 
   EXPECT_CALL(mock_service_,
               OnGetActions(StrEq("runnable name"), _, _, _, _, _))
@@ -391,20 +383,18 @@
 }
 
 TEST_F(ScriptTrackerTest, NoRunnableScriptsEvenWithDOMChanges) {
-  SupportsScriptResponseProto scripts;
-  auto* script = AddScript(&scripts, "name", "path", "");
+  auto* script = AddScript("name", "path", "");
   script->mutable_presentation()->mutable_precondition()->add_path_pattern(
       "doesnotmatch");
-  SetAndCheckScripts(scripts);
+  SetAndCheckScripts();
 
   EXPECT_THAT(runnable_scripts(), SizeIs(0));
   EXPECT_EQ(1, no_runnable_scripts_anymore_);
 }
 
 TEST_F(ScriptTrackerTest, NoRunnableScriptsWaitingForDOMChanges) {
-  SupportsScriptResponseProto scripts;
-  AddScript(&scripts, "runnable name", "runnable path", "does_not_exist");
-  SetAndCheckScripts(scripts);
+  AddScript("runnable name", "runnable path", "does_not_exist");
+  SetAndCheckScripts();
 
   EXPECT_THAT(runnable_scripts(), SizeIs(0));
   EXPECT_EQ(0, no_runnable_scripts_anymore_);
diff --git a/components/autofill_assistant/browser/service.proto b/components/autofill_assistant/browser/service.proto
index 142e6ec1..46d80c2 100644
--- a/components/autofill_assistant/browser/service.proto
+++ b/components/autofill_assistant/browser/service.proto
@@ -55,6 +55,22 @@
 // Response of the list of supported scripts.
 message SupportsScriptResponseProto {
   repeated SupportedScriptProto scripts = 1;
+
+  // Defines what should happen if no scripts in [scripts] becomes runnable,
+  // because of preconditions.
+  optional ScriptTimeoutError script_timeout_error = 2;
+}
+
+message ScriptTimeoutError {
+  // Wait for that long before considering that scripts preconditions have timed
+  // out and executing the script specified in script_path.
+  //
+  // The script might be called more than once if the script terminates
+  // successfully and again still nothing is found after timeout_ms.
+  optional int32 timeout_ms = 1;
+
+  // The script to execute when the error happens.
+  optional string script_path = 2;
 }
 
 // Supported script.
diff --git a/components/autofill_strings.grdp b/components/autofill_strings.grdp
index 7aa6cee..8c5ba22 100644
--- a/components/autofill_strings.grdp
+++ b/components/autofill_strings.grdp
@@ -269,7 +269,7 @@
     Confirm name
   </message>
   <message name="IDS_AUTOFILL_SAVE_CARD_UPDATE_EXPIRATION_DATE_TITLE" desc="Title for the dialog requesting the user to fill in the expiration date of the credit card during the save card to google flow.">
-    Update expiration date
+    Enter expiration date
   </message>
   <message name="IDS_AUTOFILL_SAVE_CARD_PROMPT_UPLOAD_EXPLANATION_TOOLTIP" desc="The tooltip hover text that explains why credit card upload to Google Payments is being offered.">
     Chrome is offering to save your cards in your Google Account because you are signed in. You can change this behavior in settings.
diff --git a/components/browser_sync/profile_sync_service.cc b/components/browser_sync/profile_sync_service.cc
index 8764984..8aaf8a7 100644
--- a/components/browser_sync/profile_sync_service.cc
+++ b/components/browser_sync/profile_sync_service.cc
@@ -13,20 +13,14 @@
 #include "base/feature_list.h"
 #include "base/files/file_path.h"
 #include "base/logging.h"
-#include "base/memory/ref_counted.h"
 #include "base/metrics/histogram_macros.h"
 #include "components/autofill/core/common/autofill_features.h"
 #include "components/browser_sync/browser_sync_switches.h"
 #include "components/browser_sync/sync_auth_manager.h"
-#include "components/invalidation/impl/invalidation_prefs.h"
-#include "components/invalidation/impl/invalidation_switches.h"
 #include "components/invalidation/public/invalidation_service.h"
-#include "components/prefs/pref_service.h"
 #include "components/signin/core/browser/account_info.h"
 #include "components/signin/core/browser/signin_metrics.h"
 #include "components/sync/base/bind_to_task_runner.h"
-#include "components/sync/base/cryptographer.h"
-#include "components/sync/base/passphrase_enums.h"
 #include "components/sync/base/report_unrecoverable_error.h"
 #include "components/sync/base/stop_source.h"
 #include "components/sync/base/sync_base_switches.h"
@@ -39,15 +33,12 @@
 #include "components/sync/driver/sync_driver_switches.h"
 #include "components/sync/driver/sync_type_preference_provider.h"
 #include "components/sync/driver/sync_util.h"
-#include "components/sync/engine/configure_reason.h"
 #include "components/sync/engine/cycle/type_debug_info_observer.h"
 #include "components/sync/engine/engine_components_factory_impl.h"
 #include "components/sync/engine/net/http_bridge_network_resources.h"
 #include "components/sync/engine/net/network_resources.h"
 #include "components/sync/engine/polling_constants.h"
 #include "components/sync/engine/sync_encryption_handler.h"
-#include "components/sync/model/change_processor.h"
-#include "components/sync/model/model_type_store_service.h"
 #include "components/sync/model/sync_error.h"
 #include "components/sync/syncable/base_transaction.h"
 #include "components/sync/syncable/directory.h"
@@ -359,9 +350,20 @@
 void ProfileSyncService::CredentialsChanged() {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
 
-  syncer::SyncCredentials credentials = auth_manager_->GetCredentials();
+  // If the engine isn't allowed to start anymore due to the credentials change,
+  // then shut down. This happens when the user signs out on the web, i.e. we're
+  // in the "Sync paused" state.
+  if (!IsEngineAllowedToStart()) {
+    // This will notify observers if appropriate.
+    StopImpl(KEEP_DATA);
+    return;
+  }
 
-  if (engine_) {
+  if (!engine_) {
+    startup_controller_->TryStart(/*force_immediate=*/true);
+  } else {
+    // If the engine already exists, just propagate the new credentials.
+    syncer::SyncCredentials credentials = auth_manager_->GetCredentials();
     if (credentials.sync_token.empty()) {
       engine_->InvalidateCredentials();
     } else {
@@ -705,6 +707,9 @@
   if (unrecoverable_error_reason_ != ERROR_REASON_UNSET) {
     result = result | DISABLE_REASON_UNRECOVERABLE_ERROR;
   }
+  if (auth_manager_->IsSyncPaused()) {
+    result = result | DISABLE_REASON_PAUSED;
+  }
   return result;
 }
 
@@ -718,9 +723,6 @@
     return TransportState::DISABLED;
   }
 
-  // Typically, Sync won't start until the initial setup is at least in
-  // progress. StartupController::TryStartImmediately bypasses the first setup
-  // check though, so we first have to check whether the engine is initialized.
   if (!engine_initialized_) {
     switch (startup_controller_->GetState()) {
       case syncer::StartupController::State::NOT_STARTED:
diff --git a/components/browser_sync/profile_sync_service.h b/components/browser_sync/profile_sync_service.h
index 172ddd0..f33128894 100644
--- a/components/browser_sync/profile_sync_service.h
+++ b/components/browser_sync/profile_sync_service.h
@@ -42,7 +42,6 @@
 #include "components/sync/engine/sync_engine.h"
 #include "components/sync/engine/sync_engine_host.h"
 #include "components/sync/js/sync_js_controller.h"
-#include "components/version_info/version_info.h"
 #include "google_apis/gaia/gaia_auth_util.h"
 #include "google_apis/gaia/google_service_auth_error.h"
 #include "services/identity/public/cpp/identity_manager.h"
diff --git a/components/browser_sync/profile_sync_service_autofill_unittest.cc b/components/browser_sync/profile_sync_service_autofill_unittest.cc
index 7051642..a192f7af 100644
--- a/components/browser_sync/profile_sync_service_autofill_unittest.cc
+++ b/components/browser_sync/profile_sync_service_autofill_unittest.cc
@@ -998,10 +998,6 @@
   StartAutofillProfileSyncService(add_autofill.callback());
   ASSERT_TRUE(add_autofill.success());
 
-  // TODO(crbug.com/904390): Remove when the investigation is over. This call is
-  // needed in the AutofillProfileChanged() callback.
-  EXPECT_CALL(autofill_table(), GetServerProfiles(_)).WillOnce(Return(true));
-
   AutofillProfileChange change(AutofillProfileChange::REMOVE,
                                sync_profile.guid(), nullptr);
   web_data_service()->OnAutofillProfileChanged(change);
diff --git a/components/browser_sync/profile_sync_service_unittest.cc b/components/browser_sync/profile_sync_service_unittest.cc
index b32c1a2..d036de25 100644
--- a/components/browser_sync/profile_sync_service_unittest.cc
+++ b/components/browser_sync/profile_sync_service_unittest.cc
@@ -773,17 +773,13 @@
 // Sync. Regression test for https://crbug.com/824791.
 TEST_F(ProfileSyncServiceTest, CredentialsRejectedByClient) {
   syncer::SyncCredentials init_credentials;
-  bool invalidate_credentials_called = false;
-  base::RepeatingClosure invalidate_credentials_callback =
-      base::BindRepeating([](bool* called) { *called = true; },
-                          base::Unretained(&invalidate_credentials_called));
 
   CreateService(ProfileSyncService::AUTO_START);
   SignIn();
   EXPECT_CALL(*component_factory(), CreateSyncEngine(_, _, _))
       .WillOnce(
           Return(ByMove(std::make_unique<FakeSyncEngineCollectCredentials>(
-              &init_credentials, invalidate_credentials_callback))));
+              &init_credentials, base::RepeatingClosure()))));
   InitializeForNthSync();
   ASSERT_EQ(syncer::SyncService::TransportState::ACTIVE,
             service()->GetTransportState());
@@ -813,7 +809,7 @@
   // Simulate the credentials getting locally rejected by the client by setting
   // the refresh token to a special invalid value.
   identity_test_env()->SetInvalidRefreshTokenForPrimaryAccount();
-  GoogleServiceAuthError rejected_by_client =
+  const GoogleServiceAuthError rejected_by_client =
       GoogleServiceAuthError::FromInvalidGaiaCredentialsReason(
           GoogleServiceAuthError::InvalidGaiaCredentialsReason::
               CREDENTIALS_REJECTED_BY_CLIENT);
@@ -822,13 +818,14 @@
                 ->identity_manager()
                 ->GetErrorStateOfRefreshTokenForAccount(primary_account_id));
   EXPECT_TRUE(service()->GetAccessTokenForTest().empty());
-  EXPECT_TRUE(invalidate_credentials_called);
 
   // The observer should have been notified of the auth error state.
   EXPECT_EQ(rejected_by_client, observer.auth_error());
-  // The overall state should remain ACTIVE.
-  EXPECT_EQ(syncer::SyncService::TransportState::ACTIVE,
+  // The Sync engine should have been shut down.
+  EXPECT_EQ(syncer::SyncService::TransportState::DISABLED,
             service()->GetTransportState());
+  EXPECT_TRUE(
+      service()->HasDisableReason(syncer::SyncService::DISABLE_REASON_PAUSED));
 
   service()->RemoveObserver(&observer);
 }
diff --git a/components/browser_sync/sync_auth_manager.cc b/components/browser_sync/sync_auth_manager.cc
index 17020a7..ee799f3 100644
--- a/components/browser_sync/sync_auth_manager.cc
+++ b/components/browser_sync/sync_auth_manager.cc
@@ -50,6 +50,16 @@
     false,
 };
 
+bool IsWebSignout(const GoogleServiceAuthError& auth_error) {
+  // The identity code sets an account's refresh token to be invalid (error
+  // CREDENTIALS_REJECTED_BY_CLIENT) if the user signs out of that account on
+  // the web.
+  return auth_error ==
+         GoogleServiceAuthError::FromInvalidGaiaCredentialsReason(
+             GoogleServiceAuthError::InvalidGaiaCredentialsReason::
+                 CREDENTIALS_REJECTED_BY_CLIENT);
+}
+
 }  // namespace
 
 SyncAuthManager::SyncAuthManager(
@@ -104,6 +114,10 @@
   return last_auth_error_;
 }
 
+bool SyncAuthManager::IsSyncPaused() const {
+  return IsWebSignout(GetLastAuthError());
+}
+
 syncer::SyncTokenStatus SyncAuthManager::GetSyncTokenStatus() const {
   DCHECK(partial_token_status_.next_token_request_time.is_null());
 
@@ -260,16 +274,13 @@
   }
 
   // Compute the validity of the new refresh token: The identity code sets an
-  // account's refresh token to be invalid (error
-  // CREDENTIALS_REJECTED_BY_CLIENT) if the user signs out of that account on
-  // the web.
+  // account's refresh token to be invalid if the user signs out of that account
+  // on the web.
   // TODO(blundell): Hide this logic inside IdentityManager.
   GoogleServiceAuthError token_error =
       identity_manager_->GetErrorStateOfRefreshTokenForAccount(
           account_info.account_id);
-  if (token_error == GoogleServiceAuthError::FromInvalidGaiaCredentialsReason(
-                         GoogleServiceAuthError::InvalidGaiaCredentialsReason::
-                             CREDENTIALS_REJECTED_BY_CLIENT)) {
+  if (IsWebSignout(token_error)) {
     // When the refresh token is replaced by an invalid token, Sync must be
     // stopped immediately, even if the current access token is still valid.
     // This happens e.g. when the user signs out of the web with Dice enabled.
@@ -315,6 +326,7 @@
 
   // If we're still here, then that means Chrome is still signed in to this
   // account. Keep Sync alive but set an auth error.
+  // TODO(crbug.com/906995): Should we stop Sync in this case?
   DCHECK_EQ(sync_account_.account_info.account_id,
             identity_manager_->GetPrimaryAccountId());
 
diff --git a/components/browser_sync/sync_auth_manager.h b/components/browser_sync/sync_auth_manager.h
index d88af49..fa97a6f 100644
--- a/components/browser_sync/sync_auth_manager.h
+++ b/components/browser_sync/sync_auth_manager.h
@@ -68,6 +68,11 @@
   // from the Sync server or from the IdentityManager.
   GoogleServiceAuthError GetLastAuthError() const;
 
+  // Returns whether we are in the "Sync paused" state. That means there is a
+  // primary account, but the user signed out in the content area, and so we
+  // don't have credentials for it anymore.
+  bool IsSyncPaused() const;
+
   // Returns the credentials to be passed to the SyncEngine.
   syncer::SyncCredentials GetCredentials() const;
 
diff --git a/components/cast_channel/cast_socket_unittest.cc b/components/cast_channel/cast_socket_unittest.cc
index befd1b7..9dbb116 100644
--- a/components/cast_channel/cast_socket_unittest.cc
+++ b/components/cast_channel/cast_socket_unittest.cc
@@ -37,7 +37,6 @@
 #include "net/base/address_list.h"
 #include "net/base/net_errors.h"
 #include "net/cert/pem_tokenizer.h"
-#include "net/socket/client_socket_handle.h"
 #include "net/socket/socket_test_util.h"
 #include "net/socket/ssl_client_socket.h"
 #include "net/socket/ssl_server_socket.h"
@@ -316,27 +315,6 @@
     }
   }
   std::unique_ptr<net::SSLClientSocket> CreateSSLClientSocket(
-      std::unique_ptr<net::ClientSocketHandle> client_handle,
-      const net::HostPortPair& host_and_port,
-      const net::SSLConfig& ssl_config,
-      const net::SSLClientSocketContext& context) override {
-    if (!ssl_connect_data_) {
-      // Test isn't overriding SSL socket creation.
-      return net::ClientSocketFactory::GetDefaultFactory()
-          ->CreateSSLClientSocket(std::move(client_handle), host_and_port,
-                                  ssl_config, context);
-    }
-    ssl_socket_data_provider_ = std::make_unique<net::SSLSocketDataProvider>(
-        ssl_connect_data_->mode, ssl_connect_data_->result);
-
-    if (tls_socket_created_)
-      std::move(tls_socket_created_).Run();
-
-    return std::make_unique<net::MockSSLClientSocket>(
-        std::move(client_handle), net::HostPortPair(), net::SSLConfig(),
-        ssl_socket_data_provider_.get());
-  }
-  std::unique_ptr<net::SSLClientSocket> CreateSSLClientSocket(
       std::unique_ptr<net::StreamSocket> nested_socket,
       const net::HostPortPair& host_and_port,
       const net::SSLConfig& ssl_config,
@@ -358,21 +336,6 @@
         ssl_socket_data_provider_.get());
   }
   std::unique_ptr<net::ProxyClientSocket> CreateProxyClientSocket(
-      std::unique_ptr<net::ClientSocketHandle> transport_socket,
-      const std::string& user_agent,
-      const net::HostPortPair& endpoint,
-      const net::ProxyServer& proxy_server,
-      net::HttpAuthController* http_auth_controller,
-      bool tunnel,
-      bool using_spdy,
-      net::NextProto negotiated_protocol,
-      net::ProxyDelegate* proxy_delegate,
-      bool is_https_proxy,
-      const net::NetworkTrafficAnnotationTag& traffic_annotation) override {
-    NOTIMPLEMENTED();
-    return nullptr;
-  }
-  std::unique_ptr<net::ProxyClientSocket> CreateProxyClientSocket(
       std::unique_ptr<net::StreamSocket> stream_socket,
       const std::string& user_agent,
       const net::HostPortPair& endpoint,
diff --git a/components/cdm/browser/media_drm_storage_impl_unittest.cc b/components/cdm/browser/media_drm_storage_impl_unittest.cc
index 4d0c21f..50c44d48 100644
--- a/components/cdm/browser/media_drm_storage_impl_unittest.cc
+++ b/components/cdm/browser/media_drm_storage_impl_unittest.cc
@@ -83,6 +83,7 @@
   void TearDown() override {
     media_drm_storage_.reset();
     base::RunLoop().RunUntilIdle();
+    RenderViewHostTestHarness::TearDown();
   }
 
  protected:
diff --git a/components/content_capture/android/BUILD.gn b/components/content_capture/android/BUILD.gn
index 5b9003f..7aeb282 100644
--- a/components/content_capture/android/BUILD.gn
+++ b/components/content_capture/android/BUILD.gn
@@ -6,6 +6,7 @@
 
 source_set("android") {
   sources = [
+    "content_capture_features_android.cc",
     "content_capture_receiver_manager_android.cc",
     "content_capture_receiver_manager_android.h",
   ]
@@ -18,11 +19,12 @@
 android_library("java") {
   deps = [
     "//base:base_java",
-    # "//content/public/android:content_java",
+    "//content/public/android:content_java",
   ]
   java_files = [
     "java/src/org/chromium/components/content_capture/ContentCaptureConsumer.java",
     "java/src/org/chromium/components/content_capture/ContentCaptureData.java",
+    "java/src/org/chromium/components/content_capture/ContentCaptureFeatures.java",
     "java/src/org/chromium/components/content_capture/ContentCaptureReceiverManager.java",
     "java/src/org/chromium/components/content_capture/FrameSession.java",
   ]
@@ -31,6 +33,7 @@
 generate_jni("jni_headers") {
   sources = [
     "java/src/org/chromium/components/content_capture/ContentCaptureData.java",
+    "java/src/org/chromium/components/content_capture/ContentCaptureFeatures.java",
     "java/src/org/chromium/components/content_capture/ContentCaptureReceiverManager.java",
   ]
   jni_package = "content_capture"
diff --git a/components/content_capture/android/content_capture_features_android.cc b/components/content_capture/android/content_capture_features_android.cc
new file mode 100644
index 0000000..49eb276
--- /dev/null
+++ b/components/content_capture/android/content_capture_features_android.cc
@@ -0,0 +1,10 @@
+// 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/content_capture/common/content_capture_features.h"
+#include "jni/ContentCaptureFeatures_jni.h"
+
+static jboolean JNI_ContentCaptureFeatures_IsEnabled(JNIEnv* env) {
+  return content_capture::features::IsContentCaptureEnabled();
+}
diff --git a/components/content_capture/android/content_capture_receiver_manager_android.cc b/components/content_capture/android/content_capture_receiver_manager_android.cc
index 215f597..522bfde 100644
--- a/components/content_capture/android/content_capture_receiver_manager_android.cc
+++ b/components/content_capture/android/content_capture_receiver_manager_android.cc
@@ -19,6 +19,18 @@
 using base::android::ScopedJavaLocalRef;
 using base::android::ToJavaLongArray;
 
+void JNI_ContentCaptureReceiverManager_Init(
+    JNIEnv* env,
+    const base::android::JavaParamRef<jobject>& jcaller,
+    const base::android::JavaParamRef<jobject>& jweb_contents) {
+  auto* web_contents = content::WebContents::FromJavaWebContents(jweb_contents);
+  DCHECK(web_contents);
+  DCHECK(!content_capture::ContentCaptureReceiverManager::FromWebContents(
+      web_contents));
+  content_capture::ContentCaptureReceiverManagerAndroid::Create(web_contents,
+                                                                jcaller);
+}
+
 namespace content_capture {
 
 namespace {
@@ -63,10 +75,7 @@
 ContentCaptureReceiverManagerAndroid::ContentCaptureReceiverManagerAndroid(
     content::WebContents* web_contents,
     const JavaRef<jobject>& jcaller)
-    : ContentCaptureReceiverManager(web_contents) {
-  JNIEnv* env = AttachCurrentThread();
-  java_ref_ = JavaObjectWeakGlobalRef(env, jcaller);
-}
+    : ContentCaptureReceiverManager(web_contents), java_ref_(jcaller) {}
 
 ContentCaptureReceiverManagerAndroid::~ContentCaptureReceiverManagerAndroid() =
     default;
@@ -83,38 +92,33 @@
     const ContentCaptureSession& parent_session,
     const ContentCaptureData& data) {
   JNIEnv* env = AttachCurrentThread();
-  ScopedJavaLocalRef<jobject> obj = java_ref_.get(env);
-  if (obj.is_null())
-    return;
+  DCHECK(java_ref_.obj());
 
   ScopedJavaLocalRef<jobject> jdata =
       ToJavaObjectOfContentCaptureData(env, data, JavaRef<jobject>());
   if (jdata.is_null())
     return;
   Java_ContentCaptureReceiverManager_didCaptureContent(
-      env, obj, ToJavaArrayOfContentCaptureData(env, parent_session), jdata);
+      env, java_ref_, ToJavaArrayOfContentCaptureData(env, parent_session),
+      jdata);
 }
 
 void ContentCaptureReceiverManagerAndroid::DidRemoveContent(
     const ContentCaptureSession& session,
     const std::vector<int64_t>& data) {
   JNIEnv* env = AttachCurrentThread();
-  ScopedJavaLocalRef<jobject> obj = java_ref_.get(env);
-  if (obj.is_null())
-    return;
+  DCHECK(java_ref_.obj());
   Java_ContentCaptureReceiverManager_didRemoveContent(
-      env, obj, ToJavaArrayOfContentCaptureData(env, session),
+      env, java_ref_, ToJavaArrayOfContentCaptureData(env, session),
       ToJavaLongArray(env, data));
 }
 
 void ContentCaptureReceiverManagerAndroid::DidRemoveSession(
     const ContentCaptureSession& session) {
   JNIEnv* env = AttachCurrentThread();
-  ScopedJavaLocalRef<jobject> obj = java_ref_.get(env);
-  if (obj.is_null())
-    return;
+  DCHECK(java_ref_.obj());
   Java_ContentCaptureReceiverManager_didRemoveSession(
-      env, obj, ToJavaArrayOfContentCaptureData(env, session));
+      env, java_ref_, ToJavaArrayOfContentCaptureData(env, session));
 }
 
 }  // namespace content_capture
diff --git a/components/content_capture/android/content_capture_receiver_manager_android.h b/components/content_capture/android/content_capture_receiver_manager_android.h
index cb98c9a..46ec64df 100644
--- a/components/content_capture/android/content_capture_receiver_manager_android.h
+++ b/components/content_capture/android/content_capture_receiver_manager_android.h
@@ -7,7 +7,7 @@
 
 #include <vector>
 
-#include "base/android/jni_weak_ref.h"
+#include "base/android/scoped_java_ref.h"
 #include "components/content_capture/browser/content_capture_receiver_manager.h"
 
 namespace content_capture {
@@ -32,7 +32,7 @@
       content::WebContents* web_contents,
       const base::android::JavaRef<jobject>& jcaller);
 
-  JavaObjectWeakGlobalRef java_ref_;
+  base::android::ScopedJavaGlobalRef<jobject> java_ref_;
 };
 
 }  // namespace content_capture
diff --git a/components/content_capture/android/java/src/org/chromium/components/content_capture/ContentCaptureFeatures.java b/components/content_capture/android/java/src/org/chromium/components/content_capture/ContentCaptureFeatures.java
new file mode 100644
index 0000000..dc7ad56
--- /dev/null
+++ b/components/content_capture/android/java/src/org/chromium/components/content_capture/ContentCaptureFeatures.java
@@ -0,0 +1,14 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+package org.chromium.components.content_capture;
+
+/**
+ * The class to get if feature is enabled from native.
+ */
+public class ContentCaptureFeatures {
+    public static boolean isEnabled() {
+        return nativeIsEnabled();
+    }
+    private static native boolean nativeIsEnabled();
+}
diff --git a/components/content_capture/android/java/src/org/chromium/components/content_capture/ContentCaptureReceiverManager.java b/components/content_capture/android/java/src/org/chromium/components/content_capture/ContentCaptureReceiverManager.java
index 4e2daef..c217f21 100644
--- a/components/content_capture/android/java/src/org/chromium/components/content_capture/ContentCaptureReceiverManager.java
+++ b/components/content_capture/android/java/src/org/chromium/components/content_capture/ContentCaptureReceiverManager.java
@@ -6,8 +6,10 @@
 
 import android.view.ViewGroup;
 
+import org.chromium.base.CommandLine;
 import org.chromium.base.Log;
 import org.chromium.base.annotations.CalledByNative;
+import org.chromium.content_public.browser.WebContents;
 
 import java.util.Arrays;
 
@@ -16,11 +18,20 @@
  */
 public class ContentCaptureReceiverManager {
     private static final String TAG = "ContentCapture";
-    private static final boolean DEBUG = false;
+    private static final String FLAG = "dump-captured-content-to-logcat-for-testing";
+    private static Boolean sDump;
 
     private ContentCaptureConsumer mContentCaptureConsumer;
 
-    public ContentCaptureReceiverManager() {}
+    public static ContentCaptureReceiverManager create(WebContents webContents) {
+        ContentCaptureReceiverManager manager = new ContentCaptureReceiverManager();
+        manager.nativeInit(webContents);
+        return manager;
+    }
+
+    public ContentCaptureReceiverManager() {
+        if (sDump == null) sDump = CommandLine.getInstance().hasSwitch(FLAG);
+    }
 
     public void onContainerViewChanged(ViewGroup containerView) {
         // Reset current consumer, the new consumer that associates with contanerView shall be set
@@ -37,7 +48,7 @@
         if (mContentCaptureConsumer != null) {
             mContentCaptureConsumer.onContentCaptured(toFrameSession(session), data);
         }
-        if (DEBUG) Log.i(TAG, "Captured Content: %s", data);
+        if (sDump.booleanValue()) Log.i(TAG, "Captured Content: %s", data);
     }
 
     @CalledByNative
@@ -45,15 +56,16 @@
         FrameSession frameSession = toFrameSession(session);
         if (mContentCaptureConsumer != null)
             mContentCaptureConsumer.onContentRemoved(frameSession, data);
-        if (DEBUG)
+        if (sDump.booleanValue()) {
             Log.i(TAG, "Removed Content: %s", frameSession.get(0) + " " + Arrays.toString(data));
+        }
     }
 
     @CalledByNative
     private void didRemoveSession(Object[] session) {
         FrameSession frameSession = toFrameSession(session);
         if (mContentCaptureConsumer != null) mContentCaptureConsumer.onSessionRemoved(frameSession);
-        if (DEBUG) Log.i(TAG, "Removed Session: %s", frameSession.get(0));
+        if (sDump.booleanValue()) Log.i(TAG, "Removed Session: %s", frameSession.get(0));
     }
 
     private FrameSession toFrameSession(Object[] session) {
@@ -61,4 +73,6 @@
         for (Object s : session) frameSession.add((ContentCaptureData) s);
         return frameSession;
     }
+
+    private native void nativeInit(WebContents webContents);
 }
diff --git a/components/content_capture/browser/content_capture_receiver_test.cc b/components/content_capture/browser/content_capture_receiver_test.cc
index f01eed7..9ada1eb1 100644
--- a/components/content_capture/browser/content_capture_receiver_test.cc
+++ b/components/content_capture/browser/content_capture_receiver_test.cc
@@ -123,8 +123,6 @@
     test_data2_.children.push_back(child);
   }
 
-  void TearDown() override { content::RenderViewHostTestHarness::TearDown(); }
-
   void SetupChildFrame() {
     child_content_capture_sender_ =
         std::make_unique<FakeContentCaptureSender>();
diff --git a/components/content_capture/renderer/content_capture_sender.cc b/components/content_capture/renderer/content_capture_sender.cc
index 1772c811..989ec63 100644
--- a/components/content_capture/renderer/content_capture_sender.cc
+++ b/components/content_capture/renderer/content_capture_sender.cc
@@ -4,6 +4,7 @@
 
 #include "components/content_capture/renderer/content_capture_sender.h"
 
+#include "base/metrics/histogram_macros.h"
 #include "components/content_capture/common/content_capture_data.h"
 #include "components/content_capture/common/content_capture_features.h"
 #include "content/public/renderer/render_frame.h"
@@ -43,6 +44,8 @@
   ContentCaptureData frame_data;
   FillContentCaptureData(&frame_data, first_data /* set_url */);
 
+  frame_data.children.reserve(data.size());
+  base::TimeTicks start = base::TimeTicks::Now();
   for (auto holder : data) {
     ContentCaptureData child;
     child.id = holder->GetId();
@@ -50,6 +53,10 @@
     child.bounds = holder->GetBoundingBox();
     frame_data.children.push_back(child);
   }
+  UMA_HISTOGRAM_CUSTOM_MICROSECONDS_TIMES(
+      "ContentCapture.GetBoundingBox", base::TimeTicks::Now() - start,
+      base::TimeDelta::FromMicroseconds(1),
+      base::TimeDelta::FromMilliseconds(10), 50);
   GetContentCaptureReceiver()->DidCaptureContent(frame_data, first_data);
 }
 
diff --git a/components/crash/content/app/crash_reporter_client.cc b/components/crash/content/app/crash_reporter_client.cc
index b3b09ce..c3c3f9d 100644
--- a/components/crash/content/app/crash_reporter_client.cc
+++ b/components/crash/content/app/crash_reporter_client.cc
@@ -148,7 +148,7 @@
 }
 
 #if defined(OS_ANDROID)
-unsigned int CrashReporterClient::GetCrashDumpPercentageForWebView() {
+unsigned int CrashReporterClient::GetCrashDumpPercentage() {
   return 100;
 }
 
diff --git a/components/crash/content/app/crash_reporter_client.h b/components/crash/content/app/crash_reporter_client.h
index caebf67..1d35caa3 100644
--- a/components/crash/content/app/crash_reporter_client.h
+++ b/components/crash/content/app/crash_reporter_client.h
@@ -156,7 +156,7 @@
   // Used by WebView to sample crashes without generating the unwanted dumps. If
   // the returned value is less than 100, crash dumping will be sampled to that
   // percentage.
-  virtual unsigned int GetCrashDumpPercentageForWebView();
+  virtual unsigned int GetCrashDumpPercentage();
 
   // Returns true if |ptype| was set to a value to override the default `ptype`
   // annotation used for the browser process.
diff --git a/components/crash/content/app/crashpad_linux.cc b/components/crash/content/app/crashpad_linux.cc
index cd865e97..8e47645 100644
--- a/components/crash/content/app/crashpad_linux.cc
+++ b/components/crash/content/app/crashpad_linux.cc
@@ -21,6 +21,7 @@
 #include "base/path_service.h"
 #include "base/posix/eintr_wrapper.h"
 #include "base/posix/global_descriptors.h"
+#include "base/rand_util.h"
 #include "base/stl_util.h"
 #include "base/strings/stringprintf.h"
 #include "build/build_config.h"
@@ -700,8 +701,9 @@
   base::android::SetJavaExceptionCallback(SetJavaExceptionInfo);
 
   unsigned int dump_percentage =
-      GetCrashReporterClient()->GetCrashDumpPercentageForWebView();
-  if (dump_percentage < 100 && rand() % 100 >= dump_percentage) {
+      GetCrashReporterClient()->GetCrashDumpPercentage();
+  if (dump_percentage < 100 &&
+      static_cast<unsigned int>(base::RandInt(0, 99)) >= dump_percentage) {
     dump_at_crash = false;
   }
 #endif  // OS_ANDROID
diff --git a/components/download/public/common/download_features.cc b/components/download/public/common/download_features.cc
index cf01b32c..66d60d3 100644
--- a/components/download/public/common/download_features.cc
+++ b/components/download/public/common/download_features.cc
@@ -9,6 +9,9 @@
 namespace download {
 namespace features {
 
+const base::Feature kUseDownloadOfflineContentProvider{
+    "UseDownloadOfflineContentProvider", base::FEATURE_DISABLED_BY_DEFAULT};
+
 const base::Feature kDownloadAutoResumptionNative {
   "DownloadsAutoResumptionNative",
 #if defined(OS_ANDROID)
diff --git a/components/download/public/common/download_features.h b/components/download/public/common/download_features.h
index 9b6b8943..b54842ba 100644
--- a/components/download/public/common/download_features.h
+++ b/components/download/public/common/download_features.h
@@ -11,6 +11,10 @@
 namespace download {
 namespace features {
 
+// Whether offline content provider should be used for the downloads UI..
+COMPONENTS_DOWNLOAD_EXPORT extern const base::Feature
+    kUseDownloadOfflineContentProvider;
+
 // Whether download auto-resumptions are enabled in native.
 COMPONENTS_DOWNLOAD_EXPORT extern const base::Feature
     kDownloadAutoResumptionNative;
diff --git a/components/invalidation/impl/BUILD.gn b/components/invalidation/impl/BUILD.gn
index 2ea9beb..c18d087a 100644
--- a/components/invalidation/impl/BUILD.gn
+++ b/components/invalidation/impl/BUILD.gn
@@ -19,6 +19,8 @@
 
 static_library("impl") {
   sources = [
+    "channels_states.cc",
+    "channels_states.h",
     "deprecated_invalidator_registrar.cc",
     "deprecated_invalidator_registrar.h",
     "fcm_invalidation_listener.cc",
diff --git a/components/invalidation/impl/channels_states.cc b/components/invalidation/impl/channels_states.cc
new file mode 100644
index 0000000..5ba702b6
--- /dev/null
+++ b/components/invalidation/impl/channels_states.cc
@@ -0,0 +1,20 @@
+// 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/invalidation/impl/channels_states.h"
+
+namespace syncer {
+
+const char* FcmChannelStateToString(FcmChannelState state) {
+  switch (state) {
+    case FcmChannelState::NOT_STARTED:
+      return "NOT_STARTED";
+    case FcmChannelState::ENABLED:
+      return "ENABLED";
+    case FcmChannelState::NO_INSTANCE_ID_TOKEN:
+      return "NO_INSTANCE_ID_TOKEN";
+  }
+}
+
+}  // namespace syncer
diff --git a/components/invalidation/impl/channels_states.h b/components/invalidation/impl/channels_states.h
new file mode 100644
index 0000000..2bc3eac9
--- /dev/null
+++ b/components/invalidation/impl/channels_states.h
@@ -0,0 +1,24 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_INVALIDATION_IMPL_CHANNELS_STATES_H_
+#define COMPONENTS_INVALIDATION_IMPL_CHANNELS_STATES_H_
+
+namespace syncer {
+
+enum class FcmChannelState {
+  NOT_STARTED,
+  // Fcm network channel is working properly.
+  ENABLED,
+  // Failed to retrieve instance id token.
+  NO_INSTANCE_ID_TOKEN,
+
+  kMaxValue = NO_INSTANCE_ID_TOKEN,
+};
+
+const char* FcmChannelStateToString(FcmChannelState state);
+
+}  // namespace syncer
+
+#endif  // COMPONENTS_INVALIDATION_IMPL_CHANNELS_STATES_H_
diff --git a/components/invalidation/impl/fcm_invalidation_listener.cc b/components/invalidation/impl/fcm_invalidation_listener.cc
index 9eade211..783c531b 100644
--- a/components/invalidation/impl/fcm_invalidation_listener.cc
+++ b/components/invalidation/impl/fcm_invalidation_listener.cc
@@ -23,8 +23,6 @@
     std::unique_ptr<FCMSyncNetworkChannel> network_channel)
     : network_channel_(std::move(network_channel)),
       delegate_(nullptr),
-      subscription_channel_state_(DEFAULT_INVALIDATION_ERROR),
-      fcm_network_state_(DEFAULT_INVALIDATION_ERROR),
       weak_factory_(this) {
   network_channel_->AddObserver(this);
 }
@@ -202,18 +200,17 @@
   per_user_topic_registration_manager_.reset();
 
   subscription_channel_state_ = DEFAULT_INVALIDATION_ERROR;
-  fcm_network_state_ = DEFAULT_INVALIDATION_ERROR;
+  fcm_network_state_ = FcmChannelState::NOT_STARTED;
 }
 
 InvalidatorState FCMInvalidationListener::GetState() const {
-  if (subscription_channel_state_ == INVALIDATION_CREDENTIALS_REJECTED ||
-      fcm_network_state_ == INVALIDATION_CREDENTIALS_REJECTED) {
+  if (subscription_channel_state_ == INVALIDATION_CREDENTIALS_REJECTED) {
     // If either the ticl or the push client rejected our credentials,
     // return INVALIDATION_CREDENTIALS_REJECTED.
     return INVALIDATION_CREDENTIALS_REJECTED;
   }
   if (subscription_channel_state_ == INVALIDATIONS_ENABLED &&
-      fcm_network_state_ == INVALIDATIONS_ENABLED) {
+      fcm_network_state_ == FcmChannelState::ENABLED) {
     // If the ticl is ready and the push client notifications are
     // enabled, return INVALIDATIONS_ENABLED.
     return INVALIDATIONS_ENABLED;
@@ -229,9 +226,8 @@
   delegate_->OnInvalidatorStateChange(GetState());
 }
 
-void FCMInvalidationListener::OnFCMSyncNetworkChannelStateChanged(
-    InvalidatorState invalidator_state) {
-  fcm_network_state_ = invalidator_state;
+void FCMInvalidationListener::OnFCMChannelStateChanged(FcmChannelState state) {
+  fcm_network_state_ = state;
   EmitStateChange();
 }
 
@@ -245,7 +241,7 @@
   base::DictionaryValue status =
       per_user_topic_registration_manager_->CollectDebugData();
   status.SetString("InvalidationListener.FCM-channel-state",
-                   InvalidatorStateToString(fcm_network_state_));
+                   FcmChannelStateToString(fcm_network_state_));
   status.SetString("InvalidationListener.Subscription-channel-state",
                    InvalidatorStateToString(subscription_channel_state_));
   for (const Topic& topic : registered_topics_) {
diff --git a/components/invalidation/impl/fcm_invalidation_listener.h b/components/invalidation/impl/fcm_invalidation_listener.h
index ed04fcf..2269093a 100644
--- a/components/invalidation/impl/fcm_invalidation_listener.h
+++ b/components/invalidation/impl/fcm_invalidation_listener.h
@@ -10,6 +10,7 @@
 #include "base/callback_forward.h"
 #include "base/macros.h"
 #include "base/memory/weak_ptr.h"
+#include "components/invalidation/impl/channels_states.h"
 #include "components/invalidation/impl/fcm_sync_network_channel.h"
 #include "components/invalidation/impl/invalidation_client.h"
 #include "components/invalidation/impl/invalidation_listener.h"
@@ -82,8 +83,7 @@
             const syncer::AckHandle& handle) override;
 
   // FCMSyncNetworkChannel::Observer implementation.
-  void OnFCMSyncNetworkChannelStateChanged(
-      InvalidatorState invalidator_state) override;
+  void OnFCMChannelStateChanged(FcmChannelState state) override;
 
   // PerUserTopicRegistrationManager::Observer implementation.
   void OnSubscriptionChannelStateChanged(
@@ -139,8 +139,8 @@
   TopicSet registered_topics_;
 
   // The states of the HTTP and FCM channel.
-  InvalidatorState subscription_channel_state_;
-  InvalidatorState fcm_network_state_;
+  InvalidatorState subscription_channel_state_ = DEFAULT_INVALIDATION_ERROR;
+  FcmChannelState fcm_network_state_ = FcmChannelState::NOT_STARTED;
 
   std::unique_ptr<PerUserTopicRegistrationManager>
       per_user_topic_registration_manager_;
diff --git a/components/invalidation/impl/fcm_invalidation_listener_unittest.cc b/components/invalidation/impl/fcm_invalidation_listener_unittest.cc
index c2f73f3..85dd8691 100644
--- a/components/invalidation/impl/fcm_invalidation_listener_unittest.cc
+++ b/components/invalidation/impl/fcm_invalidation_listener_unittest.cc
@@ -301,11 +301,12 @@
   }
 
   void EnableNotifications() {
-    fcm_sync_network_channel_->NotifyChannelStateChange(INVALIDATIONS_ENABLED);
+    fcm_sync_network_channel_->NotifyChannelStateChange(
+        FcmChannelState::ENABLED);
     listener_.InformTokenRecieved(fake_invalidation_client_, "token");
   }
 
-  void DisableNotifications(InvalidatorState state) {
+  void DisableNotifications(FcmChannelState state) {
     fcm_sync_network_channel_->NotifyChannelStateChange(state);
   }
 
@@ -478,7 +479,7 @@
 TEST_F(FCMInvalidationListenerTest, EnableNotificationsNotReady) {
   EXPECT_EQ(TRANSIENT_INVALIDATION_ERROR, GetInvalidatorState());
 
-  DisableNotifications(TRANSIENT_INVALIDATION_ERROR);
+  DisableNotifications(FcmChannelState::NOT_STARTED);
 
   EXPECT_EQ(TRANSIENT_INVALIDATION_ERROR, GetInvalidatorState());
 
@@ -516,17 +517,17 @@
 }
 
 // Enable notifications and ready the client.  Then disable
-// notifications with an auth error and re-enable notifications.  The
-// delegate should go into an auth error mode and then back out.
-TEST_F(FCMInvalidationListenerTest, PushClientAuthError) {
+// notifications with no_token error.  The delegate should
+// go into an error and then back out.
+TEST_F(FCMInvalidationListenerTest, FcmChannelNoTokenError) {
   EnableNotifications();
   listener_.Ready(fake_invalidation_client_);
 
   EXPECT_EQ(INVALIDATIONS_ENABLED, GetInvalidatorState());
 
-  DisableNotifications(INVALIDATION_CREDENTIALS_REJECTED);
+  DisableNotifications(FcmChannelState::NO_INSTANCE_ID_TOKEN);
 
-  EXPECT_EQ(INVALIDATION_CREDENTIALS_REJECTED, GetInvalidatorState());
+  EXPECT_EQ(TRANSIENT_INVALIDATION_ERROR, GetInvalidatorState());
 
   EnableNotifications();
 
diff --git a/components/invalidation/impl/fcm_network_handler.cc b/components/invalidation/impl/fcm_network_handler.cc
index 9f9fef6..72388ece 100644
--- a/components/invalidation/impl/fcm_network_handler.cc
+++ b/components/invalidation/impl/fcm_network_handler.cc
@@ -128,7 +128,7 @@
       // validation.
       DeliverToken(subscription_token);
       token_ = subscription_token;
-      UpdateGcmChannelState(/* online */ true);
+      UpdateChannelState(FcmChannelState::ENABLED);
       break;
     case InstanceID::INVALID_PARAMETER:
     case InstanceID::DISABLED:
@@ -138,7 +138,7 @@
     case InstanceID::NETWORK_ERROR:
       DLOG(WARNING) << "Messaging subscription failed; InstanceID::Result = "
                     << result;
-      UpdateGcmChannelState(/* online */ false);
+      UpdateChannelState(FcmChannelState::NO_INSTANCE_ID_TOKEN);
       break;
   }
   ScheduleNextTokenValidation();
@@ -178,6 +178,7 @@
   diagnostic_info_.instance_id_token_verified = base::Time::Now();
   diagnostic_info_.token_verification_result = result;
   if (result == InstanceID::SUCCESS) {
+    UpdateChannelState(FcmChannelState::ENABLED);
     if (token_ != new_token) {
       diagnostic_info_.token_changed = true;
       token_ = new_token;
@@ -188,12 +189,11 @@
   ScheduleNextTokenValidation();
 }
 
-void FCMNetworkHandler::UpdateGcmChannelState(bool online) {
-  if (gcm_channel_online_ == online)
+void FCMNetworkHandler::UpdateChannelState(FcmChannelState state) {
+  if (channel_state_ == state)
     return;
-  gcm_channel_online_ = online;
-  NotifyChannelStateChange(gcm_channel_online_ ? INVALIDATIONS_ENABLED
-                                               : TRANSIENT_INVALIDATION_ERROR);
+  channel_state_ = state;
+  NotifyChannelStateChange(channel_state_);
 }
 
 void FCMNetworkHandler::ShutdownHandler() {}
diff --git a/components/invalidation/impl/fcm_network_handler.h b/components/invalidation/impl/fcm_network_handler.h
index 4c283de..185017c 100644
--- a/components/invalidation/impl/fcm_network_handler.h
+++ b/components/invalidation/impl/fcm_network_handler.h
@@ -10,6 +10,7 @@
 #include "base/timer/timer.h"
 #include "components/gcm_driver/gcm_app_handler.h"
 #include "components/gcm_driver/instance_id/instance_id.h"
+#include "components/invalidation/impl/channels_states.h"
 #include "components/invalidation/impl/fcm_sync_network_channel.h"
 #include "components/prefs/pref_registry_simple.h"
 #include "components/prefs/pref_service.h"
@@ -68,7 +69,7 @@
   void StartListening();
   void StopListening();
   bool IsListening() const;
-  void UpdateGcmChannelState(bool);
+  void UpdateChannelState(FcmChannelState state);
 
   // GCMAppHandler overrides.
   void ShutdownHandler() override;
@@ -100,7 +101,7 @@
   gcm::GCMDriver* const gcm_driver_;
   instance_id::InstanceIDDriver* const instance_id_driver_;
 
-  bool gcm_channel_online_ = false;
+  FcmChannelState channel_state_ = FcmChannelState::NOT_STARTED;
   std::string token_;
 
   std::unique_ptr<base::OneShotTimer> token_validation_timer_;
diff --git a/components/invalidation/impl/fcm_sync_network_channel.cc b/components/invalidation/impl/fcm_sync_network_channel.cc
index 3e43b48..58b001f 100644
--- a/components/invalidation/impl/fcm_sync_network_channel.cc
+++ b/components/invalidation/impl/fcm_sync_network_channel.cc
@@ -29,10 +29,9 @@
   observers_.RemoveObserver(observer);
 }
 
-void FCMSyncNetworkChannel::NotifyChannelStateChange(
-    InvalidatorState invalidator_state) {
+void FCMSyncNetworkChannel::NotifyChannelStateChange(FcmChannelState state) {
   for (auto& observer : observers_)
-    observer.OnFCMSyncNetworkChannelStateChanged(invalidator_state);
+    observer.OnFCMChannelStateChanged(state);
 }
 
 bool FCMSyncNetworkChannel::DeliverIncomingMessage(
diff --git a/components/invalidation/impl/fcm_sync_network_channel.h b/components/invalidation/impl/fcm_sync_network_channel.h
index 1b47189..a409e9c 100644
--- a/components/invalidation/impl/fcm_sync_network_channel.h
+++ b/components/invalidation/impl/fcm_sync_network_channel.h
@@ -11,8 +11,8 @@
 #include "base/callback.h"
 #include "base/observer_list.h"
 #include "base/values.h"
+#include "components/invalidation/impl/channels_states.h"
 #include "components/invalidation/impl/network_channel.h"
-#include "components/invalidation/public/invalidator_state.h"
 
 namespace syncer {
 
@@ -24,8 +24,8 @@
  public:
   class Observer {
    public:
-    virtual void OnFCMSyncNetworkChannelStateChanged(
-        InvalidatorState invalidator_state) = 0;
+    virtual void OnFCMChannelStateChanged(
+        FcmChannelState invalidator_state) = 0;
   };
 
   FCMSyncNetworkChannel();
@@ -51,7 +51,7 @@
   // NotifyChannelStateChange. If communication doesn't work and it is possible
   // that invalidations from server will not reach this client then channel
   // should call this function with TRANSIENT_INVALIDATION_ERROR.
-  void NotifyChannelStateChange(InvalidatorState invalidator_state);
+  void NotifyChannelStateChange(FcmChannelState invalidator_state);
 
   // Subclass should call DeliverIncomingMessage for message to reach
   // invalidations library.
diff --git a/components/management_strings.grdp b/components/management_strings.grdp
index 84894b1..60a0008c 100644
--- a/components/management_strings.grdp
+++ b/components/management_strings.grdp
@@ -130,7 +130,7 @@
     Information about installed extensions and plugins
   </message>
   <message name="IDS_MANAGEMENT_EXTENSION_REPORT_SAFE_BROWSING_WARNINGS" desc="Message explaining that an extension currently reports the user's safe browsing warnings and ignored warnings">
-    Safe Browsing warnings
+    <ph name="BEGIN_LINK">&lt;a target="_blank" href="$1"&gt;</ph>Safe Browsing<ph name="END_LINK">&lt;/a&gt;</ph> warnings
   </message>
   <message name="IDS_MANAGEMENT_EXTENSION_REPORT_USER_BROWSING_DATA" desc="Message explaining that an extension currently reports the user's browsing data">
     Websites you visit and time spent on them
diff --git a/components/ntp_tiles/custom_links_manager_impl_unittest.cc b/components/ntp_tiles/custom_links_manager_impl_unittest.cc
index eceabe13b..88411c6 100644
--- a/components/ntp_tiles/custom_links_manager_impl_unittest.cc
+++ b/components/ntp_tiles/custom_links_manager_impl_unittest.cc
@@ -51,11 +51,13 @@
 const char kTestUrl[] = "http://test.com/";
 
 base::Value::ListStorage FillTestListStorage(const char* url,
-                                             const char* title) {
+                                             const char* title,
+                                             const bool is_most_visited) {
   base::Value::ListStorage new_link_list;
   base::DictionaryValue new_link;
   new_link.SetKey("url", base::Value(url));
   new_link.SetKey("title", base::Value(title));
+  new_link.SetKey("isMostVisited", base::Value(is_most_visited));
   new_link_list.push_back(std::move(new_link));
   return new_link_list;
 }
@@ -495,6 +497,35 @@
   scoped_task_environment_.RunUntilIdle();
 }
 
+TEST_F(CustomLinksManagerImplTest, ShouldDeleteOnHistoryDeletionAfterShutdown) {
+  // Initialize.
+  ASSERT_TRUE(custom_links_->Initialize(FillTestTiles(kTestCase2)));
+  ASSERT_EQ(FillTestLinks(kTestCase2), custom_links_->GetLinks());
+
+  // Simulate shutdown by recreating CustomLinksManagerImpl.
+  custom_links_.reset();
+  custom_links_ =
+      std::make_unique<CustomLinksManagerImpl>(&prefs_, history_service_.get());
+
+  // Set up Most Visited callback.
+  base::MockCallback<base::RepeatingClosure> callback;
+  std::unique_ptr<base::CallbackList<void()>::Subscription> subscription =
+      custom_links_->RegisterCallbackForOnChanged(callback.Get());
+
+  // Delete all Most Visited links.
+  EXPECT_CALL(callback, Run());
+  static_cast<history::HistoryServiceObserver*>(custom_links_.get())
+      ->OnURLsDeleted(
+          history_service_.get(),
+          history::DeletionInfo(history::DeletionTimeRange::AllTime(),
+                                /*expired=*/false, history::URLRows(),
+                                /*favicon_urls=*/std::set<GURL>(),
+                                /*restrict_urls=*/base::nullopt));
+  EXPECT_TRUE(custom_links_->GetLinks().empty());
+
+  scoped_task_environment_.RunUntilIdle();
+}
+
 TEST_F(CustomLinksManagerImplTest, ShouldNotDeleteCustomLinkOnHistoryDeletion) {
   // Set up Most Visited callback.
   base::MockCallback<base::RepeatingClosure> callback;
@@ -648,11 +679,11 @@
   // Modify the preference. This should notify and update the current list of
   // links.
   EXPECT_CALL(callback, Run());
-  prefs_.SetUserPref(
-      prefs::kCustomLinksList,
-      std::make_unique<base::Value>(FillTestListStorage(kTestUrl, kTestTitle)));
+  prefs_.SetUserPref(prefs::kCustomLinksList,
+                     std::make_unique<base::Value>(
+                         FillTestListStorage(kTestUrl, kTestTitle, true)));
   EXPECT_EQ(std::vector<Link>(
-                {Link{GURL(kTestUrl), base::UTF8ToUTF16(kTestTitle), false}}),
+                {Link{GURL(kTestUrl), base::UTF8ToUTF16(kTestTitle), true}}),
             custom_links_->GetLinks());
 }
 
@@ -668,9 +699,9 @@
   EXPECT_CALL(callback, Run()).Times(2);
   prefs_.SetUserPref(prefs::kCustomLinksInitialized,
                      std::make_unique<base::Value>(true));
-  prefs_.SetUserPref(
-      prefs::kCustomLinksList,
-      std::make_unique<base::Value>(FillTestListStorage(kTestUrl, kTestTitle)));
+  prefs_.SetUserPref(prefs::kCustomLinksList,
+                     std::make_unique<base::Value>(
+                         FillTestListStorage(kTestUrl, kTestTitle, false)));
   EXPECT_TRUE(custom_links_->IsInitialized());
   EXPECT_EQ(std::vector<Link>(
                 {Link{GURL(kTestUrl), base::UTF8ToUTF16(kTestTitle), false}}),
diff --git a/components/ntp_tiles/custom_links_store.cc b/components/ntp_tiles/custom_links_store.cc
index 53f38616..799e3c60 100644
--- a/components/ntp_tiles/custom_links_store.cc
+++ b/components/ntp_tiles/custom_links_store.cc
@@ -22,6 +22,7 @@
 
 const char* kDictionaryKeyUrl = "url";
 const char* kDictionaryKeyTitle = "title";
+const char* kDictionaryKeyIsMostVisited = "isMostVisited";
 
 }  // namespace
 
@@ -40,14 +41,20 @@
   for (const base::Value& link : stored_links->GetList()) {
     const base::Value* url_value = link.FindKey(kDictionaryKeyUrl);
     const base::Value* title_value = link.FindKey(kDictionaryKeyTitle);
+    const base::Value* mv_value = link.FindKey(kDictionaryKeyIsMostVisited);
+
     GURL url = GURL(url_value->GetString());
     if (!url_value || !title_value || !url.is_valid()) {
       ClearLinks();
       links.clear();
       return links;
     }
+    // Assume false if this value was not stored.
+    bool is_most_visited = mv_value ? mv_value->GetBool() : false;
+
     links.emplace_back(CustomLinksManager::Link{
-        std::move(url), base::UTF8ToUTF16(title_value->GetString())});
+        std::move(url), base::UTF8ToUTF16(title_value->GetString()),
+        is_most_visited});
   }
   return links;
 }
@@ -59,6 +66,8 @@
     base::DictionaryValue new_link;
     new_link.SetKey(kDictionaryKeyUrl, base::Value(link.url.spec()));
     new_link.SetKey(kDictionaryKeyTitle, base::Value(link.title));
+    new_link.SetKey(kDictionaryKeyIsMostVisited,
+                    base::Value(link.is_most_visited));
     new_link_list.push_back(std::move(new_link));
   }
   prefs_->Set(prefs::kCustomLinksList, base::Value(std::move(new_link_list)));
diff --git a/components/ntp_tiles/custom_links_store_unittest.cc b/components/ntp_tiles/custom_links_store_unittest.cc
index df4f394..efa4cfa 100644
--- a/components/ntp_tiles/custom_links_store_unittest.cc
+++ b/components/ntp_tiles/custom_links_store_unittest.cc
@@ -28,41 +28,42 @@
 
 class CustomLinksStoreTest : public testing::Test {
  public:
-  CustomLinksStoreTest() : custom_links_store_(&prefs_) {
+  CustomLinksStoreTest() {
+    custom_links_store_ = std::make_unique<CustomLinksStore>(&prefs_);
     CustomLinksStore::RegisterProfilePrefs(prefs_.registry());
   }
 
  protected:
   sync_preferences::TestingPrefServiceSyncable prefs_;
-  CustomLinksStore custom_links_store_;
+  std::unique_ptr<CustomLinksStore> custom_links_store_;
 
   DISALLOW_COPY_AND_ASSIGN(CustomLinksStoreTest);
 };
 
 TEST_F(CustomLinksStoreTest, StoreAndRetrieveLinks) {
   std::vector<CustomLinksManager::Link> initial_links({CustomLinksManager::Link{
-      GURL(kTestUrl1), base::UTF8ToUTF16(kTestTitle1)}});
+      GURL(kTestUrl1), base::UTF8ToUTF16(kTestTitle1), true}});
 
-  custom_links_store_.StoreLinks(initial_links);
+  custom_links_store_->StoreLinks(initial_links);
   std::vector<CustomLinksManager::Link> retrieved_links =
-      custom_links_store_.RetrieveLinks();
+      custom_links_store_->RetrieveLinks();
   EXPECT_EQ(initial_links, retrieved_links);
 }
 
 TEST_F(CustomLinksStoreTest, StoreEmptyList) {
   std::vector<CustomLinksManager::Link> populated_links(
-      {CustomLinksManager::Link{GURL(kTestUrl1),
-                                base::UTF8ToUTF16(kTestTitle1)},
-       CustomLinksManager::Link{GURL(kTestUrl2),
-                                base::UTF8ToUTF16(kTestTitle2)}});
+      {CustomLinksManager::Link{GURL(kTestUrl1), base::UTF8ToUTF16(kTestTitle1),
+                                false},
+       CustomLinksManager::Link{GURL(kTestUrl2), base::UTF8ToUTF16(kTestTitle2),
+                                true}});
 
-  custom_links_store_.StoreLinks(populated_links);
+  custom_links_store_->StoreLinks(populated_links);
   std::vector<CustomLinksManager::Link> retrieved_links =
-      custom_links_store_.RetrieveLinks();
+      custom_links_store_->RetrieveLinks();
   ASSERT_EQ(populated_links, retrieved_links);
 
-  custom_links_store_.StoreLinks(std::vector<CustomLinksManager::Link>());
-  retrieved_links = custom_links_store_.RetrieveLinks();
+  custom_links_store_->StoreLinks(std::vector<CustomLinksManager::Link>());
+  retrieved_links = custom_links_store_->RetrieveLinks();
   EXPECT_TRUE(retrieved_links.empty());
 }
 
@@ -70,14 +71,33 @@
   std::vector<CustomLinksManager::Link> initial_links({CustomLinksManager::Link{
       GURL(kTestUrl1), base::UTF8ToUTF16(kTestTitle1)}});
 
-  custom_links_store_.StoreLinks(initial_links);
+  custom_links_store_->StoreLinks(initial_links);
   std::vector<CustomLinksManager::Link> retrieved_links =
-      custom_links_store_.RetrieveLinks();
+      custom_links_store_->RetrieveLinks();
   ASSERT_EQ(initial_links, retrieved_links);
 
-  custom_links_store_.ClearLinks();
-  retrieved_links = custom_links_store_.RetrieveLinks();
+  custom_links_store_->ClearLinks();
+  retrieved_links = custom_links_store_->RetrieveLinks();
   EXPECT_TRUE(retrieved_links.empty());
 }
 
+TEST_F(CustomLinksStoreTest, LinksSavedAfterShutdown) {
+  std::vector<CustomLinksManager::Link> initial_links(
+      {CustomLinksManager::Link{GURL(kTestUrl1), base::UTF8ToUTF16(kTestTitle1),
+                                false},
+       CustomLinksManager::Link{GURL(kTestUrl2), base::UTF8ToUTF16(kTestTitle2),
+                                true}});
+
+  custom_links_store_->StoreLinks(initial_links);
+  std::vector<CustomLinksManager::Link> retrieved_links =
+      custom_links_store_->RetrieveLinks();
+  ASSERT_EQ(initial_links, retrieved_links);
+
+  // Simulate shutdown by recreating CustomLinksStore.
+  custom_links_store_.reset();
+  custom_links_store_ = std::make_unique<CustomLinksStore>(&prefs_);
+  retrieved_links = custom_links_store_->RetrieveLinks();
+  EXPECT_EQ(initial_links, retrieved_links);
+}
+
 }  // namespace ntp_tiles
diff --git a/components/pairing/BUILD.gn b/components/pairing/BUILD.gn
index 0d176de..ee1b970 100644
--- a/components/pairing/BUILD.gn
+++ b/components/pairing/BUILD.gn
@@ -31,7 +31,7 @@
   deps = [
     ":proto",
     "//base",
-    "//chromeos:chromeos_constants",
+    "//chromeos/constants",
     "//device/bluetooth",
     "//net",
     "//services/device/public/mojom",
diff --git a/components/password_manager/core/browser/login_database.cc b/components/password_manager/core/browser/login_database.cc
index 6d1b7d8..b134dc6 100644
--- a/components/password_manager/core/browser/login_database.cc
+++ b/components/password_manager/core/browser/login_database.cc
@@ -1073,15 +1073,20 @@
 
 bool LoginDatabase::RemoveLoginByPrimaryKey(int primary_key,
                                             PasswordStoreChangeList* changes) {
-  PrimaryKeyToFormMap key_to_form_map;
+  PasswordForm form;
   if (changes) {
     changes->clear();
     sql::Statement s1(db_.GetCachedStatement(
         SQL_FROM_HERE, "SELECT * FROM logins WHERE id = ?"));
     s1.BindInt(0, primary_key);
-    if (!StatementToForms(&s1, nullptr, &key_to_form_map)) {
+    if (!s1.Step()) {
       return false;
     }
+    int db_primary_key = -1;
+    EncryptionResult result = InitPasswordFormFromStatement(
+        s1, /*decrypt_and_fill_password_value=*/false, &db_primary_key, &form);
+    DCHECK_EQ(result, ENCRYPTION_RESULT_SUCCESS);
+    DCHECK_EQ(db_primary_key, primary_key);
   }
 
 #if defined(OS_IOS)
@@ -1095,8 +1100,8 @@
     return false;
   }
   if (changes) {
-    changes->emplace_back(PasswordStoreChange::REMOVE,
-                          *key_to_form_map[primary_key], primary_key);
+    changes->emplace_back(PasswordStoreChange::REMOVE, std::move(form),
+                          primary_key);
   }
   return true;
 }
@@ -1189,11 +1194,11 @@
   sql::Statement s(
       db_.GetCachedStatement(SQL_FROM_HERE, autosignin_statement_.c_str()));
   PrimaryKeyToFormMap key_to_form_map;
-  bool result = StatementToForms(&s, nullptr, &key_to_form_map);
+  FormRetrievalResult result = StatementToForms(&s, nullptr, &key_to_form_map);
   for (auto& pair : key_to_form_map) {
     forms->push_back(std::move(pair.second));
   }
-  return result;
+  return result == FormRetrievalResult::kSuccess;
 }
 
 bool LoginDatabase::DisableAutoSignInForOrigin(const GURL& origin) {
@@ -1207,17 +1212,20 @@
 
 LoginDatabase::EncryptionResult LoginDatabase::InitPasswordFormFromStatement(
     const sql::Statement& s,
+    bool decrypt_and_fill_password_value,
     int* primary_key,
     PasswordForm* form) const {
   std::string encrypted_password;
   s.ColumnBlobAsString(COLUMN_PASSWORD_VALUE, &encrypted_password);
   base::string16 decrypted_password;
-  EncryptionResult encryption_result =
-      DecryptedString(encrypted_password, &decrypted_password);
-  if (encryption_result != ENCRYPTION_RESULT_SUCCESS) {
-    VLOG(0) << "Password decryption failed, encryption_result is "
-            << encryption_result;
-    return encryption_result;
+  if (decrypt_and_fill_password_value) {
+    EncryptionResult encryption_result =
+        DecryptedString(encrypted_password, &decrypted_password);
+    if (encryption_result != ENCRYPTION_RESULT_SUCCESS) {
+      VLOG(0) << "Password decryption failed, encryption_result is "
+              << encryption_result;
+      return encryption_result;
+    }
   }
 
   *primary_key = s.ColumnInt(COLUMN_ID);
@@ -1354,10 +1362,10 @@
                               PSL_DOMAIN_MATCH_COUNT);
   }
   PrimaryKeyToFormMap key_to_form_map;
-  bool success = StatementToForms(
+  FormRetrievalResult result = StatementToForms(
       &s, should_PSL_matching_apply || should_federated_apply ? &form : nullptr,
       &key_to_form_map);
-  if (!success) {
+  if (result != FormRetrievalResult::kSuccess) {
     return false;
   }
   for (auto& pair : key_to_form_map) {
@@ -1394,7 +1402,7 @@
   s.BindString(0, signon_realms_with_same_organization_name_regexp);
 
   PrimaryKeyToFormMap key_to_form_map;
-  bool success = StatementToForms(&s, nullptr, &key_to_form_map);
+  FormRetrievalResult result = StatementToForms(&s, nullptr, &key_to_form_map);
   for (auto& pair : key_to_form_map) {
     forms->push_back(std::move(pair.second));
   }
@@ -1409,7 +1417,7 @@
     return candidate_form_organization_name != organization_name;
   });
 
-  return success;
+  return result == FormRetrievalResult::kSuccess;
 }
 
 bool LoginDatabase::GetLoginsCreatedBetween(
@@ -1424,7 +1432,8 @@
   s.BindInt64(1, end.is_null() ? std::numeric_limits<int64_t>::max()
                                : end.ToInternalValue());
 
-  return StatementToForms(&s, nullptr, key_to_form_map);
+  return StatementToForms(&s, nullptr, key_to_form_map) ==
+         FormRetrievalResult::kSuccess;
 }
 
 bool LoginDatabase::GetLoginsSyncedBetween(
@@ -1440,10 +1449,12 @@
               end.is_null() ? base::Time::Max().ToInternalValue()
                             : end.ToInternalValue());
 
-  return StatementToForms(&s, nullptr, key_to_form_map);
+  return StatementToForms(&s, nullptr, key_to_form_map) ==
+         FormRetrievalResult::kSuccess;
 }
 
-bool LoginDatabase::GetAllLogins(PrimaryKeyToFormMap* key_to_form_map) {
+FormRetrievalResult LoginDatabase::GetAllLogins(
+    PrimaryKeyToFormMap* key_to_form_map) {
   DCHECK(key_to_form_map);
   key_to_form_map->clear();
 
@@ -1476,7 +1487,8 @@
 
   PrimaryKeyToFormMap key_to_form_map;
 
-  if (!StatementToForms(&s, nullptr, &key_to_form_map)) {
+  if (StatementToForms(&s, nullptr, &key_to_form_map) !=
+      FormRetrievalResult::kSuccess) {
     return false;
   }
 
@@ -1724,7 +1736,7 @@
   return nullptr;
 }
 
-bool LoginDatabase::StatementToForms(
+FormRetrievalResult LoginDatabase::StatementToForms(
     sql::Statement* statement,
     const PasswordStore::FormDigest* matched_form,
     PrimaryKeyToFormMap* key_to_form_map) {
@@ -1736,10 +1748,11 @@
   while (statement->Step()) {
     auto new_form = std::make_unique<PasswordForm>();
     int primary_key = -1;
-    EncryptionResult result =
-        InitPasswordFormFromStatement(*statement, &primary_key, new_form.get());
+    EncryptionResult result = InitPasswordFormFromStatement(
+        *statement, /*decrypt_and_fill_password_value=*/true, &primary_key,
+        new_form.get());
     if (result == ENCRYPTION_RESULT_SERVICE_FAILURE)
-      return false;
+      return FormRetrievalResult::kEncrytionServiceFailure;
     if (result == ENCRYPTION_RESULT_ITEM_FAILURE) {
       if (IsUsingCleanupMechanism())
         forms_to_be_deleted.push_back(GetFormForRemoval(*statement));
@@ -1800,8 +1813,8 @@
 #endif
 
   if (!statement->Succeeded())
-    return false;
-  return true;
+    return FormRetrievalResult::kDbError;
+  return FormRetrievalResult::kSuccess;
 }
 
 void LoginDatabase::InitializeStatementStrings(const SQLTableBuilder& builder) {
diff --git a/components/password_manager/core/browser/login_database.h b/components/password_manager/core/browser/login_database.h
index dfa165b..f7d8fcc 100644
--- a/components/password_manager/core/browser/login_database.h
+++ b/components/password_manager/core/browser/login_database.h
@@ -145,7 +145,8 @@
       WARN_UNUSED_RESULT;
 
   // Gets the complete list of all credentials.
-  bool GetAllLogins(PrimaryKeyToFormMap* key_to_form_map) WARN_UNUSED_RESULT;
+  FormRetrievalResult GetAllLogins(PrimaryKeyToFormMap* key_to_form_map)
+      WARN_UNUSED_RESULT;
 
   // Gets the complete list of not blacklisted credentials.
   bool GetAutofillableLogins(
@@ -255,11 +256,15 @@
 
   // Fills |form| from the values in the given statement (which is assumed to be
   // of the form used by the Get*Logins methods). Fills the corresponding DB
-  // primary key in |primary_key|. Returns the EncryptionResult from decrypting
-  // the password in |s|; if not ENCRYPTION_RESULT_SUCCESS, |form| is not
-  // filled.
+  // primary key in |primary_key|. If |decrypt_and_fill_password_value| is set
+  // to true, it tries to decrypt the stored password and returns the
+  // EncryptionResult from decrypting the password in |s|; if not
+  // ENCRYPTION_RESULT_SUCCESS, |form| is not filled. If
+  // |decrypt_and_fill_password_value| is set to false, it always returns
+  // ENCRYPTION_RESULT_SUCCESS.
   EncryptionResult InitPasswordFormFromStatement(
       const sql::Statement& s,
+      bool decrypt_and_fill_password_value,
       int* primary_key,
       autofill::PasswordForm* form) const WARN_UNUSED_RESULT;
 
@@ -290,17 +295,18 @@
   // Reads the stored ModelTypeState. Returns nullptr in case of failure.
   std::unique_ptr<sync_pb::ModelTypeState> GetModelTypeState();
 
-  // Overwrites |forms| with credentials retrieved from |statement|. If
-  // |matched_form| is not null, filters out all results but those PSL-matching
+  // Overwrites |key_to_form_map| with credentials retrieved from |statement|.
+  // If |matched_form| is not null, filters out all results but those
+  // PSL-matching
   // |*matched_form| or federated credentials for it. If feature for recovering
   // passwords is enabled, it removes all passwords that couldn't be decrypted
   // when encryption was available from the database. On success returns true.
   // |key_to_form_map| must not be null and will be used to return the results.
   // The key of the map is the DB primary key.
-  bool StatementToForms(sql::Statement* statement,
-                        const PasswordStore::FormDigest* matched_form,
-                        PrimaryKeyToFormMap* key_to_form_map)
-      WARN_UNUSED_RESULT;
+  FormRetrievalResult StatementToForms(
+      sql::Statement* statement,
+      const PasswordStore::FormDigest* matched_form,
+      PrimaryKeyToFormMap* key_to_form_map) WARN_UNUSED_RESULT;
 
   // Initializes all the *_statement_ data members with appropriate SQL
   // fragments based on |builder|.
diff --git a/components/password_manager/core/browser/login_database_unittest.cc b/components/password_manager/core/browser/login_database_unittest.cc
index fb7baba..93f83e81 100644
--- a/components/password_manager/core/browser/login_database_unittest.cc
+++ b/components/password_manager/core/browser/login_database_unittest.cc
@@ -277,7 +277,7 @@
   EXPECT_TRUE(db().GetAutofillableLogins(&result));
   EXPECT_EQ(0U, result.size());
 
-  EXPECT_TRUE(db().GetAllLogins(&key_to_form_map));
+  EXPECT_EQ(db().GetAllLogins(&key_to_form_map), FormRetrievalResult::kSuccess);
   EXPECT_EQ(0U, key_to_form_map.size());
 
   // Example password form.
@@ -294,7 +294,7 @@
   EXPECT_EQ(form, *result[0]);
   result.clear();
 
-  EXPECT_TRUE(db().GetAllLogins(&key_to_form_map));
+  EXPECT_EQ(db().GetAllLogins(&key_to_form_map), FormRetrievalResult::kSuccess);
   EXPECT_EQ(1U, key_to_form_map.size());
   EXPECT_EQ(form, *key_to_form_map[1]);
   key_to_form_map.clear();
@@ -427,6 +427,9 @@
   EXPECT_EQ(form, *result[0]);
   result.clear();
 
+  // RemoveLoginByPrimaryKey() doesn't decrypt or fill the password value.
+  form.password_value = ASCIIToUTF16("");
+
   EXPECT_TRUE(db().RemoveLoginByPrimaryKey(primary_key, &change_list));
   EXPECT_EQ(RemoveChangeForForm(form), change_list);
   EXPECT_TRUE(db().GetAutofillableLogins(&result));
diff --git a/components/password_manager/core/browser/mock_password_store.h b/components/password_manager/core/browser/mock_password_store.h
index e18bd59c..9c116d6 100644
--- a/components/password_manager/core/browser/mock_password_store.h
+++ b/components/password_manager/core/browser/mock_password_store.h
@@ -90,7 +90,7 @@
 #endif
   MOCK_METHOD0(BeginTransaction, bool());
   MOCK_METHOD0(CommitTransaction, bool());
-  MOCK_METHOD1(ReadAllLogins, bool(PrimaryKeyToFormMap*));
+  MOCK_METHOD1(ReadAllLogins, FormRetrievalResult(PrimaryKeyToFormMap*));
   MOCK_METHOD1(RemoveLoginByPrimaryKeySync, PasswordStoreChangeList(int));
   MOCK_METHOD0(GetMetadataStore, PasswordStoreSync::MetadataStore*());
 
diff --git a/components/password_manager/core/browser/password_store_default.cc b/components/password_manager/core/browser/password_store_default.cc
index 8c894ae..961f67b5 100644
--- a/components/password_manager/core/browser/password_store_default.cc
+++ b/components/password_manager/core/browser/password_store_default.cc
@@ -239,9 +239,12 @@
   return false;
 }
 
-bool PasswordStoreDefault::ReadAllLogins(PrimaryKeyToFormMap* key_to_form_map) {
+FormRetrievalResult PasswordStoreDefault::ReadAllLogins(
+    PrimaryKeyToFormMap* key_to_form_map) {
   DCHECK(background_task_runner()->RunsTasksInCurrentSequence());
-  return login_db_ && login_db_->GetAllLogins(key_to_form_map);
+  if (!login_db_)
+    return FormRetrievalResult::kDbError;
+  return login_db_->GetAllLogins(key_to_form_map);
 }
 
 PasswordStoreChangeList PasswordStoreDefault::RemoveLoginByPrimaryKeySync(
diff --git a/components/password_manager/core/browser/password_store_default.h b/components/password_manager/core/browser/password_store_default.h
index 6b981af..e3d7a92 100644
--- a/components/password_manager/core/browser/password_store_default.h
+++ b/components/password_manager/core/browser/password_store_default.h
@@ -87,7 +87,8 @@
   // Implements PasswordStoreSync interface.
   bool BeginTransaction() override;
   bool CommitTransaction() override;
-  bool ReadAllLogins(PrimaryKeyToFormMap* key_to_form_map) override;
+  FormRetrievalResult ReadAllLogins(
+      PrimaryKeyToFormMap* key_to_form_map) override;
   PasswordStoreChangeList RemoveLoginByPrimaryKeySync(int primary_key) override;
   PasswordStoreSync::MetadataStore* GetMetadataStore() override;
 
diff --git a/components/password_manager/core/browser/password_store_sync.h b/components/password_manager/core/browser/password_store_sync.h
index 6a93de3..3b698f23 100644
--- a/components/password_manager/core/browser/password_store_sync.h
+++ b/components/password_manager/core/browser/password_store_sync.h
@@ -36,6 +36,17 @@
   kEncryptionUnavailable,
 };
 
+// Result values for retrieving form from the store.
+enum class FormRetrievalResult {
+  // Success.
+  kSuccess,
+  // Database error.
+  kDbError,
+  // A service-level failure (e.g., on a platform using a keyring, the keyring
+  // is temporarily unavailable).
+  kEncrytionServiceFailure,
+};
+
 // PasswordStore interface for PasswordSyncableService. It provides access to
 // synchronous methods of PasswordStore which shouldn't be accessible to other
 // classes. These methods are to be called on the PasswordStore background
@@ -67,8 +78,8 @@
 
   // Overwrites |key_to_form_map| with a map from the DB primary key to the
   // corresponding form for all stored credentials. Returns true on success.
-  virtual bool ReadAllLogins(PrimaryKeyToFormMap* key_to_form_map)
-      WARN_UNUSED_RESULT = 0;
+  virtual FormRetrievalResult ReadAllLogins(
+      PrimaryKeyToFormMap* key_to_form_map) WARN_UNUSED_RESULT = 0;
 
   // Deletes logins that cannot be decrypted.
   virtual DatabaseCleanupResult DeleteUndecryptableLogins() = 0;
diff --git a/components/password_manager/core/browser/sync/password_sync_bridge.cc b/components/password_manager/core/browser/sync/password_sync_bridge.cc
index 281423d..beadf7e 100644
--- a/components/password_manager/core/browser/sync/password_sync_bridge.cc
+++ b/components/password_manager/core/browser/sync/password_sync_bridge.cc
@@ -11,7 +11,7 @@
 #include "base/strings/string_number_conversions.h"
 #include "base/strings/utf_string_conversions.h"
 #include "components/autofill/core/common/password_form.h"
-#include "components/password_manager/core/browser/password_store_sync.h"
+#include "components/password_manager/core/common/password_manager_features.h"
 #include "components/sync/model/metadata_batch.h"
 #include "components/sync/model/metadata_change_list.h"
 #include "components/sync/model/model_type_change_processor.h"
@@ -145,6 +145,12 @@
                   .Serialize() == password_form.federation_origin.Serialize());
 }
 
+bool ShouldRecoverPasswordsDuringMerge() {
+  return base::FeatureList::IsEnabled(
+             features::kRecoverPasswordsForSyncUsers) &&
+         !base::FeatureList::IsEnabled(features::kDeleteCorruptedPasswords);
+}
+
 // A simple class for scoping a password store sync transaction. This does not
 // support rollback since the password store sync doesn't either.
 class ScopedStoreTransaction {
@@ -247,12 +253,36 @@
 
   base::AutoReset<bool> processing_changes(&is_processing_remote_sync_changes_,
                                            true);
+
   // Read all local passwords.
   PrimaryKeyToFormMap key_to_local_form_map;
-  if (!password_store_sync_->ReadAllLogins(&key_to_local_form_map)) {
+  FormRetrievalResult read_result =
+      password_store_sync_->ReadAllLogins(&key_to_local_form_map);
+
+  if (read_result == FormRetrievalResult::kDbError) {
     return syncer::ModelError(FROM_HERE,
                               "Failed to load entries from password store.");
   }
+  if (read_result == FormRetrievalResult::kEncrytionServiceFailure) {
+    if (!ShouldRecoverPasswordsDuringMerge()) {
+      return syncer::ModelError(FROM_HERE,
+                                "Failed to load entries from password store. "
+                                "Encryption service failure.");
+    }
+    base::Optional<syncer::ModelError> cleanup_result_error =
+        CleanupPasswordStore();
+    if (cleanup_result_error) {
+      return cleanup_result_error;
+    }
+    // Clean up done successfully, try to read again.
+    read_result = password_store_sync_->ReadAllLogins(&key_to_local_form_map);
+    if (read_result != FormRetrievalResult::kSuccess) {
+      return syncer::ModelError(
+          FROM_HERE,
+          "Failed to load entries from password store after cleanup.");
+    }
+  }
+  DCHECK_EQ(read_result, FormRetrievalResult::kSuccess);
 
   // Collect the client tags of remote passwords and the corresponding
   // EntityChange. Note that |entity_data| only contains client tag *hashes*.
@@ -488,7 +518,8 @@
   // There are more efficient implementations, but since this method is rarely
   // called, simplicity is preferred over efficiency.
   PrimaryKeyToFormMap key_to_form_map;
-  if (!password_store_sync_->ReadAllLogins(&key_to_form_map)) {
+  if (password_store_sync_->ReadAllLogins(&key_to_form_map) !=
+      FormRetrievalResult::kSuccess) {
     change_processor()->ReportError(
         {FROM_HERE, "Failed to load entries from the password store."});
     return;
@@ -508,7 +539,8 @@
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
 
   PrimaryKeyToFormMap key_to_form_map;
-  if (!password_store_sync_->ReadAllLogins(&key_to_form_map)) {
+  if (password_store_sync_->ReadAllLogins(&key_to_form_map) !=
+      FormRetrievalResult::kSuccess) {
     change_processor()->ReportError(
         {FROM_HERE, "Failed to load entries from the password store."});
     return;
@@ -556,4 +588,20 @@
       std::move(delete_metadata_change_list));
 }
 
+base::Optional<syncer::ModelError> PasswordSyncBridge::CleanupPasswordStore() {
+  DatabaseCleanupResult cleanup_result =
+      password_store_sync_->DeleteUndecryptableLogins();
+  switch (cleanup_result) {
+    case DatabaseCleanupResult::kSuccess:
+      break;
+    case DatabaseCleanupResult::kEncryptionUnavailable:
+      return syncer::ModelError(
+          FROM_HERE, "Failed to get encryption key during database cleanup.");
+    case DatabaseCleanupResult::kItemFailure:
+    case DatabaseCleanupResult::kDatabaseUnavailable:
+      return syncer::ModelError(FROM_HERE, "Failed to cleanup database.");
+  }
+  return base::nullopt;
+}
+
 }  // namespace password_manager
diff --git a/components/password_manager/core/browser/sync/password_sync_bridge.h b/components/password_manager/core/browser/sync/password_sync_bridge.h
index 7838ddbc..bc7def1 100644
--- a/components/password_manager/core/browser/sync/password_sync_bridge.h
+++ b/components/password_manager/core/browser/sync/password_sync_bridge.h
@@ -8,6 +8,7 @@
 #include "base/macros.h"
 #include "base/sequence_checker.h"
 #include "components/password_manager/core/browser/password_store_change.h"
+#include "components/password_manager/core/browser/password_store_sync.h"
 #include "components/sync/model/model_type_sync_bridge.h"
 
 namespace syncer {
@@ -58,6 +59,13 @@
                                 delete_metadata_change_list) override;
 
  private:
+  // On MacOS it may happen that some passwords cannot be decrypted due to
+  // modification of encryption key in Keychain (https://crbug.com/730625). This
+  // method deletes those logins from the store. So during merge, the data in
+  // sync will be added to the password store. This should be called during
+  // MergeSyncData().
+  base::Optional<syncer::ModelError> CleanupPasswordStore();
+
   // Password store responsible for persistence.
   PasswordStoreSync* const password_store_sync_;
 
diff --git a/components/password_manager/core/browser/sync/password_sync_bridge_unittest.cc b/components/password_manager/core/browser/sync/password_sync_bridge_unittest.cc
index fa9823f..40a5e9b 100644
--- a/components/password_manager/core/browser/sync/password_sync_bridge_unittest.cc
+++ b/components/password_manager/core/browser/sync/password_sync_bridge_unittest.cc
@@ -115,13 +115,13 @@
   FakeDatabase() = default;
   ~FakeDatabase() = default;
 
-  bool ReadAllLogins(PrimaryKeyToFormMap* map) {
+  FormRetrievalResult ReadAllLogins(PrimaryKeyToFormMap* map) {
     map->clear();
     for (const auto& pair : data_) {
       map->emplace(pair.first,
                    std::make_unique<autofill::PasswordForm>(*pair.second));
     }
-    return true;
+    return FormRetrievalResult::kSuccess;
   }
 
   PasswordStoreChangeList AddLogin(const autofill::PasswordForm& form) {
@@ -193,7 +193,7 @@
                bool(std::vector<std::unique_ptr<autofill::PasswordForm>>*));
   MOCK_METHOD1(FillBlacklistLogins,
                bool(std::vector<std::unique_ptr<autofill::PasswordForm>>*));
-  MOCK_METHOD1(ReadAllLogins, bool(PrimaryKeyToFormMap*));
+  MOCK_METHOD1(ReadAllLogins, FormRetrievalResult(PrimaryKeyToFormMap*));
   MOCK_METHOD1(RemoveLoginByPrimaryKeySync, PasswordStoreChangeList(int));
   MOCK_METHOD0(DeleteUndecryptableLogins, DatabaseCleanupResult());
   MOCK_METHOD1(AddLoginSync,
@@ -627,9 +627,9 @@
 // ShouldMergeSync() would return an error without crashing.
 TEST_F(PasswordSyncBridgeTest,
        ShouldMergeSyncRemoteAndLocalPasswordsWithErrorWhenStoreReadFails) {
-  // Simulate a failed ReadAllLogins() by returning false.
+  // Simulate a failed ReadAllLogins() by returning a kDbError.
   ON_CALL(*mock_password_store_sync(), ReadAllLogins(_))
-      .WillByDefault(testing::Return(false));
+      .WillByDefault(testing::Return(FormRetrievalResult::kDbError));
   base::Optional<syncer::ModelError> error =
       bridge()->MergeSyncData(bridge()->CreateMetadataChangeList(), {});
   EXPECT_TRUE(error);
@@ -697,4 +697,22 @@
                             mock_password_store_sync());
 }
 
+// Tests that in case ReadAllLogins() during initial merge returns encryption
+// service failure, the bridge would try to do a DB clean up.
+TEST_F(PasswordSyncBridgeTest, ShouldDeleteUndecryptableLoginsDuringMerge) {
+  ON_CALL(*mock_password_store_sync(), DeleteUndecryptableLogins())
+      .WillByDefault(Return(DatabaseCleanupResult::kSuccess));
+
+  // We should try to read first, and simulate an encryption failure. Then,
+  // cleanup the database and try to read again which should be successful now.
+  EXPECT_CALL(*mock_password_store_sync(), ReadAllLogins(_))
+      .WillOnce(Return(FormRetrievalResult::kEncrytionServiceFailure))
+      .WillOnce(Return(FormRetrievalResult::kSuccess));
+  EXPECT_CALL(*mock_password_store_sync(), DeleteUndecryptableLogins());
+
+  base::Optional<syncer::ModelError> error =
+      bridge()->MergeSyncData(bridge()->CreateMetadataChangeList(), {});
+  EXPECT_FALSE(error);
+}
+
 }  // 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 4d64475..80ba9bd0 100644
--- a/components/password_manager/core/browser/test_password_store.cc
+++ b/components/password_manager/core/browser/test_password_store.cc
@@ -220,9 +220,10 @@
   return true;
 }
 
-bool TestPasswordStore::ReadAllLogins(PrimaryKeyToFormMap* key_to_form_map) {
+FormRetrievalResult TestPasswordStore::ReadAllLogins(
+    PrimaryKeyToFormMap* key_to_form_map) {
   NOTIMPLEMENTED();
-  return true;
+  return FormRetrievalResult::kSuccess;
 }
 
 PasswordStoreChangeList TestPasswordStore::RemoveLoginByPrimaryKeySync(
diff --git a/components/password_manager/core/browser/test_password_store.h b/components/password_manager/core/browser/test_password_store.h
index 88d60c77..ba764c4 100644
--- a/components/password_manager/core/browser/test_password_store.h
+++ b/components/password_manager/core/browser/test_password_store.h
@@ -90,7 +90,8 @@
   // PasswordStoreSync interface.
   bool BeginTransaction() override;
   bool CommitTransaction() override;
-  bool ReadAllLogins(PrimaryKeyToFormMap* key_to_form_map) override;
+  FormRetrievalResult ReadAllLogins(
+      PrimaryKeyToFormMap* key_to_form_map) override;
   PasswordStoreChangeList RemoveLoginByPrimaryKeySync(int primary_key) override;
   PasswordStoreSync::MetadataStore* GetMetadataStore() override;
 
diff --git a/components/password_manager/ios/password_form_helper.h b/components/password_manager/ios/password_form_helper.h
index d619c0a..f74c69a9 100644
--- a/components/password_manager/ios/password_form_helper.h
+++ b/components/password_manager/ios/password_form_helper.h
@@ -93,7 +93,7 @@
 // Finds the password form named |formName| and calls
 // |completionHandler| with the populated |FormData| data structure. |found| is
 // YES if the current form was found successfully, NO otherwise.
-- (void)extractPasswordFormData:(const std::string&)formName
+- (void)extractPasswordFormData:(NSString*)formName
               completionHandler:
                   (void (^)(BOOL found,
                             const autofill::FormData& form))completionHandler;
diff --git a/components/password_manager/ios/password_form_helper.mm b/components/password_manager/ios/password_form_helper.mm
index 5aceefd..82dcf3f 100644
--- a/components/password_manager/ios/password_form_helper.mm
+++ b/components/password_manager/ios/password_form_helper.mm
@@ -427,7 +427,7 @@
 // Finds the password form named |formName| and calls
 // |completionHandler| with the populated |FormData| data structure. |found| is
 // YES if the current form was found successfully, NO otherwise.
-- (void)extractPasswordFormData:(const std::string&)formName
+- (void)extractPasswordFormData:(NSString*)formName
               completionHandler:(void (^)(BOOL found, const FormData& form))
                                     completionHandler {
   DCHECK(completionHandler);
@@ -459,7 +459,7 @@
     completionHandler(YES, formData);
   };
 
-  [self.jsPasswordManager extractForm:base::SysUTF8ToNSString(formName)
+  [self.jsPasswordManager extractForm:formName
                     completionHandler:extractFormDataCompletionHandler];
 }
 
diff --git a/components/password_manager/ios/password_form_helper_unittest.mm b/components/password_manager/ios/password_form_helper_unittest.mm
index bcb2e44..b7e21338 100644
--- a/components/password_manager/ios/password_form_helper_unittest.mm
+++ b/components/password_manager/ios/password_form_helper_unittest.mm
@@ -750,7 +750,7 @@
   __block int call_counter = 0;
   __block int success_counter = 0;
   __block FormData result = FormData();
-  [helper_ extractPasswordFormData:GetFormId(1)
+  [helper_ extractPasswordFormData:base::SysUTF8ToNSString(GetFormId(1))
                  completionHandler:^(BOOL complete, const FormData& form) {
                    ++call_counter;
                    if (complete) {
@@ -768,7 +768,7 @@
   success_counter = 0;
   result = FormData();
 
-  [helper_ extractPasswordFormData:"unknown"
+  [helper_ extractPasswordFormData:@"unknown"
                  completionHandler:^(BOOL complete, const FormData& form) {
                    ++call_counter;
                    if (complete) {
diff --git a/components/pdf_strings.grdp b/components/pdf_strings.grdp
index 0290c9c79..5810537 100644
--- a/components/pdf_strings.grdp
+++ b/components/pdf_strings.grdp
@@ -98,6 +98,15 @@
       <message name="IDS_PDF_ANNOTATION_ERASER" desc="Button tooltip for selecting a tool that erases strokes that were drawn on top of the PDF document">
         Eraser
       </message>
+      <message name="IDS_PDF_ANNOTATION_UNDO" desc="Button tooltip for undoing an annotation edit (drawing a stroke, erasing a stroke)">
+        Undo
+      </message>
+      <message name="IDS_PDF_ANNOTATION_REDO" desc="Button tooltip for redoing an annotation edit (drawing a stroke, erasing a stroke)">
+        Redo
+      </message>
+      <message name="IDS_PDF_ANNOTATION_EXPAND" desc="Button tooltip for expanding the color palette to show more colors">
+        Expand
+      </message>
 
       <message name="IDS_PDF_ANNOTATION_COLOR_BLACK" desc="Button tooltip for selecting the Black color for drawing on top of the PDF document">
         Black
diff --git a/components/policy/BUILD.gn b/components/policy/BUILD.gn
index 6d776e3..01ddddf 100644
--- a/components/policy/BUILD.gn
+++ b/components/policy/BUILD.gn
@@ -294,9 +294,7 @@
   component_build_force_source_set = true
   defines = [ "POLICY_PROTO_COMPILATION" ]
 
-  deps = [
-    ":cloud_policy_code_generate",
-  ]
+  proto_deps = [ ":cloud_policy_code_generate" ]
 }
 
 # This target builds the "full" protobuf, used for tests only.
@@ -320,10 +318,9 @@
   component_build_force_source_set = true
   defines = [ "POLICY_CHROME_SETTINGS_PROTO_COMPILATION" ]
 
-  deps = [
-    ":cloud_policy_code_generate",
-    ":cloud_policy_proto_generated_compile",
-  ]
+  proto_deps = [ ":cloud_policy_code_generate" ]
+
+  link_deps = [ ":cloud_policy_proto_generated_compile" ]
 }
 
 static_library("generated") {
diff --git a/components/policy/core/common/BUILD.gn b/components/policy/core/common/BUILD.gn
index 05b89101..b2da3a2 100644
--- a/components/policy/core/common/BUILD.gn
+++ b/components/policy/core/common/BUILD.gn
@@ -90,6 +90,7 @@
     "config_dir_policy_loader.h",
     "configuration_policy_provider.cc",
     "configuration_policy_provider.h",
+    "extension_policy_migrator.cc",
     "extension_policy_migrator.h",
     "external_data_fetcher.cc",
     "external_data_fetcher.h",
diff --git a/components/policy/core/common/cloud/cloud_policy_refresh_scheduler.h b/components/policy/core/common/cloud/cloud_policy_refresh_scheduler.h
index 6ac2703..05bd7c7 100644
--- a/components/policy/core/common/cloud/cloud_policy_refresh_scheduler.h
+++ b/components/policy/core/common/cloud/cloud_policy_refresh_scheduler.h
@@ -74,9 +74,7 @@
 
   // Whether the invalidations service is available and receiving notifications
   // of policy updates.
-  bool invalidations_available() {
-    return invalidations_available_;
-  }
+  bool invalidations_available() const { return invalidations_available_; }
 
   // CloudPolicyClient::Observer:
   void OnPolicyFetched(CloudPolicyClient* client) override;
diff --git a/components/policy/core/common/cloud/component_cloud_policy_service.cc b/components/policy/core/common/cloud/component_cloud_policy_service.cc
index 2a1ab68..fd71e59 100644
--- a/components/policy/core/common/cloud/component_cloud_policy_service.cc
+++ b/components/policy/core/common/cloud/component_cloud_policy_service.cc
@@ -7,6 +7,7 @@
 #include <stddef.h>
 
 #include <algorithm>
+#include <functional>
 #include <unordered_map>
 #include <utility>
 
@@ -251,8 +252,8 @@
   // Purge any components that don't have a policy configured at the server.
   // TODO(emaxx): This is insecure, as it happens before the policy validation:
   // see crbug.com/668733.
-  store_.Purge(base::BindRepeating(&NotInResponseMap,
-                                   base::ConstRef(*last_fetched_policy_)));
+  store_.Purge(
+      base::BindRepeating(&NotInResponseMap, std::cref(*last_fetched_policy_)));
 
   for (auto it = last_fetched_policy_->begin();
        it != last_fetched_policy_->end(); ++it) {
diff --git a/components/policy/core/common/extension_policy_migrator.cc b/components/policy/core/common/extension_policy_migrator.cc
new file mode 100644
index 0000000..f847c86
--- /dev/null
+++ b/components/policy/core/common/extension_policy_migrator.cc
@@ -0,0 +1,35 @@
+// 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/policy/core/common/extension_policy_migrator.h"
+
+#include <algorithm>
+#include <utility>
+
+#include "base/bind.h"
+
+namespace policy {
+
+namespace {
+
+void DoNothing(base::Value* val) {}
+
+}  // namespace
+
+ExtensionPolicyMigrator::Migration::Migration(Migration&&) = default;
+
+ExtensionPolicyMigrator::Migration::Migration(const char* old_name_,
+                                              const char* new_name_)
+    : Migration(old_name_, new_name_, base::BindRepeating(&DoNothing)) {}
+
+ExtensionPolicyMigrator::Migration::Migration(const char* old_name_,
+                                              const char* new_name_,
+                                              ValueTransform transform_)
+    : old_name(old_name_),
+      new_name(new_name_),
+      transform(std::move(transform_)) {}
+
+ExtensionPolicyMigrator::Migration::~Migration() = default;
+
+}  // namespace policy
diff --git a/components/policy/core/common/extension_policy_migrator.h b/components/policy/core/common/extension_policy_migrator.h
index 072d6ec7..e091cff 100644
--- a/components/policy/core/common/extension_policy_migrator.h
+++ b/components/policy/core/common/extension_policy_migrator.h
@@ -5,7 +5,11 @@
 #ifndef COMPONENTS_POLICY_CORE_COMMON_EXTENSION_POLICY_MIGRATOR_H_
 #define COMPONENTS_POLICY_CORE_COMMON_EXTENSION_POLICY_MIGRATOR_H_
 
+#include <memory>
+
+#include "base/callback.h"
 #include "base/containers/span.h"
+#include "base/values.h"
 #include "components/policy/core/common/policy_bundle.h"
 #include "components/policy/policy_export.h"
 
@@ -27,11 +31,24 @@
 
   // Indicates how to rename a policy when migrating from the extension domain
   // to the Chrome domain.
-  struct Migration {
+  struct POLICY_EXPORT Migration {
+    using ValueTransform = base::RepeatingCallback<void(base::Value*)>;
+
+    Migration(Migration&&);
+    Migration(const char* old_name_, const char* new_name_);
+    Migration(const char* old_name_,
+              const char* new_name_,
+              ValueTransform transform_);
+    ~Migration();
+
     // Old name for the policy, in the extension domain.
     const char* old_name;
     // New name for the policy, in the Chrome domain.
     const char* new_name;
+    // Function to use to convert values from the old namespace to the new
+    // namespace (e.g. convert value types). It should mutate the Value in
+    // place. By default, it does no transform.
+    ValueTransform transform;
   };
 };
 
diff --git a/components/policy/resources/policy_templates.json b/components/policy/resources/policy_templates.json
index e353f8f5..15b1c88 100644
--- a/components/policy/resources/policy_templates.json
+++ b/components/policy/resources/policy_templates.json
@@ -14931,6 +14931,29 @@
       If the policy is set to false or left unset, DTC is switched off and is not able to collect, process and report telemetry and diagnostics data from the device.
       If wilco DTC is available on the given device and the policy is set to true, collecting, processing and reporting of telemetry and diagnostics data is switched on.''',
     },
+    {
+      'name': 'AllowPopupsDuringPageUnload',
+      'type': 'main',
+      'schema': { 'type': 'boolean' },
+      'supported_on': ['chrome.*:74-', 'chrome_os:74-', 'android:74-'],
+      'features': {
+        'dynamic_refresh': False,
+        'per_profile': True,
+      },
+      'example_value': False,
+      'id': 533,
+      'caption': '''Allows a page to show popups during its unloading''',
+      'tags': [],
+      'desc': '''This policy allows an admin to specify that a page may show popups during its unloading.
+
+      When the policy is set to enabled, pages are allowed to show popups while they are being unloaded.
+
+      When the policy is set to disabled or not set, pages are not allowed to show popups while they are being unloaded, as per the spec (https://html.spec.whatwg.org/#apis-for-creating-and-navigating-browsing-contexts-by-name).
+
+      This policy will be removed in Chrome 82.
+
+      See https://www.chromestatus.com/feature/5989473649164288 .''',
+    },
   ],
 
   'messages': {
@@ -15096,5 +15119,5 @@
   },
   'placeholders': [],
   'deleted_policy_ids': [412],
-  'highest_id_currently_used':  532
+  'highest_id_currently_used':  533
 }
diff --git a/components/policy_strings.grdp b/components/policy_strings.grdp
index 83926d7..deca469 100644
--- a/components/policy_strings.grdp
+++ b/components/policy_strings.grdp
@@ -313,6 +313,15 @@
   <message name="IDS_POLICY_NOT_SPECIFIED" desc="Indicates if that device attribute has not specified yet.">
     Not Specified
   </message>
+  <message name="IDS_POLICY_LABEL_PUSH_POLICIES" desc="Label to indicates the policies can be pushed or not to chrome.">
+    Policies push:
+  </message>
+  <message name="IDS_POLICY_PUSH_POLICIES_ON" desc="Indicates the policies can be pushed to chrome.">
+    On
+  </message>
+  <message name="IDS_POLICY_PUSH_POLICIES_OFF" desc="Indicates the policies cannot be pushed to chrome.">
+    Off
+  </message>
   <message name="IDS_POLICY_NEVER_FETCHED" desc="Indicates that a policy fetch was never performed before.">
     Never
   </message>
diff --git a/components/previews/content/proto/BUILD.gn b/components/previews/content/proto/BUILD.gn
index 8bc279bc..5a75c08 100644
--- a/components/previews/content/proto/BUILD.gn
+++ b/components/previews/content/proto/BUILD.gn
@@ -4,25 +4,11 @@
 
 import("//third_party/protobuf/proto_library.gni")
 
-copy("hints_header_include") {
-  sources = [
-    "hints_header_include.h",
-  ]
-  outputs = [
-    "$root_gen_dir" + "/components/previews/content/proto/hints.pb.h",
-  ]
-  deps = [
-    "//components/optimization_guide/proto:optimization_guide_proto",
-  ]
-}
-
 proto_library("hint_cache_proto") {
   import_dirs = [ "//components/optimization_guide/proto" ]
   sources = [
     "hint_cache.proto",
   ]
-  deps = [
-    ":hints_header_include",
-    "//components/optimization_guide/proto:optimization_guide_proto",
-  ]
+  link_deps =
+      [ "//components/optimization_guide/proto:optimization_guide_proto" ]
 }
diff --git a/components/previews/content/proto/hints_header_include.h b/components/previews/content/proto/hints_header_include.h
deleted file mode 100644
index 3abb136..0000000
--- a/components/previews/content/proto/hints_header_include.h
+++ /dev/null
@@ -1,12 +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 COMPONENTS_PREVIEWS_CONTENT_PROTO_HINTS_HEADER_INCLUDE_H_
-#define COMPONENTS_PREVIEWS_CONTENT_PROTO_HINTS_HEADER_INCLUDE_H_
-
-// This is a bridge to include the generated proto header for the
-// optimization_guide proto.
-#include "components/optimization_guide/proto/hints.pb.h"
-
-#endif  // COMPONENTS_PREVIEWS_CONTENT_PROTO_HINTS_HEADER_INCLUDE_H_
diff --git a/components/safe_browsing/common/safebrowsing_constants.cc b/components/safe_browsing/common/safebrowsing_constants.cc
index 31436fc..b3d8400b4 100644
--- a/components/safe_browsing/common/safebrowsing_constants.cc
+++ b/components/safe_browsing/common/safebrowsing_constants.cc
@@ -16,24 +16,8 @@
 const base::FilePath::CharType kChannelIDFile[] =
     FILE_PATH_LITERAL(" Channel IDs");
 
-// The default URL prefix where browser fetches chunk updates, hashes,
-// and reports safe browsing hits and malware details.
-const char kSbDefaultURLPrefix[] =
-    "https://safebrowsing.google.com/safebrowsing";
-
-// The backup URL prefix used when there are issues establishing a connection
-// with the server at the primary URL.
-const char kSbBackupConnectErrorURLPrefix[] =
-    "https://alt1-safebrowsing.google.com/safebrowsing";
-
-// The backup URL prefix used when there are HTTP-specific issues with the
-// server at the primary URL.
-const char kSbBackupHttpErrorURLPrefix[] =
-    "https://alt2-safebrowsing.google.com/safebrowsing";
-
-// The backup URL prefix used when there are local network specific issues.
-const char kSbBackupNetworkErrorURLPrefix[] =
-    "https://alt3-safebrowsing.google.com/safebrowsing";
+// The URL for the Safe Browsing page.
+const char kSafeBrowsingUrl[] = "https://safebrowsing.google.com/";
 
 const char kCustomCancelReasonForURLLoader[] = "SafeBrowsing";
 
diff --git a/components/safe_browsing/common/safebrowsing_constants.h b/components/safe_browsing/common/safebrowsing_constants.h
index fd8e39f..8e64e7c 100644
--- a/components/safe_browsing/common/safebrowsing_constants.h
+++ b/components/safe_browsing/common/safebrowsing_constants.h
@@ -15,20 +15,8 @@
 extern const base::FilePath::CharType kCookiesFile[];
 extern const base::FilePath::CharType kChannelIDFile[];
 
-// The default URL prefix where browser fetches chunk updates, hashes,
-// and reports safe browsing hits and malware details.
-extern const char kSbDefaultURLPrefix[];
-
-// The backup URL prefix used when there are issues establishing a connection
-// with the server at the primary URL.
-extern const char kSbBackupConnectErrorURLPrefix[];
-
-// The backup URL prefix used when there are HTTP-specific issues with the
-// server at the primary URL.
-extern const char kSbBackupHttpErrorURLPrefix[];
-
-// The backup URL prefix used when there are local network specific issues.
-extern const char kSbBackupNetworkErrorURLPrefix[];
+// The URL for the Safe Browsing page.
+extern const char kSafeBrowsingUrl[];
 
 // When a network::mojom::URLLoader is cancelled because of SafeBrowsing, this
 // custom cancellation reason could be used to notify the implementation side.
@@ -36,7 +24,7 @@
 // details.
 extern const char kCustomCancelReasonForURLLoader[];
 
-// Returns the error_code to use when safe browsing blocks a request.
+// Returns the error_code to use when Safe Browsing blocks a request.
 int GetNetErrorCodeForSafeBrowsing();
 }
 
diff --git a/components/safe_browsing/db/BUILD.gn b/components/safe_browsing/db/BUILD.gn
index 0e5740fe..acda1f22 100644
--- a/components/safe_browsing/db/BUILD.gn
+++ b/components/safe_browsing/db/BUILD.gn
@@ -15,9 +15,7 @@
   sources = [
     "v4_store.proto",
   ]
-  deps = [
-    ":safebrowsing_proto",
-  ]
+  link_deps = [ ":safebrowsing_proto" ]
 }
 
 proto_library("metadata_proto") {
diff --git a/components/search_provider_logos/switches.cc b/components/search_provider_logos/switches.cc
index 13640cc4..0ee18289 100644
--- a/components/search_provider_logos/switches.cc
+++ b/components/search_provider_logos/switches.cc
@@ -9,6 +9,12 @@
 
 // Overrides the URL used to fetch the current Google Doodle.
 // Example: https://www.google.com/async/ddljson
+// Testing? Try:
+//   https://www.gstatic.com/chrome/ntp/doodle_test/ddljson_android0.json
+//   https://www.gstatic.com/chrome/ntp/doodle_test/ddljson_android1.json
+//   https://www.gstatic.com/chrome/ntp/doodle_test/ddljson_android2.json
+//   https://www.gstatic.com/chrome/ntp/doodle_test/ddljson_android3.json
+//   https://www.gstatic.com/chrome/ntp/doodle_test/ddljson_android4.json
 const char kGoogleDoodleUrl[] = "google-doodle-url";
 
 // Use a static URL for the logo of the default search engine.
diff --git a/components/send_tab_to_self/proto/BUILD.gn b/components/send_tab_to_self/proto/BUILD.gn
index 57e0838f..afe1e55 100644
--- a/components/send_tab_to_self/proto/BUILD.gn
+++ b/components/send_tab_to_self/proto/BUILD.gn
@@ -4,26 +4,10 @@
 
 import("//third_party/protobuf/proto_library.gni")
 
-copy("send_tab_to_self_header_include") {
-  sources = [
-    "send_tab_to_self_header_include.h",
-  ]
-  outputs = [
-    "$root_gen_dir" +
-        "/components/send_tab_to_self/proto/send_tab_to_self_specifics.pb.h",
-  ]
-  deps = [
-    "//components/sync/protocol:protocol",
-  ]
-}
-
 proto_library("send_tab_to_self_proto") {
   import_dirs = [ "//components/sync/protocol" ]
   sources = [
     "send_tab_to_self.proto",
   ]
-  deps = [
-    ":send_tab_to_self_header_include",
-    "//components/sync/protocol:protocol",
-  ]
+  link_deps = [ "//components/sync/protocol:protocol" ]
 }
diff --git a/components/send_tab_to_self/proto/send_tab_to_self_header_include.h b/components/send_tab_to_self/proto/send_tab_to_self_header_include.h
deleted file mode 100644
index b2bc370..0000000
--- a/components/send_tab_to_self/proto/send_tab_to_self_header_include.h
+++ /dev/null
@@ -1,12 +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 COMPONENTS_SEND_TAB_TO_SELF_PROTO_SEND_TAB_TO_SELF_HEADER_INCLUDE_H_
-#define COMPONENTS_SEND_TAB_TO_SELF_PROTO_SEND_TAB_TO_SELF_HEADER_INCLUDE_H_
-
-// This is a bridge to include the generated proto header for the
-// send tab to self proto.
-#include "components/sync/protocol/send_tab_to_self_specifics.pb.h"
-
-#endif  // COMPONENTS_SEND_TAB_TO_SELF_PROTO_SEND_TAB_TO_SELF_HEADER_INCLUDE_H_
diff --git a/components/spellcheck/browser/spell_check_host_impl.cc b/components/spellcheck/browser/spell_check_host_impl.cc
index a056b1e..d5482a5 100644
--- a/components/spellcheck/browser/spell_check_host_impl.cc
+++ b/components/spellcheck/browser/spell_check_host_impl.cc
@@ -63,14 +63,6 @@
 #endif
 }
 
-void SpellCheckHostImpl::ToggleSpellCheck(bool enabled, bool checked) {
-  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
-#if defined(OS_ANDROID)
-  if (!enabled)
-    session_bridge_.DisconnectSession();
-#endif
-}
-
 void SpellCheckHostImpl::CheckSpelling(const base::string16& word,
                                        int route_id,
                                        CheckSpellingCallback callback) {
@@ -89,3 +81,10 @@
   std::move(callback).Run(std::vector<base::string16>());
 }
 #endif  // BUILDFLAG(USE_BROWSER_SPELLCHECKER)
+
+#if defined(OS_ANDROID)
+void SpellCheckHostImpl::DisconnectSessionBridge() {
+  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+  session_bridge_.DisconnectSession();
+}
+#endif
diff --git a/components/spellcheck/browser/spell_check_host_impl.h b/components/spellcheck/browser/spell_check_host_impl.h
index a419ef80..f2a1d66e1 100644
--- a/components/spellcheck/browser/spell_check_host_impl.h
+++ b/components/spellcheck/browser/spell_check_host_impl.h
@@ -45,7 +45,6 @@
   void RequestTextCheck(const base::string16& text,
                         int route_id,
                         RequestTextCheckCallback callback) override;
-  void ToggleSpellCheck(bool enabled, bool checked) override;
   void CheckSpelling(const base::string16& word,
                      int route_id,
                      CheckSpellingCallback callback) override;
@@ -53,6 +52,11 @@
                           FillSuggestionListCallback callback) override;
 #endif  // BUILDFLAG(USE_BROWSER_SPELLCHECKER)
 
+#if defined(OS_ANDROID)
+  // spellcheck::mojom::SpellCheckHost:
+  void DisconnectSessionBridge() override;
+#endif
+
  private:
 #if defined(OS_ANDROID)
   // Android-specific object used to query the Android spellchecker.
diff --git a/components/spellcheck/browser/spellchecker_session_bridge_android.cc b/components/spellcheck/browser/spellchecker_session_bridge_android.cc
index 3a303d4..ab3b84d 100644
--- a/components/spellcheck/browser/spellchecker_session_bridge_android.cc
+++ b/components/spellcheck/browser/spellchecker_session_bridge_android.cc
@@ -55,10 +55,10 @@
   }
 
   // RequestTextCheck API call arrives at the SpellCheckHost before
-  // ToggleSpellCheck when the user focuses an input field that already
+  // DisconnectSessionBridge when the user focuses an input field that already
   // contains completed text.  We need to initialize the spellchecker here
-  // rather than in response to ToggleSpellCheck so that the existing text
-  // will be spellchecked immediately.
+  // rather than in response to DisconnectSessionBridge so that the existing
+  // text will be spellchecked immediately.
   if (java_object_.is_null()) {
     java_object_.Reset(Java_SpellCheckerSessionBridge_create(
         base::android::AttachCurrentThread(),
diff --git a/components/spellcheck/common/spellcheck.mojom b/components/spellcheck/common/spellcheck.mojom
index 5587d26f..828724bd 100644
--- a/components/spellcheck/common/spellcheck.mojom
+++ b/components/spellcheck/common/spellcheck.mojom
@@ -57,9 +57,9 @@
   RequestTextCheck(mojo_base.mojom.String16 text, int32 route_id) =>
       (array<SpellCheckResult> results);
 
-  // Toggles the enabled state of the platform-specific spell checker.
-  [EnableIf=USE_BROWSER_SPELLCHECKER]
-  ToggleSpellCheck(bool enabled, bool checked);
+  // Disconnects the Android spell checker session bridge.
+  [EnableIf=is_android]
+  DisconnectSessionBridge();
 
   // Checks the correctness of a word with a platform-specific spell checker.
   // TODO(groby): This needs to originate from SpellcheckProvider.
diff --git a/components/spellcheck/renderer/spellcheck_provider.cc b/components/spellcheck/renderer/spellcheck_provider.cc
index ad3325e..17bbd9c 100644
--- a/components/spellcheck/renderer/spellcheck_provider.cc
+++ b/components/spellcheck/renderer/spellcheck_provider.cc
@@ -6,6 +6,7 @@
 
 #include "base/bind.h"
 #include "base/metrics/histogram_macros.h"
+#include "build/build_config.h"
 #include "components/spellcheck/common/spellcheck.mojom.h"
 #include "components/spellcheck/common/spellcheck_result.h"
 #include "components/spellcheck/renderer/spellcheck.h"
@@ -144,15 +145,18 @@
 }
 
 void SpellCheckProvider::FocusedNodeChanged(const blink::WebNode& unused) {
-#if BUILDFLAG(USE_BROWSER_SPELLCHECKER)
+#if defined(OS_ANDROID)
+  if (!spell_check_host_.is_bound())
+    return;
+
   WebLocalFrame* frame = render_frame()->GetWebFrame();
   WebElement element = frame->GetDocument().IsNull()
                            ? WebElement()
                            : frame->GetDocument().FocusedElement();
   bool enabled = !element.IsNull() && element.IsEditable();
-  bool checked = enabled && IsSpellCheckingEnabled();
-  GetSpellCheckHost().ToggleSpellCheck(enabled, checked);
-#endif  // USE_BROWSER_SPELLCHECKER
+  if (!enabled)
+    GetSpellCheckHost().DisconnectSessionBridge();
+#endif  // defined(OS_ANDROID)
 }
 
 bool SpellCheckProvider::IsSpellCheckingEnabled() const {
diff --git a/components/spellcheck/renderer/spellcheck_provider_test.cc b/components/spellcheck/renderer/spellcheck_provider_test.cc
index 94fb1eb..4eb50cf 100644
--- a/components/spellcheck/renderer/spellcheck_provider_test.cc
+++ b/components/spellcheck/renderer/spellcheck_provider_test.cc
@@ -109,10 +109,6 @@
   text_check_requests_.push_back(std::make_pair(text, std::move(callback)));
 }
 
-void TestingSpellCheckProvider::ToggleSpellCheck(bool, bool) {
-  NOTREACHED();
-}
-
 void TestingSpellCheckProvider::CheckSpelling(const base::string16&,
                                               int,
                                               CheckSpellingCallback) {
@@ -125,6 +121,12 @@
 }
 #endif  // BUILDFLAG(USE_BROWSER_SPELLCHECKER)
 
+#if defined(OS_ANDROID)
+void TestingSpellCheckProvider::DisconnectSessionBridge() {
+  NOTREACHED();
+}
+#endif
+
 void TestingSpellCheckProvider::SetLastResults(
     const base::string16 last_request,
     blink::WebVector<blink::WebTextCheckingResult>& last_results) {
diff --git a/components/spellcheck/renderer/spellcheck_provider_test.h b/components/spellcheck/renderer/spellcheck_provider_test.h
index 9e8534e..65e4558c 100644
--- a/components/spellcheck/renderer/spellcheck_provider_test.h
+++ b/components/spellcheck/renderer/spellcheck_provider_test.h
@@ -9,6 +9,7 @@
 #include <vector>
 
 #include "base/strings/string16.h"
+#include "build/build_config.h"
 #include "components/spellcheck/renderer/empty_local_interface_provider.h"
 #include "components/spellcheck/renderer/spellcheck_provider.h"
 #include "mojo/public/cpp/bindings/binding.h"
@@ -88,7 +89,6 @@
   void RequestTextCheck(const base::string16&,
                         int,
                         RequestTextCheckCallback) override;
-  void ToggleSpellCheck(bool, bool) override;
   using SpellCheckProvider::CheckSpelling;
   void CheckSpelling(const base::string16&,
                      int,
@@ -97,6 +97,10 @@
                           FillSuggestionListCallback) override;
 #endif
 
+#if defined(OS_ANDROID)
+  void DisconnectSessionBridge() override;
+#endif
+
   // Message loop (if needed) to deliver the SpellCheckHost request flow.
   std::unique_ptr<base::MessageLoop> loop_;
 
diff --git a/components/sync/BUILD.gn b/components/sync/BUILD.gn
index 1b2c5c9..129a828 100644
--- a/components/sync/BUILD.gn
+++ b/components/sync/BUILD.gn
@@ -605,7 +605,7 @@
 
   if (is_chromeos) {
     # Required by get_session_name.cc on Chrome OS.
-    deps += [ "//chromeos:chromeos_constants" ]
+    deps += [ "//chromeos/constants" ]
   }
 
   if (is_mac) {
@@ -989,7 +989,7 @@
 
   if (is_chromeos) {
     # Required by get_session_name_unittest.cc on Chrome OS.
-    deps += [ "//chromeos:chromeos_constants" ]
+    deps += [ "//chromeos/constants" ]
   }
 
   if (is_ios) {
diff --git a/components/sync/driver/about_sync_util.cc b/components/sync/driver/about_sync_util.cc
index 3af491f..19c69404 100644
--- a/components/sync/driver/about_sync_util.cc
+++ b/components/sync/driver/about_sync_util.cc
@@ -197,6 +197,8 @@
     reason_strings.push_back("User choice");
   if (disable_reasons & syncer::SyncService::DISABLE_REASON_UNRECOVERABLE_ERROR)
     reason_strings.push_back("Unrecoverable error");
+  if (disable_reasons & syncer::SyncService::DISABLE_REASON_PAUSED)
+    reason_strings.push_back("Paused");
   return base::JoinString(reason_strings, ", ");
 }
 
diff --git a/components/sync/driver/sync_service.h b/components/sync/driver/sync_service.h
index 6390822..dc143b7 100644
--- a/components/sync/driver/sync_service.h
+++ b/components/sync/driver/sync_service.h
@@ -73,7 +73,11 @@
     // Sync has encountered an unrecoverable error. It won't attempt to start
     // again until either the browser is restarted, or the user fully signs out
     // and back in again.
-    DISABLE_REASON_UNRECOVERABLE_ERROR = 1 << 4
+    DISABLE_REASON_UNRECOVERABLE_ERROR = 1 << 4,
+    // Sync is paused because the user signed out on the web. This is different
+    // from NOT_SIGNED_IN: In this case, there *is* still a primary account, but
+    // it doesn't have valid credentials.
+    DISABLE_REASON_PAUSED = 1 << 5,
   };
 
   // The overall state of the SyncService, in ascending order of "activeness".
diff --git a/components/translate/core/browser/translate_prefs.cc b/components/translate/core/browser/translate_prefs.cc
index 0bc1a0b3..26d58d0 100644
--- a/components/translate/core/browser/translate_prefs.cc
+++ b/components/translate/core/browser/translate_prefs.cc
@@ -38,7 +38,7 @@
 namespace translate {
 
 const char kForceTriggerTranslateCount[] =
-    "translate_force_trigger_on_english_count_for_backoff";
+    "translate_force_trigger_on_english_count_for_backoff_1";
 const char TranslatePrefs::kPrefTranslateSiteBlacklistDeprecated[] =
     "translate_site_blacklist";
 const char TranslatePrefs::kPrefTranslateSiteBlacklistWithTime[] =
diff --git a/components/ukm/content/source_url_recorder.cc b/components/ukm/content/source_url_recorder.cc
index cb3f5f9..106f0b6 100644
--- a/components/ukm/content/source_url_recorder.cc
+++ b/components/ukm/content/source_url_recorder.cc
@@ -8,6 +8,7 @@
 
 #include "base/containers/flat_map.h"
 #include "base/macros.h"
+#include "base/metrics/field_trial_params.h"
 #include "content/public/browser/navigation_handle.h"
 #include "content/public/browser/web_contents.h"
 #include "content/public/browser/web_contents_binding_set.h"
@@ -15,12 +16,28 @@
 #include "content/public/browser/web_contents_user_data.h"
 #include "services/metrics/public/cpp/delegating_ukm_recorder.h"
 #include "services/metrics/public/cpp/ukm_builders.h"
+#include "services/metrics/public/cpp/ukm_recorder.h"
 #include "services/metrics/public/cpp/ukm_source_id.h"
 #include "third_party/blink/public/mojom/ukm/ukm.mojom.h"
 #include "url/gurl.h"
 
 namespace ukm {
 
+namespace {
+
+// -1 indicates no max number of same document sources per full source.
+int kUnlimitedSameDocumentSourcesPerFullSource = -1;
+
+// Returns the maximum number of same document sources that are allowed to be
+// recorded for a full source.
+int GetMaxSameDocumentSourcesPerFullSource() {
+  return base::GetFieldTrialParamByFeatureAsInt(
+      kUkmFeature, "MaxSameDocumentSourcesPerFullSource",
+      kUnlimitedSameDocumentSourcesPerFullSource);
+}
+
+}  // namespace
+
 namespace internal {
 
 int64_t CreateUniqueTabId() {
@@ -71,6 +88,12 @@
   // Record any pending DocumentCreated events to UKM.
   void MaybeFlushPendingEvents();
 
+  void HandleSameDocumentNavigation(
+      content::NavigationHandle* navigation_handle);
+  void HandleDifferentDocumentNavigation(
+      content::NavigationHandle* navigation_handle,
+      const GURL& initial_url);
+
   void MaybeRecordUrl(content::NavigationHandle* navigation_handle,
                       const GURL& initial_url);
 
@@ -97,7 +120,13 @@
   };
   std::vector<PendingEvent> pending_document_created_events_;
 
-  SourceId last_committed_source_id_;
+  // The source id of the last committed full navigation (where a full
+  // navigation is a non-same-document navigation).
+  SourceId last_committed_full_navigation_source_id_;
+
+  // The source id of the last committed navigation, either full navigation or
+  // same document.
+  SourceId last_committed_full_navigation_or_same_document_source_id_;
 
   // The source id of the last committed source in the tab that opened this tab.
   // Will be set to kInvalidSourceId after the first navigation in this tab is
@@ -106,6 +135,8 @@
 
   const int64_t tab_id_;
 
+  int num_same_document_sources_for_full_navigation_source_;
+
   WEB_CONTENTS_USER_DATA_KEY_DECL();
 
   DISALLOW_COPY_AND_ASSIGN(SourceUrlRecorderWebContentsObserver);
@@ -117,9 +148,12 @@
     content::WebContents* web_contents)
     : content::WebContentsObserver(web_contents),
       bindings_(web_contents, this),
-      last_committed_source_id_(ukm::kInvalidSourceId),
+      last_committed_full_navigation_source_id_(ukm::kInvalidSourceId),
+      last_committed_full_navigation_or_same_document_source_id_(
+          ukm::kInvalidSourceId),
       opener_source_id_(ukm::kInvalidSourceId),
-      tab_id_(CreateUniqueTabId()) {}
+      tab_id_(CreateUniqueTabId()),
+      num_same_document_sources_for_full_navigation_source_(0) {}
 
 void SourceUrlRecorderWebContentsObserver::DidStartNavigation(
     content::NavigationHandle* navigation_handle) {
@@ -147,15 +181,54 @@
 void SourceUrlRecorderWebContentsObserver::DidFinishNavigation(
     content::NavigationHandle* navigation_handle) {
   auto it = pending_navigations_.find(navigation_handle->GetNavigationId());
-  if (it == pending_navigations_.end())
+  if (!navigation_handle->IsInMainFrame()) {
+    DCHECK(it == pending_navigations_.end());
+    return;
+  }
+
+  if (navigation_handle->IsSameDocument()) {
+    DCHECK(it == pending_navigations_.end());
+    HandleSameDocumentNavigation(navigation_handle);
+    return;
+  }
+
+  if (it != pending_navigations_.end()) {
+    GURL initial_url = std::move(it->second);
+    pending_navigations_.erase(it);
+    HandleDifferentDocumentNavigation(navigation_handle, initial_url);
+  }
+}
+
+void SourceUrlRecorderWebContentsObserver::HandleSameDocumentNavigation(
+    content::NavigationHandle* navigation_handle) {
+  if (!navigation_handle->HasCommitted())
     return;
 
-  DCHECK(navigation_handle->IsInMainFrame());
-  DCHECK(!navigation_handle->IsSameDocument());
+  // Only record same document sources if we were also recording the associated
+  // full source.
+  if (last_committed_full_navigation_source_id_ == ukm::kInvalidSourceId) {
+    return;
+  }
 
-  GURL initial_url = std::move(it->second);
-  pending_navigations_.erase(it);
+  const int max_same_document_sources_per_full_source =
+      GetMaxSameDocumentSourcesPerFullSource();
 
+  if (max_same_document_sources_per_full_source ==
+          kUnlimitedSameDocumentSourcesPerFullSource ||
+      num_same_document_sources_for_full_navigation_source_ <
+          max_same_document_sources_per_full_source) {
+    MaybeRecordUrl(navigation_handle, GURL::EmptyGURL());
+  }
+
+  last_committed_full_navigation_or_same_document_source_id_ =
+      ukm::ConvertToSourceId(navigation_handle->GetNavigationId(),
+                             ukm::SourceIdType::NAVIGATION_ID);
+  ++num_same_document_sources_for_full_navigation_source_;
+}
+
+void SourceUrlRecorderWebContentsObserver::HandleDifferentDocumentNavigation(
+    content::NavigationHandle* navigation_handle,
+    const GURL& initial_url) {
   // UKM doesn't want to record URLs for navigations that result in downloads.
   if (navigation_handle->IsDownload())
     return;
@@ -163,8 +236,11 @@
   MaybeRecordUrl(navigation_handle, initial_url);
 
   if (navigation_handle->HasCommitted()) {
-    last_committed_source_id_ = ukm::ConvertToSourceId(
+    last_committed_full_navigation_source_id_ = ukm::ConvertToSourceId(
         navigation_handle->GetNavigationId(), ukm::SourceIdType::NAVIGATION_ID);
+    last_committed_full_navigation_or_same_document_source_id_ =
+        last_committed_full_navigation_source_id_;
+    num_same_document_sources_for_full_navigation_source_ = 0;
   }
 
   MaybeFlushPendingEvents();
@@ -192,7 +268,7 @@
 
 ukm::SourceId SourceUrlRecorderWebContentsObserver::GetLastCommittedSourceId()
     const {
-  return last_committed_source_id_;
+  return last_committed_full_navigation_source_id_;
 }
 
 void SourceUrlRecorderWebContentsObserver::SetDocumentSourceId(
@@ -212,7 +288,7 @@
 }
 
 void SourceUrlRecorderWebContentsObserver::MaybeFlushPendingEvents() {
-  if (!last_committed_source_id_)
+  if (!last_committed_full_navigation_source_id_)
     return;
 
   ukm::DelegatingUkmRecorder* ukm_recorder = ukm::DelegatingUkmRecorder::Get();
@@ -223,7 +299,7 @@
     auto record = pending_document_created_events_.back();
 
     ukm::builders::DocumentCreated(record.source_id)
-        .SetNavigationSourceId(last_committed_source_id_)
+        .SetNavigationSourceId(last_committed_full_navigation_source_id_)
         .SetIsMainFrame(record.is_main_frame)
         .SetIsCrossOriginFrame(record.is_cross_origin_frame)
         .Record(ukm_recorder);
@@ -236,7 +312,6 @@
     content::NavigationHandle* navigation_handle,
     const GURL& initial_url) {
   DCHECK(navigation_handle->IsInMainFrame());
-  DCHECK(!navigation_handle->IsSameDocument());
 
   ukm::DelegatingUkmRecorder* ukm_recorder = ukm::DelegatingUkmRecorder::Get();
   if (!ukm_recorder)
@@ -246,11 +321,27 @@
   const GURL& final_url = navigation_handle->GetURL();
   // TODO(crbug.com/869123): This check isn't quite correct, as self redirecting
   // is possible. This may also be changed to include the entire redirect chain.
-  if (final_url != initial_url)
+  // Additionally, since same-document navigations don't have initial URLs,
+  // ignore empty initial URLs.
+  if (!initial_url.is_empty() && final_url != initial_url)
     navigation_data.urls = {initial_url};
   navigation_data.urls.push_back(final_url);
 
-  navigation_data.previous_source_id = last_committed_source_id_;
+  navigation_data.is_same_document_navigation =
+      navigation_handle->IsSameDocument();
+  navigation_data.previous_source_id =
+      last_committed_full_navigation_source_id_;
+
+  // If the last_committed_full_navigation_or_same_document_source_id_ isn't
+  // equal to the last_committed_full_navigation_source_id_, it indicates the
+  // previous source was a same document navigation.
+  const bool previous_source_was_same_document_navigation =
+      last_committed_full_navigation_or_same_document_source_id_ !=
+      last_committed_full_navigation_source_id_;
+  if (previous_source_was_same_document_navigation) {
+    navigation_data.previous_same_document_source_id =
+        last_committed_full_navigation_or_same_document_source_id_;
+  }
   navigation_data.opener_source_id = opener_source_id_;
   navigation_data.tab_id = tab_id_;
 
diff --git a/components/ukm/content/source_url_recorder_test.cc b/components/ukm/content/source_url_recorder_test.cc
index aaed558..3acc43a 100644
--- a/components/ukm/content/source_url_recorder_test.cc
+++ b/components/ukm/content/source_url_recorder_test.cc
@@ -3,6 +3,7 @@
 // found in the LICENSE file.
 
 #include "components/ukm/content/source_url_recorder.h"
+#include "base/test/scoped_feature_list.h"
 #include "components/ukm/test_ukm_recorder.h"
 #include "content/public/browser/web_contents.h"
 #include "content/public/test/navigation_simulator.h"
@@ -10,6 +11,7 @@
 #include "services/metrics/public/cpp/ukm_recorder.h"
 #include "services/metrics/public/cpp/ukm_source.h"
 #include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/metrics_proto/ukm/source.pb.h"
 #include "url/gurl.h"
 
 using content::NavigationSimulator;
@@ -81,7 +83,84 @@
   EXPECT_EQ(main_frame_url, GetAssociatedURLForWebContentsDocument());
 }
 
-TEST_F(SourceUrlRecorderWebContentsObserverTest, IgnoreSameDocumentNavigation) {
+TEST_F(SourceUrlRecorderWebContentsObserverTest, SameDocumentNavigation) {
+  GURL url1("https://www.example.com/");
+  GURL url2("https://www.example.com/2");
+  GURL same_document_url1("https://www.example.com/#samedocument");
+  GURL same_document_url2("https://www.example.com/#samedocument2");
+  NavigationSimulator::NavigateAndCommitFromBrowser(web_contents(), url1);
+  NavigationSimulator::CreateRendererInitiated(same_document_url1, main_rfh())
+      ->CommitSameDocument();
+  NavigationSimulator::NavigateAndCommitFromBrowser(web_contents(), url2);
+  NavigationSimulator::CreateRendererInitiated(same_document_url2, main_rfh())
+      ->CommitSameDocument();
+
+  EXPECT_EQ(same_document_url2, web_contents()->GetLastCommittedURL());
+
+  // Serialize each source so we can verify expectations below.
+  ukm::Source full_nav_source1, full_nav_source2, same_doc_source1,
+      same_doc_source2;
+  EXPECT_EQ(4ul, test_ukm_recorder_.GetSources().size());
+  for (auto& kv : test_ukm_recorder_.GetSources()) {
+    if (kv.second->url() == url1) {
+      kv.second->PopulateProto(&full_nav_source1);
+    } else if (kv.second->url() == url2) {
+      kv.second->PopulateProto(&full_nav_source2);
+    } else if (kv.second->url() == same_document_url1) {
+      kv.second->PopulateProto(&same_doc_source1);
+    } else if (kv.second->url() == same_document_url2) {
+      kv.second->PopulateProto(&same_doc_source2);
+    } else {
+      FAIL() << "Encountered unexpected source.";
+    }
+  }
+
+  // The first navigation was a non-same-document navigation to url1. As such,
+  // it shouldn't have any previous source ids.
+  EXPECT_EQ(url1, full_nav_source1.url());
+  EXPECT_TRUE(full_nav_source1.has_id());
+  EXPECT_FALSE(full_nav_source1.is_same_document_navigation());
+  EXPECT_FALSE(full_nav_source1.has_previous_source_id());
+  EXPECT_FALSE(full_nav_source1.has_previous_same_document_source_id());
+
+  // The second navigation was a same-document navigation to
+  // same_document_url1. It should have a previous_source_id that points to
+  // url1's source, but no previous_same_document_source_id.
+  EXPECT_EQ(same_document_url1, same_doc_source1.url());
+  EXPECT_TRUE(same_doc_source1.has_id());
+  EXPECT_TRUE(same_doc_source1.is_same_document_navigation());
+  EXPECT_EQ(full_nav_source1.id(), same_doc_source1.previous_source_id());
+  EXPECT_FALSE(same_doc_source1.has_previous_same_document_source_id());
+
+  // The third navigation was a non-same-document navigation to url2. It should
+  // have a previous_source_id pointing to the source for url1, and a
+  // previous_same_document_source_id pointing to the source for
+  // same_document_url1.
+  EXPECT_EQ(url2, full_nav_source2.url());
+  EXPECT_TRUE(full_nav_source2.has_id());
+  EXPECT_FALSE(full_nav_source2.is_same_document_navigation());
+  EXPECT_EQ(full_nav_source1.id(), full_nav_source2.previous_source_id());
+  EXPECT_EQ(same_doc_source1.id(),
+            full_nav_source2.previous_same_document_source_id());
+
+  // The fourth navigation was a same-document navigation to
+  // same_document_url2. It should have a previous_source_id pointing to the
+  // source for url2, and no previous_same_document_source_id.
+  EXPECT_EQ(same_document_url2, same_doc_source2.url());
+  EXPECT_TRUE(same_doc_source2.has_id());
+  EXPECT_TRUE(same_doc_source2.is_same_document_navigation());
+  EXPECT_EQ(full_nav_source2.id(), same_doc_source2.previous_source_id());
+  EXPECT_FALSE(same_doc_source2.has_previous_same_document_source_id());
+
+  EXPECT_EQ(url2, GetAssociatedURLForWebContentsDocument());
+}
+
+TEST_F(SourceUrlRecorderWebContentsObserverTest,
+       SameDocumentNavigationDisabled) {
+  base::test::ScopedFeatureList scoped_feature_list;
+  scoped_feature_list.InitAndEnableFeatureWithParameters(
+      ukm::kUkmFeature, {{"MaxSameDocumentSourcesPerFullSource", "0"}});
+
   GURL url("https://www.example.com/");
   GURL same_document_url("https://www.example.com/#samedocument");
   NavigationSimulator::NavigateAndCommitFromBrowser(web_contents(), url);
@@ -92,9 +171,10 @@
 
   const auto& sources = test_ukm_recorder_.GetSources();
   EXPECT_EQ(1ul, sources.size());
-  for (const auto& kv : sources) {
+  for (auto& kv : sources) {
     EXPECT_EQ(url, kv.second->url());
     EXPECT_EQ(1u, kv.second->urls().size());
+    EXPECT_FALSE(kv.second->navigation_data().is_same_document_navigation);
   }
 
   EXPECT_EQ(url, GetAssociatedURLForWebContentsDocument());
diff --git a/components/update_client/update_checker.cc b/components/update_client/update_checker.cc
index 5b095cc5..b9d83ba 100644
--- a/components/update_client/update_checker.cc
+++ b/components/update_client/update_checker.cc
@@ -7,6 +7,7 @@
 #include <stddef.h>
 
 #include <algorithm>
+#include <functional>
 #include <memory>
 #include <string>
 #include <utility>
@@ -134,9 +135,8 @@
       base::BindOnce(&UpdateCheckerImpl::ReadUpdaterStateAttributes,
                      base::Unretained(this)),
       base::BindOnce(&UpdateCheckerImpl::CheckForUpdatesHelper,
-                     base::Unretained(this), session_id,
-                     base::ConstRef(components), additional_attributes,
-                     enabled_component_updates));
+                     base::Unretained(this), session_id, std::cref(components),
+                     additional_attributes, enabled_component_updates));
 }
 
 // This function runs on the blocking pool task runner.
diff --git a/components/viz/common/BUILD.gn b/components/viz/common/BUILD.gn
index 65dbf3c..1b3be20 100644
--- a/components/viz/common/BUILD.gn
+++ b/components/viz/common/BUILD.gn
@@ -212,6 +212,7 @@
     "//gpu/command_buffer/client:gles2_interface",
     "//gpu/command_buffer/client:raster",
     "//gpu/command_buffer/client:raster_interface",
+    "//gpu/config",
     "//gpu/vulkan:buildflags",
     "//mojo/public/cpp/system",
     "//third_party/libyuv",
diff --git a/components/viz/common/DEPS b/components/viz/common/DEPS
index d1f52eb..e8cea65c 100644
--- a/components/viz/common/DEPS
+++ b/components/viz/common/DEPS
@@ -35,4 +35,7 @@
     "+gpu/ipc/gl_in_process_context.h",
     "+gpu/ipc/test_gpu_thread_holder.h",
   ],
+  "features.cc" : [
+    "+gpu/config/gpu_finch_features.h",
+  ],
 }
diff --git a/components/viz/common/features.cc b/components/viz/common/features.cc
index b816e39..8e024e5a 100644
--- a/components/viz/common/features.cc
+++ b/components/viz/common/features.cc
@@ -7,6 +7,7 @@
 #include "base/command_line.h"
 #include "build/build_config.h"
 #include "components/viz/common/switches.h"
+#include "gpu/config/gpu_finch_features.h"
 
 namespace features {
 
@@ -58,6 +59,10 @@
 }
 
 bool IsVizDisplayCompositorEnabled() {
+#if defined(OS_ANDROID)
+  if (features::IsAndroidSurfaceControlEnabled())
+    return true;
+#endif
   return base::FeatureList::IsEnabled(kVizDisplayCompositor);
 }
 
diff --git a/components/viz/common/quads/draw_quad_unittest.cc b/components/viz/common/quads/draw_quad_unittest.cc
index 7428364c..9992732 100644
--- a/components/viz/common/quads/draw_quad_unittest.cc
+++ b/components/viz/common/quads/draw_quad_unittest.cc
@@ -400,17 +400,17 @@
 
 TEST(DrawQuadTest, CopyVideoHoleDrawQuad) {
   gfx::Rect visible_rect(40, 50, 30, 20);
-  base::UnguessableToken overlay_id = base::UnguessableToken::Create();
+  base::UnguessableToken overlay_plane_id = base::UnguessableToken::Create();
   CREATE_SHARED_STATE();
 
-  CREATE_QUAD_NEW(VideoHoleDrawQuad, visible_rect, overlay_id);
+  CREATE_QUAD_NEW(VideoHoleDrawQuad, visible_rect, overlay_plane_id);
   EXPECT_EQ(DrawQuad::VIDEO_HOLE, copy_quad->material);
   EXPECT_EQ(visible_rect, copy_quad->visible_rect);
-  EXPECT_EQ(overlay_id, copy_quad->overlay_id);
+  EXPECT_EQ(overlay_plane_id, copy_quad->overlay_plane_id);
 
-  CREATE_QUAD_ALL(VideoHoleDrawQuad, overlay_id);
+  CREATE_QUAD_ALL(VideoHoleDrawQuad, overlay_plane_id);
   EXPECT_EQ(DrawQuad::VIDEO_HOLE, copy_quad->material);
-  EXPECT_EQ(overlay_id, copy_quad->overlay_id);
+  EXPECT_EQ(overlay_plane_id, copy_quad->overlay_plane_id);
 }
 
 TEST(DrawQuadTest, CopyYUVVideoDrawQuad) {
@@ -658,10 +658,10 @@
 
 TEST_F(DrawQuadIteratorTest, VideoHoleDrawQuad) {
   gfx::Rect visible_rect(40, 50, 30, 20);
-  base::UnguessableToken overlay_id = base::UnguessableToken::Create();
+  base::UnguessableToken overlay_plane_id = base::UnguessableToken::Create();
 
   CREATE_SHARED_STATE();
-  CREATE_QUAD_NEW(VideoHoleDrawQuad, visible_rect, overlay_id);
+  CREATE_QUAD_NEW(VideoHoleDrawQuad, visible_rect, overlay_plane_id);
   EXPECT_EQ(0, IterateAndCount(quad_new));
 }
 
diff --git a/components/viz/common/quads/video_hole_draw_quad.cc b/components/viz/common/quads/video_hole_draw_quad.cc
index 2dcd5f7..a3c8425 100644
--- a/components/viz/common/quads/video_hole_draw_quad.cc
+++ b/components/viz/common/quads/video_hole_draw_quad.cc
@@ -19,20 +19,20 @@
 void VideoHoleDrawQuad::SetNew(const SharedQuadState* shared_quad_state,
                                const gfx::Rect& rect,
                                const gfx::Rect& visible_rect,
-                               const base::UnguessableToken& overlay_id) {
+                               const base::UnguessableToken& overlay_plane_id) {
   DrawQuad::SetAll(shared_quad_state, DrawQuad::VIDEO_HOLE, rect, visible_rect,
                    /*needs_blending=*/false);
-  this->overlay_id = overlay_id;
+  this->overlay_plane_id = overlay_plane_id;
 }
 
 void VideoHoleDrawQuad::SetAll(const SharedQuadState* shared_quad_state,
                                const gfx::Rect& rect,
                                const gfx::Rect& visible_rect,
                                bool needs_blending,
-                               const base::UnguessableToken& overlay_id) {
+                               const base::UnguessableToken& overlay_plane_id) {
   DrawQuad::SetAll(shared_quad_state, DrawQuad::VIDEO_HOLE, rect, visible_rect,
                    needs_blending);
-  this->overlay_id = overlay_id;
+  this->overlay_plane_id = overlay_plane_id;
 }
 
 const VideoHoleDrawQuad* VideoHoleDrawQuad::MaterialCast(const DrawQuad* quad) {
@@ -42,7 +42,7 @@
 
 void VideoHoleDrawQuad::ExtendValue(
     base::trace_event::TracedValue* value) const {
-  value->SetString("overlay_id", overlay_id.ToString());
+  value->SetString("overlay_plane_id", overlay_plane_id.ToString());
 }
 
 }  // namespace viz
diff --git a/components/viz/common/quads/video_hole_draw_quad.h b/components/viz/common/quads/video_hole_draw_quad.h
index b53e4224..e4cbf07 100644
--- a/components/viz/common/quads/video_hole_draw_quad.h
+++ b/components/viz/common/quads/video_hole_draw_quad.h
@@ -19,8 +19,8 @@
 namespace viz {
 
 // A VideoHoleDrawQuad is used by Chromecast to instruct that a video
-// overlay is to be activated. It carries |overlay_id| which identifies
-// the origin of the video overlay frame. |overlay_id| will be used
+// overlay is to be activated. It carries |overlay_plane_id| which identifies
+// the origin of the video overlay frame. |overlay_plane_id| will be used
 // to find the right VideoDecoder to apply SetGeometry() on.
 class VIZ_COMMON_EXPORT VideoHoleDrawQuad : public DrawQuad {
  public:
@@ -31,16 +31,16 @@
   void SetNew(const SharedQuadState* shared_quad_state,
               const gfx::Rect& rect,
               const gfx::Rect& visible_rect,
-              const base::UnguessableToken& overlay_id);
+              const base::UnguessableToken& overlay_plane_id);
 
   void SetAll(const SharedQuadState* shared_quad_state,
               const gfx::Rect& rect,
               const gfx::Rect& visible_rect,
               bool needs_blending,
-              const base::UnguessableToken& overlay_id);
+              const base::UnguessableToken& overlay_plane_id);
 
   static const VideoHoleDrawQuad* MaterialCast(const DrawQuad*);
-  base::UnguessableToken overlay_id;
+  base::UnguessableToken overlay_plane_id;
 
  private:
   void ExtendValue(base::trace_event::TracedValue* value) const override;
diff --git a/components/viz/host/gpu_host_impl.cc b/components/viz/host/gpu_host_impl.cc
index 3b243d5..4fd50ad9 100644
--- a/components/viz/host/gpu_host_impl.cc
+++ b/components/viz/host/gpu_host_impl.cc
@@ -509,8 +509,7 @@
   client_id_to_shader_cache_.clear();
 
   if (!params_.disable_gpu_shader_disk_cache) {
-    bool oopd_enabled =
-        base::FeatureList::IsEnabled(features::kVizDisplayCompositor);
+    bool oopd_enabled = features::IsVizDisplayCompositorEnabled();
     if (oopd_enabled)
       CreateChannelCache(gpu::kInProcessCommandBufferClientId);
 
diff --git a/components/viz/service/display/direct_renderer.cc b/components/viz/service/display/direct_renderer.cc
index d32567b..53ae85d 100644
--- a/components/viz/service/display/direct_renderer.cc
+++ b/components/viz/service/display/direct_renderer.cc
@@ -345,6 +345,7 @@
         gfx::RectF(device_viewport_size.width(), device_viewport_size.height());
     output_surface_plane.resource_size_in_pixels = device_viewport_size;
     output_surface_plane.format = output_surface_->GetOverlayBufferFormat();
+    output_surface_plane.color_space = reshape_device_color_space_;
     output_surface_plane.use_output_surface_for_resource = true;
     output_surface_plane.overlay_handled = true;
     output_surface_plane.is_opaque = true;
diff --git a/components/viz/service/display/display_resource_provider.cc b/components/viz/service/display/display_resource_provider.cc
index 9875e5d..9d45b46 100644
--- a/components/viz/service/display/display_resource_provider.cc
+++ b/components/viz/service/display/display_resource_provider.cc
@@ -257,6 +257,11 @@
   return BufferFormat(resource->transferable.format);
 }
 
+const gfx::ColorSpace& DisplayResourceProvider::GetColorSpace(ResourceId id) {
+  ChildResource* resource = GetResource(id);
+  return resource->transferable.color_space;
+}
+
 void DisplayResourceProvider::WaitSyncToken(ResourceId id) {
   ChildResource* resource = TryGetResource(id);
   // TODO(ericrk): We should never fail TryGetResource, but we appear to
diff --git a/components/viz/service/display/display_resource_provider.h b/components/viz/service/display/display_resource_provider.h
index 946a204..20a4f37 100644
--- a/components/viz/service/display/display_resource_provider.h
+++ b/components/viz/service/display/display_resource_provider.h
@@ -112,6 +112,7 @@
   GLenum GetResourceTextureTarget(ResourceId id);
   // Return the format of the underlying buffer that can be used for scanout.
   gfx::BufferFormat GetBufferFormat(ResourceId id);
+  const gfx::ColorSpace& GetColorSpace(ResourceId id);
   // Indicates if this resource may be used for a hardware overlay plane.
   bool IsOverlayCandidate(ResourceId id);
 
diff --git a/components/viz/service/display/overlay_candidate.cc b/components/viz/service/display/overlay_candidate.cc
index f1a25012..6e94a82d 100644
--- a/components/viz/service/display/overlay_candidate.cc
+++ b/components/viz/service/display/overlay_candidate.cc
@@ -227,6 +227,7 @@
     return false;
 
   candidate->format = resource_provider->GetBufferFormat(resource_id);
+  candidate->color_space = resource_provider->GetColorSpace(resource_id);
   if (!base::ContainsValue(kOverlayFormats, candidate->format))
     return false;
 
diff --git a/components/viz/service/display/overlay_candidate.h b/components/viz/service/display/overlay_candidate.h
index d54f598..3c52114c 100644
--- a/components/viz/service/display/overlay_candidate.h
+++ b/components/viz/service/display/overlay_candidate.h
@@ -71,6 +71,8 @@
   gfx::OverlayTransform transform;
   // Format of the buffer to scanout.
   gfx::BufferFormat format;
+  // ColorSpace of the buffer for scanout.
+  gfx::ColorSpace color_space;
   // Size of the resource, in pixels.
   gfx::Size resource_size_in_pixels;
   // Rect on the display to position the overlay to. Implementer must convert
diff --git a/components/viz/service/display/overlay_strategy_underlay_cast.cc b/components/viz/service/display/overlay_strategy_underlay_cast.cc
index e72b280..3b87d13 100644
--- a/components/viz/service/display/overlay_strategy_underlay_cast.cc
+++ b/components/viz/service/display/overlay_strategy_underlay_cast.cc
@@ -101,7 +101,7 @@
       // TODO(guohuideng): activate overlay through MediaServe when it's
       // ready, using |overlay_plane_id|. see b/79266094.
       base::UnguessableToken overlay_plane_id =
-          VideoHoleDrawQuad::MaterialCast(*it)->overlay_id;
+          VideoHoleDrawQuad::MaterialCast(*it)->overlay_plane_id;
       ANALYZER_ALLOW_UNUSED(overlay_plane_id);
 
       render_pass->quad_list.ReplaceExistingQuadWithOpaqueTransparentSolidColor(
diff --git a/components/viz/service/display/skia_renderer.cc b/components/viz/service/display/skia_renderer.cc
index 4882351..aa0f9661 100644
--- a/components/viz/service/display/skia_renderer.cc
+++ b/components/viz/service/display/skia_renderer.cc
@@ -894,7 +894,7 @@
     if (dst_rect.IsEmpty())
       return false;
 
-    params->image_filter = filter;
+    params->image_filter = filter->makeWithLocalMatrix(local_matrix);
   }
   return true;
 }
diff --git a/components/viz/service/display_embedder/buffer_queue.cc b/components/viz/service/display_embedder/buffer_queue.cc
index 19744b5..9fb0ab8b 100644
--- a/components/viz/service/display_embedder/buffer_queue.cc
+++ b/components/viz/service/display_embedder/buffer_queue.cc
@@ -306,6 +306,11 @@
   allocated_count_++;
   gl_->BindTexture(texture_target_, texture);
   gl_->BindTexImage2DCHROMIUM(texture_target_, id);
+
+  // The texture must be bound to the image before setting the color space.
+  gl_->SetColorSpaceMetadataCHROMIUM(
+      texture, reinterpret_cast<GLColorSpace>(&color_space_));
+
   return std::make_unique<AllocatedSurface>(this, std::move(buffer), texture,
                                             id, stencil, gfx::Rect(size_));
 }
diff --git a/components/viz/service/display_embedder/gl_output_surface_buffer_queue_android.cc b/components/viz/service/display_embedder/gl_output_surface_buffer_queue_android.cc
index dbb26271..15b40f4 100644
--- a/components/viz/service/display_embedder/gl_output_surface_buffer_queue_android.cc
+++ b/components/viz/service/display_embedder/gl_output_surface_buffer_queue_android.cc
@@ -9,6 +9,7 @@
 #include "components/viz/service/display_embedder/compositor_overlay_candidate_validator_android.h"
 #include "third_party/khronos/GLES2/gl2.h"
 #include "ui/gfx/geometry/rect_conversions.h"
+#include "ui/gl/android/android_surface_control_compat.h"
 
 namespace viz {
 namespace {
@@ -29,6 +30,14 @@
     // Only update the last candidate that was added to the list. All previous
     // overlays should have already been handled.
     auto& candidate = surfaces->back();
+    if (!gl::SurfaceControl::SupportsColorSpace(candidate.color_space)) {
+      DCHECK(!candidate.use_output_surface_for_resource)
+          << "The main overlay must only use color space supported by the "
+             "device";
+      candidate.overlay_handled = false;
+      return;
+    }
+
     candidate.display_rect =
         gfx::RectF(gfx::ToEnclosingRect(candidate.display_rect));
     candidate.overlay_handled = true;
diff --git a/components/viz/service/display_embedder/gpu_display_provider.cc b/components/viz/service/display_embedder/gpu_display_provider.cc
index 72e6374..e83a880 100644
--- a/components/viz/service/display_embedder/gpu_display_provider.cc
+++ b/components/viz/service/display_embedder/gpu_display_provider.cc
@@ -158,6 +158,7 @@
       }
       output_surface = std::make_unique<SkiaOutputSurfaceImplNonDDL>(
           std::move(gl_surface), shared_context_state_, mailbox_manager_.get(),
+          gpu_service_impl_->shared_image_manager(),
           gpu_service_impl_->sync_point_manager(),
           true /* need_swapbuffers_ack */);
 
diff --git a/components/viz/service/display_embedder/skia_output_surface_impl_non_ddl.cc b/components/viz/service/display_embedder/skia_output_surface_impl_non_ddl.cc
index ea8deb7..cdcc403 100644
--- a/components/viz/service/display_embedder/skia_output_surface_impl_non_ddl.cc
+++ b/components/viz/service/display_embedder/skia_output_surface_impl_non_ddl.cc
@@ -18,11 +18,15 @@
 #include "components/viz/service/display/output_surface_client.h"
 #include "components/viz/service/display/output_surface_frame.h"
 #include "components/viz/service/display/resource_metadata.h"
+#include "gpu/command_buffer/common/shared_image_usage.h"
 #include "gpu/command_buffer/common/swap_buffers_complete_params.h"
 #include "gpu/command_buffer/service/mailbox_manager.h"
+#include "gpu/command_buffer/service/shared_image_factory.h"
+#include "gpu/command_buffer/service/shared_image_representation.h"
 #include "gpu/command_buffer/service/skia_utils.h"
 #include "gpu/command_buffer/service/sync_point_manager.h"
 #include "gpu/command_buffer/service/texture_base.h"
+#include "third_party/skia/include/core/SkPromiseImageTexture.h"
 #include "third_party/skia/include/core/SkYUVAIndex.h"
 #include "ui/gfx/skia_util.h"
 #include "ui/gl/gl_bindings.h"
@@ -46,17 +50,35 @@
       command_buffer_id, sequence_id);
 }
 
+std::unique_ptr<gpu::SharedImageRepresentationFactory>
+CreateSharedImageRepresentationFactory(gpu::SharedImageManager* manager) {
+  if (!manager)
+    return nullptr;
+  // TODO(https://crbug.com/899905): Use a real MemoryTracker, not nullptr.
+  return std::make_unique<gpu::SharedImageRepresentationFactory>(
+      manager, nullptr /* tracker */);
+}
+
+void ReleaseSharedImagePresentation(void* context) {
+  std::unique_ptr<gpu::SharedImageRepresentationSkia> representation(
+      static_cast<gpu::SharedImageRepresentationSkia*>(context));
+  representation->EndReadAccess();
+}
+
 }  // namespace
 
 SkiaOutputSurfaceImplNonDDL::SkiaOutputSurfaceImplNonDDL(
     scoped_refptr<gl::GLSurface> gl_surface,
     scoped_refptr<gpu::SharedContextState> shared_context_state,
     gpu::MailboxManager* mailbox_manager,
+    gpu::SharedImageManager* shared_image_manager,
     gpu::SyncPointManager* sync_point_manager,
     bool need_swapbuffers_ack)
     : gl_surface_(std::move(gl_surface)),
       shared_context_state_(std::move(shared_context_state)),
       mailbox_manager_(mailbox_manager),
+      sir_factory_(
+          CreateSharedImageRepresentationFactory(shared_image_manager)),
       sync_point_order_data_(sync_point_manager->CreateSyncPointOrderData()),
       sync_point_client_state_(
           CreateSyncPointClientState(sync_point_manager,
@@ -201,6 +223,11 @@
     ResourceMetadata metadata) {
   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
 
+  if (metadata.mailbox_holder.mailbox.IsSharedImage() && sir_factory_) {
+    WaitSyncToken(metadata.mailbox_holder.sync_token);
+    return MakeSkImageFromSharedImage(metadata);
+  }
+
   GrBackendTexture backend_texture;
   if (!GetGrBackendTexture(metadata, &backend_texture)) {
     DLOG(ERROR) << "Failed to GetGrBackendTexture from mailbox.";
@@ -401,25 +428,68 @@
   observers_.RemoveObserver(observer);
 }
 
+bool SkiaOutputSurfaceImplNonDDL::WaitSyncToken(
+    const gpu::SyncToken& sync_token) {
+  base::WaitableEvent event;
+  if (!sync_point_client_state_->Wait(
+          sync_token, base::BindOnce(&base::WaitableEvent::Signal,
+                                     base::Unretained(&event)))) {
+    return false;
+  }
+  event.Wait();
+  return true;
+}
+
+sk_sp<SkImage> SkiaOutputSurfaceImplNonDDL::MakeSkImageFromSharedImage(
+    const ResourceMetadata& metadata) {
+  auto representation =
+      sir_factory_->ProduceSkia(metadata.mailbox_holder.mailbox);
+  if (!representation) {
+    DLOG(ERROR) << "Failed to make the SkImage - SharedImage mailbox not "
+                   "found in SharedImageManager.";
+    return nullptr;
+  }
+
+  if (!(representation->usage() & gpu::SHARED_IMAGE_USAGE_DISPLAY)) {
+    DLOG(ERROR) << "Failed to make the SkImage - SharedImage was not created "
+                   "with display usage.";
+    return nullptr;
+  }
+  // TODO(penghuang): make SharedImageBacking be aware the target context.
+  auto promise_texture =
+      representation->BeginReadAccess(nullptr /* read_surface */);
+  if (!promise_texture) {
+    DLOG(ERROR)
+        << "Failed to begin read access for SharedImageRepresentationSkia";
+    return nullptr;
+  }
+
+  SkColorType color_type = ResourceFormatToClosestSkColorType(
+      true /* gpu_compositing */, metadata.resource_format);
+
+  auto sk_image = SkImage::MakeFromTexture(
+      gr_context(), promise_texture->backendTexture(), kTopLeft_GrSurfaceOrigin,
+      color_type, metadata.alpha_type, metadata.color_space.ToSkColorSpace(),
+      ReleaseSharedImagePresentation, representation.get());
+
+  if (!sk_image) {
+    DLOG(ERROR) << "Failed to create the SkImage";
+    return nullptr;
+  }
+
+  representation.release();
+  return sk_image;
+}
+
 bool SkiaOutputSurfaceImplNonDDL::GetGrBackendTexture(
     const ResourceMetadata& metadata,
     GrBackendTexture* backend_texture) {
   DCHECK(!metadata.mailbox_holder.mailbox.IsZero());
-
-  base::WaitableEvent event;
-  if (sync_point_client_state_->Wait(
-          metadata.mailbox_holder.sync_token,
-          base::BindOnce(&base::WaitableEvent::Signal,
-                         base::Unretained(&event)))) {
-    event.Wait();
+  if (WaitSyncToken(metadata.mailbox_holder.sync_token)) {
     DCHECK(mailbox_manager_->UsesSync());
     mailbox_manager_->PullTextureUpdates(metadata.mailbox_holder.sync_token);
   }
 
-  if (metadata.mailbox_holder.mailbox.IsSharedImage()) {
-    // TODO(https://crbug.com/900973): support shared image.
-  }
-
   auto* texture_base =
       mailbox_manager_->ConsumeTexture(metadata.mailbox_holder.mailbox);
   if (!texture_base) {
diff --git a/components/viz/service/display_embedder/skia_output_surface_impl_non_ddl.h b/components/viz/service/display_embedder/skia_output_surface_impl_non_ddl.h
index ca43df60..3bb3e957 100644
--- a/components/viz/service/display_embedder/skia_output_surface_impl_non_ddl.h
+++ b/components/viz/service/display_embedder/skia_output_surface_impl_non_ddl.h
@@ -28,6 +28,8 @@
 
 namespace gpu {
 class MailboxManager;
+class SharedImageManager;
+class SharedImageRepresentationFactory;
 class SyncPointClientState;
 class SyncPointManager;
 class SyncPointOrderData;
@@ -46,6 +48,7 @@
       scoped_refptr<gl::GLSurface> gl_surface,
       scoped_refptr<gpu::SharedContextState> shared_context_state,
       gpu::MailboxManager* mailbox_manager,
+      gpu::SharedImageManager* shared_image_manager,
       gpu::SyncPointManager* sync_point_manager,
       bool need_swapbuffers_ack);
   ~SkiaOutputSurfaceImplNonDDL() override;
@@ -107,6 +110,8 @@
  private:
   GrContext* gr_context() { return shared_context_state_->gr_context(); }
 
+  bool WaitSyncToken(const gpu::SyncToken& sync_token);
+  sk_sp<SkImage> MakeSkImageFromSharedImage(const ResourceMetadata& metadata);
   bool GetGrBackendTexture(const ResourceMetadata& metadata,
                            GrBackendTexture* backend_texture);
 
@@ -118,7 +123,8 @@
   // Stuffs for running with |task_executor_| instead of |gpu_service_|.
   scoped_refptr<gl::GLSurface> gl_surface_;
   scoped_refptr<gpu::SharedContextState> shared_context_state_;
-  gpu::MailboxManager* mailbox_manager_;
+  gpu::MailboxManager* const mailbox_manager_;
+  std::unique_ptr<gpu::SharedImageRepresentationFactory> sir_factory_;
   scoped_refptr<gpu::SyncPointOrderData> sync_point_order_data_;
   scoped_refptr<gpu::SyncPointClientState> sync_point_client_state_;
   const bool need_swapbuffers_ack_;
diff --git a/components/viz/service/display_embedder/skia_output_surface_impl_on_gpu.cc b/components/viz/service/display_embedder/skia_output_surface_impl_on_gpu.cc
index cfd219e1..5e1af789 100644
--- a/components/viz/service/display_embedder/skia_output_surface_impl_on_gpu.cc
+++ b/components/viz/service/display_embedder/skia_output_surface_impl_on_gpu.cc
@@ -18,6 +18,7 @@
 #include "components/viz/service/display/texture_deleter.h"
 #include "components/viz/service/display_embedder/direct_context_provider.h"
 #include "components/viz/service/gl/gpu_service_impl.h"
+#include "gpu/command_buffer/common/shared_image_usage.h"
 #include "gpu/command_buffer/common/swap_buffers_complete_params.h"
 #include "gpu/command_buffer/service/context_state.h"
 #include "gpu/command_buffer/service/gr_shader_cache.h"
@@ -183,7 +184,9 @@
 std::unique_ptr<gpu::SharedImageRepresentationFactory>
 CreateSharedImageRepresentationFactory(
     gpu::CommandBufferTaskExecutor* task_executor) {
-  return nullptr;
+  // TODO(https://crbug.com/899905): Use a real MemoryTracker, not nullptr.
+  return std::make_unique<gpu::SharedImageRepresentationFactory>(
+      task_executor->shared_image_manager(), nullptr);
 }
 
 class ScopedSurfaceToTexture {
@@ -931,10 +934,7 @@
     std::unique_ptr<gpu::SharedImageRepresentationSkia>* shared_image_out) {
   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
 
-  if (!shared_image_representation_factory_) {
-    // TODO(https://crbug.com/900973): support shared image for Android
-    // WebView.
-  } else if (!*shared_image_out && mailbox_holder.mailbox.IsSharedImage()) {
+  if (!*shared_image_out && mailbox_holder.mailbox.IsSharedImage()) {
     std::unique_ptr<gpu::SharedImageRepresentationSkia> shared_image =
         shared_image_representation_factory_->ProduceSkia(
             mailbox_holder.mailbox);
@@ -943,6 +943,11 @@
                      "mailbox not found in SharedImageManager.";
       return nullptr;
     }
+    if (!(shared_image->usage() & gpu::SHARED_IMAGE_USAGE_DISPLAY)) {
+      DLOG(ERROR) << "Failed to fulfill the promise texture - SharedImage "
+                     "was not created with display usage.";
+      return nullptr;
+    }
     *shared_image_out = std::move(shared_image);
   }
   if (*shared_image_out) {
@@ -1190,6 +1195,8 @@
 
 void SkiaOutputSurfaceImplOnGpu::PullTextureUpdates(
     std::vector<gpu::SyncToken> sync_tokens) {
+  // TODO(https://crbug.com/900973): Remove it when MailboxManager is replaced
+  // with SharedImage API.
   if (mailbox_manager_->UsesSync()) {
     for (auto& sync_token : sync_tokens)
       mailbox_manager_->PullTextureUpdates(sync_token);
@@ -1198,6 +1205,8 @@
 
 void SkiaOutputSurfaceImplOnGpu::ReleaseFenceSyncAndPushTextureUpdates(
     uint64_t sync_fence_release) {
+  // TODO(https://crbug.com/900973): Remove it when MailboxManager is replaced
+  // with SharedImage API.
   if (mailbox_manager_->UsesSync()) {
     // If MailboxManagerSync is used, we are sharing textures between threads.
     // In this case, sync point can only guarantee GL commands are issued in
diff --git a/components/viz/service/display_embedder/skia_output_surface_impl_unittest.cc b/components/viz/service/display_embedder/skia_output_surface_impl_unittest.cc
index b35ca51..a85a1175 100644
--- a/components/viz/service/display_embedder/skia_output_surface_impl_unittest.cc
+++ b/components/viz/service/display_embedder/skia_output_surface_impl_unittest.cc
@@ -134,7 +134,8 @@
   gpu_service_->InitializeWithHost(
       std::move(gpu_host_proxy), gpu::GpuProcessActivityFlags(),
       gl::init::CreateOffscreenGLSurface(gfx::Size()),
-      nullptr /* sync_point_manager */, nullptr /* shutdown_event */);
+      nullptr /* sync_point_manager */, nullptr /* shared_image_manager */,
+      nullptr /* shutdown_event */);
   task_executor_ = base::MakeRefCounted<gpu::GpuInProcessThreadService>(
       gpu_thread_->task_runner(), gpu_service_->scheduler(),
       gpu_service_->sync_point_manager(), gpu_service_->mailbox_manager(),
diff --git a/components/viz/service/gl/gpu_service_impl.cc b/components/viz/service/gl/gpu_service_impl.cc
index c4d8779..2c29bf9 100644
--- a/components/viz/service/gl/gpu_service_impl.cc
+++ b/components/viz/service/gl/gpu_service_impl.cc
@@ -190,6 +190,7 @@
   // Scheduler must be destroyed before sync point manager is destroyed.
   scheduler_.reset();
   owned_sync_point_manager_.reset();
+  owned_shared_image_manager_.reset();
 
   // Signal this event before destroying the child process. That way all
   // background threads can cleanup. For example, in the renderer the
@@ -223,6 +224,7 @@
     gpu::GpuProcessActivityFlags activity_flags,
     scoped_refptr<gl::GLSurface> default_offscreen_surface,
     gpu::SyncPointManager* sync_point_manager,
+    gpu::SharedImageManager* shared_image_manager,
     base::WaitableEvent* shutdown_event) {
   DCHECK(main_runner_->BelongsToCurrentThread());
   gpu_host->DidInitialize(gpu_info_, gpu_feature_info_,
@@ -239,10 +241,17 @@
     logging::SetLogMessageHandler(GpuLogMessageHandler);
   }
 
-  sync_point_manager_ = sync_point_manager;
-  if (!sync_point_manager_) {
+  if (!sync_point_manager) {
     owned_sync_point_manager_ = std::make_unique<gpu::SyncPointManager>();
-    sync_point_manager_ = owned_sync_point_manager_.get();
+    sync_point_manager = owned_sync_point_manager_.get();
+  }
+
+  if (!shared_image_manager) {
+    // The shared image will be only used on GPU main thread, so it doesn't need
+    // to be thread safe.
+    owned_shared_image_manager_ =
+        std::make_unique<gpu::SharedImageManager>(false /* thread_safe */);
+    shared_image_manager = owned_shared_image_manager_.get();
   }
 
   shutdown_event_ = shutdown_event;
@@ -254,7 +263,7 @@
   }
 
   scheduler_ =
-      std::make_unique<gpu::Scheduler>(main_runner_, sync_point_manager_);
+      std::make_unique<gpu::Scheduler>(main_runner_, sync_point_manager);
 
   skia_output_surface_sequence_id_ =
       scheduler_->CreateSequence(gpu::SchedulingPriority::kHigh);
@@ -264,9 +273,9 @@
   // initialization has succeeded.
   gpu_channel_manager_ = std::make_unique<gpu::GpuChannelManager>(
       gpu_preferences_, this, watchdog_thread_.get(), main_runner_, io_runner_,
-      scheduler_.get(), sync_point_manager_, gpu_memory_buffer_factory_.get(),
-      gpu_feature_info_, std::move(activity_flags),
-      std::move(default_offscreen_surface),
+      scheduler_.get(), sync_point_manager, shared_image_manager,
+      gpu_memory_buffer_factory_.get(), gpu_feature_info_,
+      std::move(activity_flags), std::move(default_offscreen_surface),
       nullptr /* image_decode_accelerator_worker */, vulkan_context_provider());
 
   media_gpu_channel_manager_.reset(
diff --git a/components/viz/service/gl/gpu_service_impl.h b/components/viz/service/gl/gpu_service_impl.h
index aa3dfcf5..fdee651 100644
--- a/components/viz/service/gl/gpu_service_impl.h
+++ b/components/viz/service/gl/gpu_service_impl.h
@@ -44,6 +44,7 @@
 class GpuWatchdogThread;
 class Scheduler;
 class SyncPointManager;
+class SharedImageManager;
 class VulkanImplementation;
 }  // namespace gpu
 
@@ -85,6 +86,7 @@
       gpu::GpuProcessActivityFlags activity_flags,
       scoped_refptr<gl::GLSurface> default_offscreen_surface,
       gpu::SyncPointManager* sync_point_manager = nullptr,
+      gpu::SharedImageManager* shared_image_manager = nullptr,
       base::WaitableEvent* shutdown_event = nullptr);
   void Bind(mojom::GpuServiceRequest request);
 
@@ -189,7 +191,9 @@
     return gpu_channel_manager_->gr_shader_cache();
   }
 
-  gpu::SyncPointManager* sync_point_manager() { return sync_point_manager_; }
+  gpu::SyncPointManager* sync_point_manager() {
+    return gpu_channel_manager_->sync_point_manager();
+  }
   gpu::Scheduler* scheduler() { return scheduler_.get(); }
 
   gpu::GpuWatchdogThread* watchdog_thread() { return watchdog_thread_.get(); }
@@ -292,10 +296,11 @@
   std::unique_ptr<gpu::GpuChannelManager> gpu_channel_manager_;
   std::unique_ptr<media::MediaGpuChannelManager> media_gpu_channel_manager_;
 
-  // On some platforms (e.g. android webview), the SyncPointManager comes from
-  // external sources.
+  // On some platforms (e.g. android webview), the SyncPointManager and
+  // SharedImageManager comes from external sources.
   std::unique_ptr<gpu::SyncPointManager> owned_sync_point_manager_;
-  gpu::SyncPointManager* sync_point_manager_ = nullptr;
+
+  std::unique_ptr<gpu::SharedImageManager> owned_shared_image_manager_;
 
   std::unique_ptr<gpu::Scheduler> scheduler_;
 
diff --git a/components/viz/service/main/viz_main_impl.cc b/components/viz/service/main/viz_main_impl.cc
index f652a94..2b1af6b 100644
--- a/components/viz/service/main/viz_main_impl.cc
+++ b/components/viz/service/main/viz_main_impl.cc
@@ -197,7 +197,8 @@
       std::move(gpu_host),
       gpu::GpuProcessActivityFlags(std::move(activity_flags)),
       gpu_init_->TakeDefaultOffscreenSurface(),
-      dependencies_.sync_point_manager, dependencies_.shutdown_event);
+      dependencies_.sync_point_manager, dependencies_.shared_image_manager,
+      dependencies_.shutdown_event);
 
   if (!pending_frame_sink_manager_params_.is_null()) {
     CreateFrameSinkManagerInternal(
diff --git a/components/viz/service/main/viz_main_impl.h b/components/viz/service/main/viz_main_impl.h
index f8d3c8a..5c7dbd94 100644
--- a/components/viz/service/main/viz_main_impl.h
+++ b/components/viz/service/main/viz_main_impl.h
@@ -28,6 +28,7 @@
 namespace gpu {
 class GpuInit;
 class SyncPointManager;
+class SharedImageManager;
 }  // namespace gpu
 
 namespace service_manager {
@@ -77,6 +78,7 @@
 
     bool create_display_compositor = false;
     gpu::SyncPointManager* sync_point_manager = nullptr;
+    gpu::SharedImageManager* shared_image_manager = nullptr;
     base::WaitableEvent* shutdown_event = nullptr;
     scoped_refptr<base::SingleThreadTaskRunner> io_thread_task_runner;
     service_manager::Connector* connector = nullptr;
diff --git a/content/browser/BUILD.gn b/content/browser/BUILD.gn
index e607da8..dd069199 100644
--- a/content/browser/BUILD.gn
+++ b/content/browser/BUILD.gn
@@ -1492,8 +1492,8 @@
     "renderer_host/media/old_render_frame_audio_output_stream_factory.h",
     "renderer_host/media/peer_connection_tracker_host.cc",
     "renderer_host/media/peer_connection_tracker_host.h",
-    "renderer_host/media/ref_counted_video_capture_factory.cc",
-    "renderer_host/media/ref_counted_video_capture_factory.h",
+    "renderer_host/media/ref_counted_video_source_provider.cc",
+    "renderer_host/media/ref_counted_video_source_provider.h",
     "renderer_host/media/render_frame_audio_input_stream_factory.cc",
     "renderer_host/media/render_frame_audio_input_stream_factory.h",
     "renderer_host/media/render_frame_audio_output_stream_factory.cc",
@@ -2466,9 +2466,9 @@
 
   if (is_chromeos) {
     deps += [
-      "//chromeos:chromeos_constants",
       "//chromeos/assistant:buildflags",
       "//chromeos/audio",
+      "//chromeos/constants",
       "//chromeos/dbus",
       "//chromeos/dbus:power_manager_proto",
       "//chromeos/network",
diff --git a/content/browser/accessibility/accessibility_auralinux_browsertest.cc b/content/browser/accessibility/accessibility_auralinux_browsertest.cc
index 0bd7053..2e9faef 100644
--- a/content/browser/accessibility/accessibility_auralinux_browsertest.cc
+++ b/content/browser/accessibility/accessibility_auralinux_browsertest.cc
@@ -2,6 +2,12 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+// Chromium cannot upgrade to ATK 2.12 API as it still needs to run
+// valid builds for Ubuntu Trusty.
+// TODO(accessibility): Remove this when Chromium drops support for ATK
+// older than 2.12.
+#define ATK_DISABLE_DEPRECATION_WARNINGS
+
 #include <atk/atk.h>
 
 #include "base/macros.h"
@@ -14,13 +20,30 @@
 #include "content/shell/browser/shell.h"
 #include "content/test/accessibility_browser_test_utils.h"
 #include "content/test/content_browser_test_utils_internal.h"
+#include "net/base/escape.h"
+#include "ui/accessibility/platform/ax_platform_node_auralinux.h"
 
 namespace content {
 
+constexpr char kInputContents[] =
+    "Moz/5.0 (ST 6.x; WWW33) "
+    "WebKit  \"KHTML, like\".";
+constexpr char kTextareaContents[] =
+    "Moz/5.0 (ST 6.x; WWW33)\n"
+    "WebKit \n\"KHTML, like\".";
+constexpr int kContentsLength =
+    static_cast<int>((sizeof(kInputContents) - 1) / sizeof(char));
+
 class AccessibilityAuraLinuxBrowserTest : public ContentBrowserTest {
  public:
-  AccessibilityAuraLinuxBrowserTest();
-  ~AccessibilityAuraLinuxBrowserTest() override;
+  AccessibilityAuraLinuxBrowserTest() = default;
+  ~AccessibilityAuraLinuxBrowserTest() override = default;
+
+ protected:
+  AtkObject* GetRendererAccessible() {
+    content::WebContents* web_contents = shell()->web_contents();
+    return web_contents->GetRenderWidgetHostView()->GetNativeViewAccessible();
+  }
 
   static bool HasObjectWithAtkRoleFrameInAncestry(AtkObject* object) {
     while (object) {
@@ -31,11 +54,151 @@
     return false;
   }
 
+  void LoadInitialAccessibilityTreeFromHtml(const std::string& html);
+  static void CheckTextAtOffset(AtkText* text_object,
+                                int offset,
+                                AtkTextBoundary boundary_type,
+                                int expected_start_offset,
+                                int expected_end_offset,
+                                const char* expected_text);
+
+  void ExecuteScript(const std::string& script);
+  AtkText* SetUpInputField();
+  AtkText* SetUpTextareaField();
+  AtkText* SetUpSampleParagraph();
+
+  AtkText* GetAtkTextForChild(AtkRole expected_role);
+
  private:
   DISALLOW_COPY_AND_ASSIGN(AccessibilityAuraLinuxBrowserTest);
 };
 
-IN_PROC_BROWSER_TEST_F(ContentBrowserTest, AuraLinuxBrowserAccessibleParent) {
+void AccessibilityAuraLinuxBrowserTest::ExecuteScript(
+    const std::string& script) {
+  shell()->web_contents()->GetMainFrame()->ExecuteJavaScriptForTests(
+      base::UTF8ToUTF16(script));
+}
+
+void AccessibilityAuraLinuxBrowserTest::LoadInitialAccessibilityTreeFromHtml(
+    const std::string& html) {
+  AccessibilityNotificationWaiter waiter(shell()->web_contents(),
+                                         ui::kAXModeComplete,
+                                         ax::mojom::Event::kLoadComplete);
+  GURL html_data_url("data:text/html," +
+                     net::EscapeQueryParamValue(html, false));
+  NavigateToURL(shell(), html_data_url);
+  waiter.WaitForNotification();
+}
+
+AtkText* AccessibilityAuraLinuxBrowserTest::GetAtkTextForChild(
+    AtkRole expected_role) {
+  AtkObject* document = GetRendererAccessible();
+  EXPECT_EQ(1, atk_object_get_n_accessible_children(document));
+
+  AtkObject* parent_element = atk_object_ref_accessible_child(document, 0);
+  int number_of_children = atk_object_get_n_accessible_children(parent_element);
+  EXPECT_LT(0, number_of_children);
+
+  // The input field is always the last child.
+  AtkObject* input =
+      atk_object_ref_accessible_child(parent_element, number_of_children - 1);
+  EXPECT_EQ(expected_role, atk_object_get_role(input));
+
+  EXPECT_TRUE(ATK_IS_TEXT(input));
+  AtkText* atk_text = ATK_TEXT(input);
+
+  g_object_unref(parent_element);
+
+  return atk_text;
+}
+
+// Loads a page with  an input text field and places sample text in it.
+AtkText* AccessibilityAuraLinuxBrowserTest::SetUpInputField() {
+  LoadInitialAccessibilityTreeFromHtml(std::string(
+                                           R"HTML(<!DOCTYPE html>
+          <html>
+          <body>
+            <form>
+              <label for="textField">Browser name:</label>
+              <input type="text" id="textField" name="name" value=")HTML") +
+                                       net::EscapeForHTML(kInputContents) +
+                                       std::string(R"HTML(">
+            </form>
+          </body>
+          </html>)HTML"));
+
+  return GetAtkTextForChild(ATK_ROLE_ENTRY);
+}
+
+// Loads a page with  a textarea text field and places sample text in it. Also,
+// places the caret before the last character.
+AtkText* AccessibilityAuraLinuxBrowserTest::SetUpTextareaField() {
+  LoadInitialAccessibilityTreeFromHtml(std::string(R"HTML(<!DOCTYPE html>
+      <html>
+      <body>
+                    <textarea rows="3" cols="60">)HTML") +
+                                       net::EscapeForHTML(kTextareaContents) +
+                                       std::string(R"HTML(</textarea>
+          </body>
+          </html>)HTML"));
+
+  return GetAtkTextForChild(ATK_ROLE_ENTRY);
+}
+
+// Loads a page with  a paragraph of sample text.
+AtkText* AccessibilityAuraLinuxBrowserTest::SetUpSampleParagraph() {
+  LoadInitialAccessibilityTreeFromHtml(
+      R"HTML(<!DOCTYPE html>
+      <html>
+      <body>
+          <p><b>Game theory</b> is "the study of
+              <a href="" title="Mathematical model">mathematical models</a>
+              of conflict and<br>cooperation between intelligent rational
+              decision-makers."
+          </p>
+      </body>
+      </html>)HTML");
+
+  AtkObject* document = GetRendererAccessible();
+  EXPECT_EQ(1, atk_object_get_n_accessible_children(document));
+
+  int number_of_children = atk_object_get_n_accessible_children(document);
+  EXPECT_LT(0, number_of_children);
+
+  // The input field is always the last child.
+  AtkObject* input = atk_object_ref_accessible_child(document, 0);
+  EXPECT_EQ(ATK_ROLE_PARAGRAPH, atk_object_get_role(input));
+
+  EXPECT_TRUE(ATK_IS_TEXT(input));
+  return ATK_TEXT(input);
+}
+
+// Ensures that the text and the start and end offsets retrieved using
+// get_textAtOffset match the expected values.
+void AccessibilityAuraLinuxBrowserTest::CheckTextAtOffset(
+    AtkText* text_object,
+    int offset,
+    AtkTextBoundary boundary_type,
+    int expected_start_offset,
+    int expected_end_offset,
+    const char* expected_text) {
+  testing::Message message;
+  message << "While checking for \'" << expected_text << "\' at "
+          << expected_start_offset << '-' << expected_end_offset << '.';
+  SCOPED_TRACE(message);
+
+  int start_offset = 0;
+  int end_offset = 0;
+  char* text = atk_text_get_text_at_offset(text_object, offset, boundary_type,
+                                           &start_offset, &end_offset);
+  EXPECT_EQ(expected_start_offset, start_offset);
+  EXPECT_EQ(expected_end_offset, end_offset);
+  EXPECT_STREQ(expected_text, text);
+  g_free(text);
+}
+
+IN_PROC_BROWSER_TEST_F(AccessibilityAuraLinuxBrowserTest,
+                       AuraLinuxBrowserAccessibleParent) {
   AccessibilityNotificationWaiter waiter(shell()->web_contents(),
                                          ui::kAXModeComplete,
                                          ax::mojom::Event::kLoadComplete);
@@ -61,4 +224,56 @@
           host_view_parent));
 }
 
+IN_PROC_BROWSER_TEST_F(AccessibilityAuraLinuxBrowserTest,
+                       TestTextAtOffsetWithBoundaryLine) {
+  AtkText* atk_text = SetUpInputField();
+
+  // Single line text fields should return the whole text.
+  CheckTextAtOffset(atk_text, 0, ATK_TEXT_BOUNDARY_LINE_START, 0,
+                    kContentsLength, kInputContents);
+}
+
+IN_PROC_BROWSER_TEST_F(AccessibilityAuraLinuxBrowserTest,
+                       TestMultiLineTextAtOffsetWithBoundaryLine) {
+  AtkText* atk_text = SetUpTextareaField();
+
+  CheckTextAtOffset(atk_text, 0, ATK_TEXT_BOUNDARY_LINE_START, 0, 24,
+                    "Moz/5.0 (ST 6.x; WWW33)\n");
+
+  // If the offset is at the newline, return the line preceding it.
+  CheckTextAtOffset(atk_text, 31, ATK_TEXT_BOUNDARY_LINE_START, 24, 32,
+                    "WebKit \n");
+
+  // Last line does not have a trailing newline.
+  CheckTextAtOffset(atk_text, 32, ATK_TEXT_BOUNDARY_LINE_START, 32,
+                    kContentsLength, "\"KHTML, like\".");
+}
+
+IN_PROC_BROWSER_TEST_F(AccessibilityAuraLinuxBrowserTest,
+                       TestParagraphTextAtOffsetWithBoundaryLine) {
+  AtkText* atk_text = SetUpSampleParagraph();
+
+  // There should be two lines in this paragraph.
+  const int newline_offset = 46;
+  int n_characters = atk_text_get_character_count(atk_text);
+  ASSERT_LT(newline_offset, n_characters);
+
+  const base::string16 string16_embed(
+      1, ui::AXPlatformNodeAuraLinux::kEmbeddedCharacter);
+  std::string expected_string = "Game theory is \"the study of " +
+                                base::UTF16ToUTF8(string16_embed) +
+                                " of conflict and\n";
+  for (int i = 0; i <= newline_offset; ++i) {
+    CheckTextAtOffset(atk_text, i, ATK_TEXT_BOUNDARY_LINE_START, 0,
+                      newline_offset + 1, expected_string.c_str());
+  }
+
+  for (int i = newline_offset + 1; i < n_characters; ++i) {
+    CheckTextAtOffset(
+        atk_text, i, ATK_TEXT_BOUNDARY_LINE_START, newline_offset + 1,
+        n_characters,
+        "cooperation between intelligent rational decision-makers.\"");
+  }
+}
+
 }  // namespace content
diff --git a/content/browser/accessibility/browser_accessibility.cc b/content/browser/accessibility/browser_accessibility.cc
index eeb6402..b0282dd 100644
--- a/content/browser/accessibility/browser_accessibility.cc
+++ b/content/browser/accessibility/browser_accessibility.cc
@@ -992,6 +992,44 @@
   return unique_id_;
 }
 
+ui::AXPlatformNodeDelegate::EnclosingBoundaryOffsets
+BrowserAccessibility::FindTextBoundariesAtOffset(
+    ui::TextBoundaryType boundary_type,
+    int offset,
+    ax::mojom::TextAffinity affinity) const {
+  switch (boundary_type) {
+    case ui::WORD_BOUNDARY: {
+      BrowserAccessibilityPositionInstance position =
+          CreatePositionAt(static_cast<int>(offset), affinity);
+      BrowserAccessibilityPositionInstance previous_word_start =
+          position->CreatePreviousWordStartPosition(
+              ui::AXBoundaryBehavior::StopIfAlreadyAtBoundary);
+      BrowserAccessibilityPositionInstance next_word_start =
+          position->CreateNextWordStartPosition(
+              ui::AXBoundaryBehavior::StopAtAnchorBoundary);
+      return std::make_pair(previous_word_start->text_offset(),
+                            next_word_start->text_offset());
+    }
+    case ui::LINE_BOUNDARY: {
+      BrowserAccessibilityPositionInstance position =
+          CreatePositionAt(static_cast<int>(offset), affinity);
+      BrowserAccessibilityPositionInstance previous_line_start =
+          position->CreatePreviousLineStartPosition(
+              ui::AXBoundaryBehavior::StopIfAlreadyAtBoundary);
+      BrowserAccessibilityPositionInstance next_line_start =
+          position->CreateNextLineStartPosition(
+              ui::AXBoundaryBehavior::StopAtAnchorBoundary);
+      return std::make_pair(previous_line_start->text_offset(),
+                            next_line_start->text_offset());
+    }
+    case ui::CHAR_BOUNDARY:
+    case ui::SENTENCE_BOUNDARY:
+    case ui::PARAGRAPH_BOUNDARY:
+    case ui::ALL_BOUNDARY:
+      return base::nullopt;
+  }
+}
+
 gfx::NativeViewAccessible BrowserAccessibility::GetNativeViewAccessible() {
   // TODO(703369) On Windows, where we have started to migrate to an
   // AXPlatformNode implementation, the BrowserAccessibilityWin subclass has
diff --git a/content/browser/accessibility/browser_accessibility.h b/content/browser/accessibility/browser_accessibility.h
index 3e731b6..0959288 100644
--- a/content/browser/accessibility/browser_accessibility.h
+++ b/content/browser/accessibility/browser_accessibility.h
@@ -358,6 +358,11 @@
   int GetIndexInParent() const override;
   gfx::AcceleratedWidget GetTargetForNativeAccessibilityEvent() override;
 
+  ui::AXPlatformNodeDelegate::EnclosingBoundaryOffsets
+  FindTextBoundariesAtOffset(ui::TextBoundaryType boundary_type,
+                             int offset,
+                             ax::mojom::TextAffinity affinity) const override;
+
   bool IsTable() const override;
   int32_t GetTableColCount() const override;
   int32_t GetTableRowCount() const override;
diff --git a/content/browser/accessibility/browser_accessibility_auralinux.cc b/content/browser/accessibility/browser_accessibility_auralinux.cc
index d41f3fa..ad27587 100644
--- a/content/browser/accessibility/browser_accessibility_auralinux.cc
+++ b/content/browser/accessibility/browser_accessibility_auralinux.cc
@@ -55,6 +55,10 @@
   return true;
 }
 
+base::string16 BrowserAccessibilityAuraLinux::GetText() const {
+  return GetNode()->AXPlatformNodeAuraLinux::GetText();
+}
+
 ui::AXPlatformNode* BrowserAccessibilityAuraLinux::GetFromNodeID(int32_t id) {
   if (!instance_active())
     return nullptr;
diff --git a/content/browser/accessibility/browser_accessibility_auralinux.h b/content/browser/accessibility/browser_accessibility_auralinux.h
index ac38ccd4..b644c7f 100644
--- a/content/browser/accessibility/browser_accessibility_auralinux.h
+++ b/content/browser/accessibility/browser_accessibility_auralinux.h
@@ -31,6 +31,8 @@
   // BrowserAccessibility methods.
   void OnDataChanged() override;
   bool IsNative() const override;
+  base::string16 GetText() const override;
+
   gfx::NativeViewAccessible GetNativeViewAccessible() override;
   ui::AXPlatformNode* GetFromNodeID(int32_t id) override;
 
diff --git a/content/browser/accessibility/browser_accessibility_manager.cc b/content/browser/accessibility/browser_accessibility_manager.cc
index 0443aae..d96a0347 100644
--- a/content/browser/accessibility/browser_accessibility_manager.cc
+++ b/content/browser/accessibility/browser_accessibility_manager.cc
@@ -5,6 +5,7 @@
 #include "content/browser/accessibility/browser_accessibility_manager.h"
 
 #include <stddef.h>
+#include <algorithm>
 #include <map>
 #include <utility>
 
@@ -31,6 +32,45 @@
 base::LazyInstance<base::Closure>::DestructorAtExit
     g_focus_change_callback_for_testing = LAZY_INSTANCE_INITIALIZER;
 
+// If 2 or more tree updates can all be merged into others,
+// process the whole set of tree updates, copying them to |dst|,
+// and returning true.  Otherwise, return false and |dst|
+// is left unchanged.
+//
+// Merging tree updates helps minimize the overhead of calling
+// Unserialize multiple times.
+bool MergeTreeUpdates(const std::vector<ui::AXTreeUpdate>& src,
+                      std::vector<ui::AXTreeUpdate>* dst) {
+  size_t merge_count = 0;
+  for (size_t i = 1; i < src.size(); i++) {
+    if (ui::TreeUpdatesCanBeMerged(src[i - 1], src[i]))
+      merge_count++;
+  }
+
+  // Doing a single merge isn't necessarily worth it because
+  // copying the tree updates takes time too so the total
+  // savings is less. But two more more merges is probably
+  // worth the overhead of copying.
+  if (merge_count < 2)
+    return false;
+
+  dst->resize(src.size() - merge_count);
+  (*dst)[0] = src[0];
+  size_t dst_index = 0;
+  for (size_t i = 1; i < src.size(); i++) {
+    if (ui::TreeUpdatesCanBeMerged(src[i - 1], src[i])) {
+      std::vector<ui::AXNodeData>& dst_nodes = (*dst)[dst_index].nodes;
+      const std::vector<ui::AXNodeData>& src_nodes = src[i].nodes;
+      dst_nodes.insert(dst_nodes.end(), src_nodes.begin(), src_nodes.end());
+    } else {
+      dst_index++;
+      (*dst)[dst_index] = src[i];
+    }
+  }
+
+  return true;
+}
+
 }  // namespace
 
 ui::AXTreeUpdate MakeAXTreeUpdate(
@@ -333,9 +373,15 @@
   if (delegate_ && !use_custom_device_scale_factor_for_testing_)
     device_scale_factor_ = delegate_->AccessibilityGetDeviceScaleFactor();
 
+  // Optionally merge multiple tree updates into fewer updates.
+  const std::vector<ui::AXTreeUpdate>* tree_updates = &details.updates;
+  std::vector<ui::AXTreeUpdate> merged_tree_updates;
+  if (MergeTreeUpdates(details.updates, &merged_tree_updates))
+    tree_updates = &merged_tree_updates;
+
   // Process all changes to the accessibility tree first.
-  for (uint32_t index = 0; index < details.updates.size(); ++index) {
-    if (!tree_->Unserialize(details.updates[index])) {
+  for (uint32_t index = 0; index < tree_updates->size(); ++index) {
+    if (!tree_->Unserialize((*tree_updates)[index])) {
       if (delegate_) {
         LOG(ERROR) << tree_->error();
         delegate_->AccessibilityFatalError();
diff --git a/content/browser/accessibility/browser_accessibility_manager_unittest.cc b/content/browser/accessibility/browser_accessibility_manager_unittest.cc
index e47e2e256..17ec486d 100644
--- a/content/browser/accessibility/browser_accessibility_manager_unittest.cc
+++ b/content/browser/accessibility/browser_accessibility_manager_unittest.cc
@@ -5,6 +5,7 @@
 #include <stddef.h>
 #include <stdint.h>
 
+#include "base/scoped_observer.h"
 #include "base/strings/string16.h"
 #include "base/strings/utf_string_conversions.h"
 #include "build/build_config.h"
@@ -78,6 +79,26 @@
   bool got_fatal_error_;
 };
 
+class CountingAXTreeObserver : public ui::AXTreeObserver {
+ public:
+  CountingAXTreeObserver() {}
+  ~CountingAXTreeObserver() override {}
+
+  int update_count() { return update_count_; }
+  int node_count() { return node_count_; }
+
+ private:
+  void OnAtomicUpdateFinished(ui::AXTree* tree,
+                              bool root_changed,
+                              const std::vector<Change>& changes) override {
+    update_count_++;
+    node_count_ += static_cast<int>(changes.size());
+  }
+
+  int update_count_ = 0;
+  int node_count_ = 0;
+};
+
 }  // anonymous namespace
 
 TEST(BrowserAccessibilityManagerTest, TestNoLeaks) {
@@ -1552,4 +1573,52 @@
   ASSERT_EQ(3, manager->GetFocus()->GetId());
 }
 
+TEST(BrowserAccessibilityManagerTest, TreeUpdatesAreMergedWhenPossible) {
+  ui::AXTreeUpdate tree;
+  tree.root_id = 1;
+  tree.nodes.resize(4);
+  tree.nodes[0].id = 1;
+  tree.nodes[0].role = ax::mojom::Role::kMenu;
+  tree.nodes[0].child_ids = {2, 3, 4};
+  tree.nodes[1].id = 2;
+  tree.nodes[1].role = ax::mojom::Role::kMenuItem;
+  tree.nodes[2].id = 3;
+  tree.nodes[2].role = ax::mojom::Role::kMenuItemCheckBox;
+  tree.nodes[3].id = 4;
+  tree.nodes[3].role = ax::mojom::Role::kMenuItemRadio;
+
+  std::unique_ptr<BrowserAccessibilityManager> manager(
+      BrowserAccessibilityManager::Create(
+          tree, nullptr, new CountedBrowserAccessibilityFactory()));
+
+  CountingAXTreeObserver observer;
+  manager->ax_tree()->AddObserver(&observer);
+
+  // Update each of the children using separate AXTreeUpdates.
+  AXEventNotificationDetails events;
+  events.updates.resize(3);
+  for (int i = 0; i < 3; i++) {
+    ui::AXTreeUpdate update;
+    update.root_id = 1;
+    update.nodes.resize(1);
+    update.nodes[0].id = 2 + i;
+    events.updates[i] = update;
+  }
+  events.updates[0].nodes[0].role = ax::mojom::Role::kMenuItemCheckBox;
+  events.updates[1].nodes[0].role = ax::mojom::Role::kMenuItemRadio;
+  events.updates[2].nodes[0].role = ax::mojom::Role::kMenuItem;
+  manager->OnAccessibilityEvents(events);
+
+  // These should have been merged into a single tree update.
+  EXPECT_EQ(1, observer.update_count());
+
+  EXPECT_EQ(ax::mojom::Role::kMenuItemCheckBox,
+            manager->GetFromID(2)->GetRole());
+  EXPECT_EQ(ax::mojom::Role::kMenuItemRadio, manager->GetFromID(3)->GetRole());
+  EXPECT_EQ(ax::mojom::Role::kMenuItem, manager->GetFromID(4)->GetRole());
+
+  // Remove the observer before the manager is destroyed.
+  manager->ax_tree()->RemoveObserver(&observer);
+}
+
 }  // namespace content
diff --git a/content/browser/blob_storage/blob_registry_wrapper.cc b/content/browser/blob_storage/blob_registry_wrapper.cc
index 3c7fc60..ad140325 100644
--- a/content/browser/blob_storage/blob_registry_wrapper.cc
+++ b/content/browser/blob_storage/blob_registry_wrapper.cc
@@ -38,6 +38,11 @@
         ChildProcessSecurityPolicyImpl::GetInstance();
     return security_policy->CanCommitURL(process_id_, url);
   }
+  bool IsProcessValid() override {
+    ChildProcessSecurityPolicyImpl* security_policy =
+        ChildProcessSecurityPolicyImpl::GetInstance();
+    return security_policy->HasSecurityState(process_id_);
+  }
 
  private:
   const int process_id_;
diff --git a/content/browser/blob_storage/blob_storage_browsertest.cc b/content/browser/blob_storage/blob_storage_browsertest.cc
index 9e1a6016..dca837e67 100644
--- a/content/browser/blob_storage/blob_storage_browsertest.cc
+++ b/content/browser/blob_storage/blob_storage_browsertest.cc
@@ -2,6 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+#include <functional>
+
 #include "base/bind.h"
 #include "base/run_loop.h"
 #include "base/task/post_task.h"
@@ -63,7 +65,7 @@
     base::PostTaskWithTraits(
         FROM_HERE, {BrowserThread::IO},
         base::BindOnce(&SetBlobLimitsOnIO, GetBlobContext(),
-                       base::ConstRef(limits_)));
+                       std::cref(limits_)));
   }
 
   void SimpleTest(const GURL& test_url, bool incognito = false) {
diff --git a/content/browser/browser_main_loop.cc b/content/browser/browser_main_loop.cc
index 368011e..1ac6a2d 100644
--- a/content/browser/browser_main_loop.cc
+++ b/content/browser/browser_main_loop.cc
@@ -1263,7 +1263,7 @@
     compositing_mode_reporter_impl_ =
         std::make_unique<viz::CompositingModeReporterImpl>();
 
-    if (base::FeatureList::IsEnabled(features::kVizDisplayCompositor)) {
+    if (features::IsVizDisplayCompositorEnabled()) {
       auto transport_factory = std::make_unique<VizProcessTransportFactory>(
           BrowserGpuChannelHostFactory::instance(), GetResizeTaskRunner(),
           compositing_mode_reporter_impl_.get());
diff --git a/content/browser/child_process_launcher_helper_mac.cc b/content/browser/child_process_launcher_helper_mac.cc
index 588e4ce0..c69c3d3 100644
--- a/content/browser/child_process_launcher_helper_mac.cc
+++ b/content/browser/child_process_launcher_helper_mac.cc
@@ -4,6 +4,7 @@
 
 #include "base/command_line.h"
 #include "base/feature_list.h"
+#include "base/metrics/field_trial.h"
 #include "base/path_service.h"
 #include "base/posix/global_descriptors.h"
 #include "base/strings/stringprintf.h"
@@ -65,6 +66,9 @@
   options->fds_to_remap = files_to_register.GetMappingWithIDAdjustment(
       base::GlobalDescriptors::kBaseDescriptor);
 
+  base::FieldTrialList::InsertFieldTrialHandleIfNeeded(
+      &options->mach_ports_for_rendezvous);
+
   options->environ = delegate_->GetEnvironment();
 
   auto sandbox_type =
diff --git a/content/browser/child_process_security_policy_impl.cc b/content/browser/child_process_security_policy_impl.cc
index 46813b6..7530fb6c 100644
--- a/content/browser/child_process_security_policy_impl.cc
+++ b/content/browser/child_process_security_policy_impl.cc
@@ -1558,6 +1558,11 @@
     isolated_origins_.erase(key);
 }
 
+bool ChildProcessSecurityPolicyImpl::HasSecurityState(int child_id) {
+  base::AutoLock lock(lock_);
+  return GetSecurityState(child_id) != nullptr;
+}
+
 ChildProcessSecurityPolicyImpl::SecurityState*
 ChildProcessSecurityPolicyImpl::GetSecurityState(int child_id) {
   auto itr = security_state_.find(child_id);
diff --git a/content/browser/child_process_security_policy_impl.h b/content/browser/child_process_security_policy_impl.h
index 4b237c2b..68b6a5e 100644
--- a/content/browser/child_process_security_policy_impl.h
+++ b/content/browser/child_process_security_policy_impl.h
@@ -350,6 +350,16 @@
   //       renderer-initiated navigations.
   bool CanRedirectToURL(const GURL& url);
 
+  // Returns true if the policy object has security state information for
+  // |child_id|. This is essentially a way to determine if the policy object
+  // is actively tracking permissions for |child_id|. This method can be called
+  // from the UI & IO threads.
+  //
+  // DO NOT ADD NEW CALLERS OF THIS METHOD.
+  // TODO(933089): Remove this method once a better long term solution is
+  // implemented for the one caller doing Blob URL revocation.
+  bool HasSecurityState(int child_id);
+
  private:
   friend class ChildProcessSecurityPolicyInProcessBrowserTest;
   friend class ChildProcessSecurityPolicyTest;
diff --git a/content/browser/child_process_security_policy_unittest.cc b/content/browser/child_process_security_policy_unittest.cc
index 99c96260..733f7273d 100644
--- a/content/browser/child_process_security_policy_unittest.cc
+++ b/content/browser/child_process_security_policy_unittest.cc
@@ -1129,8 +1129,7 @@
 
   pending_remove_complete_event.Wait();
 
-  // Capture state after IO thread task has run, but before the task it posted
-  // to the UI thread has run.
+  // Capture state after IO thread task has run.
   ui_after_io_task_completed = p->CanAccessDataForOrigin(kRendererID, url);
 
   // Run pending UI thread tasks.
@@ -1498,4 +1497,118 @@
                                    url::Origin::Create(GURL("file:///foo"))));
 }
 
+// Tests behavior of HasSecurityState() during race conditions that
+// can occur during Remove(). It verifies that SecurityState for a child ID is
+// preserved after a Remove() call until the task, that Remove() has posted to
+// the IO thread, has run.
+//
+// We use a combination of waitable events and extra tasks posted to the
+// threads to capture permission state from the UI & IO threads during the
+// removal process. It is intended to simulate pending tasks that could be
+// run on each thread during removal.
+TEST_F(ChildProcessSecurityPolicyTest, HasSecurityState) {
+  ChildProcessSecurityPolicyImpl* p =
+      ChildProcessSecurityPolicyImpl::GetInstance();
+
+  GURL url("file:///etc/passwd");
+
+  EXPECT_FALSE(p->HasSecurityState(kRendererID));
+
+  p->Add(kRendererID, browser_context());
+
+  base::WaitableEvent ready_for_remove_event;
+  base::WaitableEvent remove_called_event;
+  base::WaitableEvent pending_remove_complete_event;
+
+  // Keep track of the return value for HasSecurityState() at various
+  // points in time during the test.
+  bool io_before_remove = false;
+  bool io_while_io_task_pending = false;
+  bool io_after_io_task_completed = false;
+  bool ui_before_remove = false;
+  bool ui_while_io_task_pending = false;
+  bool ui_after_io_task_completed = false;
+
+  // Post a task that will run on the IO thread before the task that
+  // Remove() will post to the IO thread.
+  base::PostTaskWithTraits(
+      FROM_HERE, {BrowserThread::IO}, base::BindLambdaForTesting([&]() {
+        // Capture state on the IO thread before Remove() is called.
+        io_before_remove = p->HasSecurityState(kRendererID);
+
+        // Tell the UI thread we are ready for Remove() to be called.
+        ready_for_remove_event.Signal();
+
+        // Wait for Remove() to be called on the UI thread.
+        remove_called_event.Wait();
+
+        // Capture state after Remove() is called, but before its task on
+        // the IO thread runs.
+        io_while_io_task_pending = p->HasSecurityState(kRendererID);
+      }));
+
+  ready_for_remove_event.Wait();
+
+  ui_before_remove = p->HasSecurityState(kRendererID);
+
+  p->Remove(kRendererID);
+
+  // Post a task to run after the task Remove() posted on the IO thread.
+  base::PostTaskWithTraits(
+      FROM_HERE, {BrowserThread::IO}, base::BindLambdaForTesting([&]() {
+        io_after_io_task_completed = p->HasSecurityState(kRendererID);
+
+        // Tell the UI thread that the task from Remove()
+        // has completed on the IO thread.
+        pending_remove_complete_event.Signal();
+      }));
+
+  // Capture state after Remove() has been called, but before its IO thread
+  // task has run. We know the IO thread task hasn't run yet because the
+  // task we posted before the Remove() call is waiting for us to signal
+  // |remove_called_event|.
+  ui_while_io_task_pending = p->HasSecurityState(kRendererID);
+
+  // Unblock the IO thread so the pending remove events can run.
+  remove_called_event.Signal();
+
+  pending_remove_complete_event.Wait();
+
+  // Capture state after IO thread task has run.
+  ui_after_io_task_completed = p->HasSecurityState(kRendererID);
+
+  // Run pending UI thread tasks.
+  base::RunLoop run_loop;
+  run_loop.RunUntilIdle();
+
+  bool ui_after_remove_complete = p->HasSecurityState(kRendererID);
+  bool io_after_remove_complete = false;
+  base::WaitableEvent after_remove_complete_event;
+
+  base::PostTaskWithTraits(
+      FROM_HERE, {BrowserThread::IO}, base::BindLambdaForTesting([&]() {
+        io_after_remove_complete = p->HasSecurityState(kRendererID);
+
+        // Tell the UI thread that this task has
+        // has completed on the IO thread.
+        after_remove_complete_event.Signal();
+      }));
+
+  // Wait for the task we just posted to the IO thread to complete.
+  after_remove_complete_event.Wait();
+
+  // Verify expected states at various parts of the removal.
+  // Note: IO thread is expected to keep pre-Remove() permissions until
+  // the task Remove() posted runs on the IO thread.
+  EXPECT_TRUE(io_before_remove);
+  EXPECT_TRUE(io_while_io_task_pending);
+  EXPECT_FALSE(io_after_io_task_completed);
+
+  EXPECT_TRUE(ui_before_remove);
+  EXPECT_FALSE(ui_while_io_task_pending);
+  EXPECT_FALSE(ui_after_io_task_completed);
+
+  EXPECT_FALSE(ui_after_remove_complete);
+  EXPECT_FALSE(io_after_remove_complete);
+}
 }  // namespace content
diff --git a/content/browser/compositor/reflector_impl_unittest.cc b/content/browser/compositor/reflector_impl_unittest.cc
index 97507c5..687a1f5b 100644
--- a/content/browser/compositor/reflector_impl_unittest.cc
+++ b/content/browser/compositor/reflector_impl_unittest.cc
@@ -201,7 +201,7 @@
 TEST_F(ReflectorImplTest, CheckNormalOutputSurface) {
   // TODO(jonross): Re-enable once Reflector is re-written to work with
   // VizDisplayCompositor. https://crbug.com/601869
-  if (base::FeatureList::IsEnabled(features::kVizDisplayCompositor))
+  if (features::IsVizDisplayCompositorEnabled())
     return;
   output_surface_->SetFlip(false);
   SetUpReflector();
@@ -215,7 +215,7 @@
 TEST_F(ReflectorImplTest, CheckInvertedOutputSurface) {
   // TODO(jonross): Re-enable once Reflector is re-written to work with
   // VizDisplayCompositor. https://crbug.com/601869
-  if (base::FeatureList::IsEnabled(features::kVizDisplayCompositor))
+  if (features::IsVizDisplayCompositorEnabled())
     return;
   output_surface_->SetFlip(true);
   SetUpReflector();
@@ -228,7 +228,7 @@
 TEST_F(ReflectorImplTest, CheckOverlayNoReflector) {
   // TODO(jonross): Re-enable once Reflector is re-written to work with
   // VizDisplayCompositor. https://crbug.com/601869
-  if (base::FeatureList::IsEnabled(features::kVizDisplayCompositor))
+  if (features::IsVizDisplayCompositorEnabled())
     return;
   viz::OverlayCandidateList list;
   viz::OverlayCandidate plane_1, plane_2;
@@ -243,7 +243,7 @@
 TEST_F(ReflectorImplTest, CheckOverlaySWMirroring) {
   // TODO(jonross): Re-enable once Reflector is re-written to work with
   // VizDisplayCompositor. https://crbug.com/601869
-  if (base::FeatureList::IsEnabled(features::kVizDisplayCompositor))
+  if (features::IsVizDisplayCompositorEnabled())
     return;
   SetUpReflector();
   viz::OverlayCandidateList list;
diff --git a/content/browser/compositor/test/test_image_transport_factory.cc b/content/browser/compositor/test/test_image_transport_factory.cc
index 6ea1ef7..f4ec67e 100644
--- a/content/browser/compositor/test/test_image_transport_factory.cc
+++ b/content/browser/compositor/test/test_image_transport_factory.cc
@@ -34,8 +34,7 @@
 }  // namespace
 
 TestImageTransportFactory::TestImageTransportFactory()
-    : enable_viz_(
-          base::FeatureList::IsEnabled(features::kVizDisplayCompositor)),
+    : enable_viz_(features::IsVizDisplayCompositorEnabled()),
       frame_sink_id_allocator_(kDefaultClientId) {
   if (enable_viz_) {
     test_frame_sink_manager_impl_ =
diff --git a/content/browser/cookie_store/cookie_change_subscription.cc b/content/browser/cookie_store/cookie_change_subscription.cc
index 288f760..9ef5974e 100644
--- a/content/browser/cookie_store/cookie_change_subscription.cc
+++ b/content/browser/cookie_store/cookie_change_subscription.cc
@@ -171,8 +171,8 @@
   }
 
   net::CookieOptions net_options;
-  net_options.set_same_site_cookie_mode(
-      net::CookieOptions::SameSiteCookieMode::INCLUDE_STRICT_AND_LAX);
+  net_options.set_same_site_cookie_context(
+      net::CookieOptions::SameSiteCookieContext::SAME_SITE_STRICT);
 
   return cookie.IncludeForRequestURL(url_, net_options) ==
          net::CanonicalCookie::CookieInclusionStatus::INCLUDE;
diff --git a/content/browser/devtools/devtools_url_interceptor_request_job.cc b/content/browser/devtools/devtools_url_interceptor_request_job.cc
index 1f6750bd..47fa1749 100644
--- a/content/browser/devtools/devtools_url_interceptor_request_job.cc
+++ b/content/browser/devtools/devtools_url_interceptor_request_job.cc
@@ -18,14 +18,13 @@
 #include "net/base/completion_once_callback.h"
 #include "net/base/elements_upload_data_stream.h"
 #include "net/base/io_buffer.h"
-#include "net/base/registry_controlled_domains/registry_controlled_domain.h"
 #include "net/base/upload_bytes_element_reader.h"
 #include "net/base/upload_element_reader.h"
 #include "net/cert/cert_status_flags.h"
 #include "net/cookies/cookie_options.h"
 #include "net/cookies/cookie_store.h"
+#include "net/cookies/cookie_util.h"
 #include "net/http/http_response_headers.h"
-#include "net/http/http_util.h"
 #include "net/traffic_annotation/network_traffic_annotation.h"
 #include "net/url_request/url_request_context.h"
 #include "third_party/blink/public/platform/resource_request_blocked_reason.h"
@@ -587,48 +586,13 @@
     return;
   }
 
+  // Matches what URLRequestHttpJob would use.
   net::CookieOptions options;
   options.set_include_httponly();
-  // The below is a copy of the logic in URLRequestHttpJob
-
-  // Set SameSiteCookieMode according to the rules laid out in
-  // https://tools.ietf.org/html/draft-ietf-httpbis-cookie-same-site:
-  //
-  // * Include both "strict" and "lax" same-site cookies if the request's
-  //   |url|, |initiator|, and |site_for_cookies| all have the same
-  //   registrable domain. Note: this also covers the case of a request
-  //   without an initiator (only happens for browser-initiated main frame
-  //   navigations).
-  //
-  // * Include only "lax" same-site cookies if the request's |URL| and
-  //   |site_for_cookies| have the same registrable domain, _and_ the
-  //   request's |method| is "safe" ("GET" or "HEAD").
-  //
-  //   Note that this will generally be the case only for cross-site requests
-  //   which target a top-level browsing context.
-  //
-  // * Include both "strict" and "lax" same-site cookies if the request is
-  //   tagged with a flag allowing it.
-  //   Note that this can be the case for requests initiated by extensions,
-  //   which need to behave as though they are made by the document itself,
-  //   but appear like cross-site ones.
-  //
-  // * Otherwise, do not include same-site cookies.
-  using namespace net::registry_controlled_domains;
-  if (SameDomainOrHost(request()->url(), request()->site_for_cookies(),
-                       INCLUDE_PRIVATE_REGISTRIES)) {
-    if (!request()->initiator() ||
-        SameDomainOrHost(request()->url(),
-                         request()->initiator().value().GetURL(),
-                         INCLUDE_PRIVATE_REGISTRIES) ||
-        request()->attach_same_site_cookies()) {
-      options.set_same_site_cookie_mode(
-          net::CookieOptions::SameSiteCookieMode::INCLUDE_STRICT_AND_LAX);
-    } else if (net::HttpUtil::IsMethodSafe(request()->method())) {
-      options.set_same_site_cookie_mode(
-          net::CookieOptions::SameSiteCookieMode::INCLUDE_LAX);
-    }
-  }
+  options.set_same_site_cookie_context(
+      net::cookie_util::ComputeSameSiteContextForRequest(
+          request()->method(), request()->url(), request()->site_for_cookies(),
+          request()->initiator(), request()->attach_same_site_cookies()));
 
   store->GetCookieListWithOptionsAsync(
       request_details_.url, options,
diff --git a/content/browser/devtools/devtools_url_loader_interceptor.cc b/content/browser/devtools/devtools_url_loader_interceptor.cc
index e64ee32..b9c8638 100644
--- a/content/browser/devtools/devtools_url_loader_interceptor.cc
+++ b/content/browser/devtools/devtools_url_loader_interceptor.cc
@@ -22,7 +22,7 @@
 #include "mojo/public/cpp/system/data_pipe_drainer.h"
 #include "net/base/load_flags.h"
 #include "net/base/mime_sniffer.h"
-#include "net/base/registry_controlled_domains/registry_controlled_domain.h"
+#include "net/cookies/cookie_util.h"
 #include "net/http/http_util.h"
 #include "net/url_request/redirect_util.h"
 #include "net/url_request/url_request.h"
@@ -1190,46 +1190,11 @@
 
   const network::ResourceRequest& request = create_loader_params_->request;
 
-  // The below is a copy of the logic in URLRequestHttpJob
+  options.set_same_site_cookie_context(
+      net::cookie_util::ComputeSameSiteContextForRequest(
+          request.method, request.url, request.site_for_cookies,
+          request.request_initiator, request.attach_same_site_cookies));
 
-  // Set SameSiteCookieMode according to the rules laid out in
-  // https://tools.ietf.org/html/draft-ietf-httpbis-cookie-same-site:
-  //
-  // * Include both "strict" and "lax" same-site cookies if the request's
-  //   |url|, |initiator|, and |site_for_cookies| all have the same
-  //   registrable domain. Note: this also covers the case of a request
-  //   without an initiator (only happens for browser-initiated main frame
-  //   navigations).
-  //
-  // * Include only "lax" same-site cookies if the request's |URL| and
-  //   |site_for_cookies| have the same registrable domain, _and_ the
-  //   request's |method| is "safe" ("GET" or "HEAD").
-  //
-  //   Note that this will generally be the case only for cross-site requests
-  //   which target a top-level browsing context.
-  //
-  // * Include both "strict" and "lax" same-site cookies if the request is
-  //   tagged with a flag allowing it.
-  //   Note that this can be the case for requests initiated by extensions,
-  //   which need to behave as though they are made by the document itself,
-  //   but appear like cross-site ones.
-  //
-  // * Otherwise, do not include same-site cookies.
-  using namespace net::registry_controlled_domains;
-  if (SameDomainOrHost(request.url, request.site_for_cookies,
-                       INCLUDE_PRIVATE_REGISTRIES)) {
-    if (!request.request_initiator ||
-        SameDomainOrHost(request.url,
-                         request.request_initiator.value().GetURL(),
-                         INCLUDE_PRIVATE_REGISTRIES) ||
-        request.attach_same_site_cookies) {
-      options.set_same_site_cookie_mode(
-          net::CookieOptions::SameSiteCookieMode::INCLUDE_STRICT_AND_LAX);
-    } else if (net::HttpUtil::IsMethodSafe(request.method)) {
-      options.set_same_site_cookie_mode(
-          net::CookieOptions::SameSiteCookieMode::INCLUDE_LAX);
-    }
-  }
   cookie_manager_->GetCookieList(request.url, options, std::move(callback));
 }
 
diff --git a/content/browser/devtools/protocol/network_handler.cc b/content/browser/devtools/protocol/network_handler.cc
index 4a0630801..f0207f0 100644
--- a/content/browser/devtools/protocol/network_handler.cc
+++ b/content/browser/devtools/protocol/network_handler.cc
@@ -250,8 +250,8 @@
         new CookieRetrieverNetworkService(std::move(callback));
     net::CookieOptions cookie_options;
     cookie_options.set_include_httponly();
-    cookie_options.set_same_site_cookie_mode(
-        net::CookieOptions::SameSiteCookieMode::INCLUDE_STRICT_AND_LAX);
+    cookie_options.set_same_site_cookie_context(
+        net::CookieOptions::SameSiteCookieContext::SAME_SITE_STRICT);
     cookie_options.set_do_not_update_access_time();
     for (const auto& url : urls) {
       cookie_manager->GetCookieList(
diff --git a/content/browser/fileapi/file_system_chooser_browsertest.cc b/content/browser/fileapi/file_system_chooser_browsertest.cc
index b2f9ebe5..131c076a 100644
--- a/content/browser/fileapi/file_system_chooser_browsertest.cc
+++ b/content/browser/fileapi/file_system_chooser_browsertest.cc
@@ -25,7 +25,7 @@
   void SetUp() override {
     ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
     scoped_feature_list_.InitAndEnableFeature(
-        blink::features::kWritableFilesAPI);
+        blink::features::kNativeFilesystemAPI);
 
     ASSERT_TRUE(embedded_test_server()->Start());
 
diff --git a/content/browser/fileapi/file_system_manager_impl.cc b/content/browser/fileapi/file_system_manager_impl.cc
index d525f15..227e29b 100644
--- a/content/browser/fileapi/file_system_manager_impl.cc
+++ b/content/browser/fileapi/file_system_manager_impl.cc
@@ -574,7 +574,7 @@
                                          CreateWriterCallback callback) {
   DCHECK_CURRENTLY_ON(BrowserThread::IO);
 
-  if (!base::FeatureList::IsEnabled(blink::features::kWritableFilesAPI)) {
+  if (!base::FeatureList::IsEnabled(blink::features::kNativeFilesystemAPI)) {
     bindings_.ReportBadMessage("FileSystemManager.CreateWriter");
     return;
   }
@@ -604,7 +604,7 @@
     bool include_accepts_all,
     ChooseEntryCallback callback) {
   DCHECK_CURRENTLY_ON(BrowserThread::IO);
-  if (!base::FeatureList::IsEnabled(blink::features::kWritableFilesAPI)) {
+  if (!base::FeatureList::IsEnabled(blink::features::kNativeFilesystemAPI)) {
     bindings_.ReportBadMessage("FSMI_WRITABLE_FILES_DISABLED");
     return;
   }
diff --git a/content/browser/frame_host/frame_tree_node.cc b/content/browser/frame_host/frame_tree_node.cc
index d6bbdb7..e8201d40 100644
--- a/content/browser/frame_host/frame_tree_node.cc
+++ b/content/browser/frame_host/frame_tree_node.cc
@@ -138,6 +138,8 @@
   // Remove the children.
   current_frame_host()->ResetChildren();
 
+  current_frame_host()->ResetLoadingState();
+
   // If the removed frame was created by a script, then its history entry will
   // never be reused - we can save some memory by removing the history entry.
   // See also https://crbug.com/784356.
@@ -167,6 +169,12 @@
     navigation_request_.reset();
     DidStopLoading();
   }
+
+  // ~SiteProcessCountTracker DCHECKs in some tests if CleanUpNavigation is not
+  // called last. Ideally this would be closer to (possible before) the
+  // ResetLoadingState() call above.
+  render_manager_.CleanUpNavigation();
+  DCHECK(!IsLoading());
 }
 
 void FrameTreeNode::AddObserver(Observer* observer) {
diff --git a/content/browser/frame_host/render_frame_host_feature_policy_unittest.cc b/content/browser/frame_host/render_frame_host_feature_policy_unittest.cc
index 803c9577..ed5f6ee 100644
--- a/content/browser/frame_host/render_frame_host_feature_policy_unittest.cc
+++ b/content/browser/frame_host/render_frame_host_feature_policy_unittest.cc
@@ -33,6 +33,14 @@
       blink::mojom::FeaturePolicyFeature::kDocumentWrite;
   static const blink::mojom::FeaturePolicyFeature kDefaultSelfFeature =
       blink::mojom::FeaturePolicyFeature::kGeolocation;
+  static const blink::mojom::FeaturePolicyFeature kParameterizedFeature =
+      blink::mojom::FeaturePolicyFeature::kOversizedImages;
+
+  const blink::PolicyValue sample_double_value =
+      blink::PolicyValue(2.5, blink::mojom::PolicyValueType::kDecDouble);
+  const blink::PolicyValue min_double_value =
+      blink::PolicyValue(2.0, blink::mojom::PolicyValueType::kDecDouble);
+  const blink::PolicyValue sample_bool_value = blink::PolicyValue(true);
 
   RenderFrameHost* GetMainRFH(const char* origin) {
     RenderFrameHost* result = web_contents()->GetMainFrame();
@@ -51,23 +59,25 @@
 
   // The header policy should only be set once on page load, so we refresh the
   // page to simulate that.
-  void RefreshPageAndSetHeaderPolicy(RenderFrameHost** rfh,
-                                     blink::mojom::FeaturePolicyFeature feature,
-                                     const std::vector<std::string>& origins) {
+  void RefreshPageAndSetHeaderPolicy(
+      RenderFrameHost** rfh,
+      blink::mojom::FeaturePolicyFeature feature,
+      const std::map<std::string, blink::PolicyValue>& values) {
     RenderFrameHost* current = *rfh;
     SimulateNavigation(&current, current->GetLastCommittedURL());
     static_cast<TestRenderFrameHost*>(current)->DidSetFramePolicyHeaders(
-        blink::WebSandboxFlags::kNone, CreateFPHeader(feature, origins));
+        blink::WebSandboxFlags::kNone, CreateFPHeader(feature, values));
     *rfh = current;
   }
 
-  void SetContainerPolicy(RenderFrameHost* parent,
-                          RenderFrameHost* child,
-                          blink::mojom::FeaturePolicyFeature feature,
-                          const std::vector<std::string>& origins) {
+  void SetContainerPolicy(
+      RenderFrameHost* parent,
+      RenderFrameHost* child,
+      blink::mojom::FeaturePolicyFeature feature,
+      const std::map<std::string, blink::PolicyValue>& values) {
     static_cast<TestRenderFrameHost*>(parent)->OnDidChangeFramePolicy(
         child->GetRoutingID(),
-        {blink::WebSandboxFlags::kNone, CreateFPHeader(feature, origins)});
+        {blink::WebSandboxFlags::kNone, CreateFPHeader(feature, values)});
   }
 
   void SimulateNavigation(RenderFrameHost** rfh, const GURL& url) {
@@ -80,14 +90,16 @@
  private:
   blink::ParsedFeaturePolicy CreateFPHeader(
       blink::mojom::FeaturePolicyFeature feature,
-      const std::vector<std::string>& origins) {
+      const std::map<std::string, blink::PolicyValue>& values) {
     blink::ParsedFeaturePolicy result(1);
     result[0].feature = feature;
-    // TODO(loonybear): Add a test for non-bool type PolicyValue.
-    // crbug.com/924568.
-    for (const std::string& origin : origins)
+    if (feature == kParameterizedFeature) {
+      result[0].fallback_value = min_double_value;
+      result[0].opaque_value = min_double_value;
+    }
+    for (auto const& value : values)
       result[0].values.insert(std::pair<url::Origin, blink::PolicyValue>(
-          url::Origin::Create(GURL(origin)), blink::PolicyValue(true)));
+          url::Origin::Create(GURL(value.first)), value.second));
     return result;
   }
 };
@@ -98,8 +110,10 @@
 
   EXPECT_TRUE(parent->IsFeatureEnabled(kDefaultEnabledFeature));
   EXPECT_TRUE(parent->IsFeatureEnabled(kDefaultSelfFeature));
+  EXPECT_TRUE(parent->IsFeatureEnabled(kParameterizedFeature));
   EXPECT_TRUE(child->IsFeatureEnabled(kDefaultEnabledFeature));
   EXPECT_FALSE(child->IsFeatureEnabled(kDefaultSelfFeature));
+  EXPECT_TRUE(child->IsFeatureEnabled(kParameterizedFeature));
 }
 
 TEST_F(RenderFrameHostFeaturePolicyTest, HeaderPolicy) {
@@ -107,7 +121,8 @@
 
   // Enable the feature for the child in the parent frame.
   RefreshPageAndSetHeaderPolicy(&parent, kDefaultSelfFeature,
-                                {kOrigin1, kOrigin2});
+                                {{std::string(kOrigin1), sample_bool_value},
+                                 {std::string(kOrigin2), sample_bool_value}});
 
   // Create the child.
   RenderFrameHost* child = AddChildRFH(parent, kOrigin2);
@@ -118,13 +133,14 @@
   // Set an empty allowlist in the child to test that the policies combine
   // correctly.
   RefreshPageAndSetHeaderPolicy(&child, kDefaultSelfFeature,
-                                std::vector<std::string>());
+                                std::map<std::string, blink::PolicyValue>());
 
   EXPECT_TRUE(parent->IsFeatureEnabled(kDefaultSelfFeature));
   EXPECT_FALSE(child->IsFeatureEnabled(kDefaultSelfFeature));
 
   // Re-enable the feature in the child.
-  RefreshPageAndSetHeaderPolicy(&child, kDefaultSelfFeature, {kOrigin2});
+  RefreshPageAndSetHeaderPolicy(&child, kDefaultSelfFeature,
+                                {{std::string(kOrigin2), sample_bool_value}});
   EXPECT_TRUE(child->IsFeatureEnabled(kDefaultSelfFeature));
 
   // Navigate the child. Check that the feature is disabled.
@@ -138,16 +154,18 @@
 
   // Set a container policy on origin 3 to give it the feature. It should not
   // be enabled because container policy will only take effect after navigation.
-  SetContainerPolicy(parent, child, kDefaultSelfFeature, {kOrigin2, kOrigin3});
+  SetContainerPolicy(parent, child, kDefaultSelfFeature,
+                     {{std::string(kOrigin2), sample_bool_value},
+                      {std::string(kOrigin3), sample_bool_value}});
   EXPECT_FALSE(child->IsFeatureEnabled(kDefaultSelfFeature));
 
   // Navigate the child so that the container policy takes effect.
   SimulateNavigation(&child, GURL(kOrigin3));
   EXPECT_TRUE(child->IsFeatureEnabled(kDefaultSelfFeature));
 
-  // // Navigate the child again, the feature should not be enabled.
-  // SimulateNavigation(&child, GURL(kOrigin4));
-  // EXPECT_FALSE(child->IsFeatureEnabled(kDefaultSelfFeature));
+  // Navigate the child again, the feature should not be enabled.
+  SimulateNavigation(&child, GURL(kOrigin4));
+  EXPECT_FALSE(child->IsFeatureEnabled(kDefaultSelfFeature));
 }
 
 TEST_F(RenderFrameHostFeaturePolicyTest, HeaderAndContainerPolicy) {
@@ -155,10 +173,12 @@
 
   // Set a header policy and container policy. Check that they both take effect.
   RefreshPageAndSetHeaderPolicy(&parent, kDefaultSelfFeature,
-                                {kOrigin1, kOrigin2});
+                                {{std::string(kOrigin1), sample_bool_value},
+                                 {std::string(kOrigin2), sample_bool_value}});
 
   RenderFrameHost* child = AddChildRFH(parent, kOrigin2);
-  SetContainerPolicy(parent, child, kDefaultSelfFeature, {kOrigin3});
+  SetContainerPolicy(parent, child, kDefaultSelfFeature,
+                     {{std::string(kOrigin3), sample_bool_value}});
 
   // The feature should be enabled in kOrigin2, kOrigin3 but not kOrigin4.
   EXPECT_TRUE(child->IsFeatureEnabled(kDefaultSelfFeature));
@@ -170,9 +190,10 @@
   // Change the header policy to turn off the feature. It should be disabled in
   // all children.
   RefreshPageAndSetHeaderPolicy(&parent, kDefaultSelfFeature,
-                                std::vector<std::string>());
+                                std::map<std::string, blink::PolicyValue>());
   child = AddChildRFH(parent, kOrigin2);
-  SetContainerPolicy(parent, child, kDefaultSelfFeature, {kOrigin3});
+  SetContainerPolicy(parent, child, kDefaultSelfFeature,
+                     {{std::string(kOrigin3), sample_bool_value}});
 
   SimulateNavigation(&child, GURL(kOrigin2));
   EXPECT_FALSE(child->IsFeatureEnabled(kDefaultSelfFeature));
@@ -180,4 +201,114 @@
   EXPECT_FALSE(child->IsFeatureEnabled(kDefaultSelfFeature));
 }
 
+TEST_F(RenderFrameHostFeaturePolicyTest, ParameterizedHeaderPolicy) {
+  RenderFrameHost* parent = GetMainRFH(kOrigin1);
+
+  blink::PolicyValue large_double_value(
+      3.5, blink::mojom::PolicyValueType::kDecDouble);
+  // Enable the feature for the child in the parent frame.
+  RefreshPageAndSetHeaderPolicy(&parent, kParameterizedFeature,
+                                {{std::string(kOrigin1), sample_double_value},
+                                 {std::string(kOrigin2), sample_double_value}});
+
+  // Create the child.
+  RenderFrameHost* child = AddChildRFH(parent, kOrigin2);
+
+  EXPECT_TRUE(
+      parent->IsFeatureEnabled(kParameterizedFeature, sample_double_value));
+  EXPECT_FALSE(
+      parent->IsFeatureEnabled(kParameterizedFeature, large_double_value));
+  EXPECT_TRUE(
+      child->IsFeatureEnabled(kParameterizedFeature, sample_double_value));
+  EXPECT_FALSE(
+      child->IsFeatureEnabled(kParameterizedFeature, large_double_value));
+
+  // Set an empty allowlist and in the child to test that the policies combine
+  // correctly. Child frame should be enabled since the feature default is
+  // enabled for all.
+  RefreshPageAndSetHeaderPolicy(&child, kDefaultSelfFeature,
+                                std::map<std::string, blink::PolicyValue>());
+  EXPECT_TRUE(
+      child->IsFeatureEnabled(kParameterizedFeature, sample_double_value));
+
+  // disable the feature in the child.
+  RefreshPageAndSetHeaderPolicy(&child, kParameterizedFeature,
+                                {{std::string(kOrigin2), min_double_value}});
+  EXPECT_TRUE(child->IsFeatureEnabled(kParameterizedFeature, min_double_value));
+  EXPECT_FALSE(
+      child->IsFeatureEnabled(kParameterizedFeature, sample_double_value));
+
+  // Navigate the child. Check that the feature is disabled.
+  SimulateNavigation(&child, GURL(kOrigin3));
+  EXPECT_FALSE(
+      child->IsFeatureEnabled(kParameterizedFeature, sample_double_value));
+}
+
+TEST_F(RenderFrameHostFeaturePolicyTest, ParameterizedContainerPolicy) {
+  RenderFrameHost* parent = GetMainRFH(kOrigin1);
+  RenderFrameHost* child = AddChildRFH(parent, kOrigin2);
+
+  // Set a container policy on origin 3 to give it the feature. It should not
+  // be enabled because container policy will only take effect after navigation.
+  SetContainerPolicy(parent, child, kParameterizedFeature,
+                     {{std::string(kOrigin2), sample_double_value},
+                      {std::string(kOrigin3), sample_double_value}});
+  blink::PolicyValue large_double_value(
+      3.0, blink::mojom::PolicyValueType::kDecDouble);
+  EXPECT_TRUE(
+      child->IsFeatureEnabled(kParameterizedFeature, large_double_value));
+
+  // Navigate the child so that the container policy takes effect.
+  SimulateNavigation(&child, GURL(kOrigin3));
+  EXPECT_FALSE(
+      child->IsFeatureEnabled(kParameterizedFeature, large_double_value));
+  EXPECT_TRUE(
+      child->IsFeatureEnabled(kParameterizedFeature, sample_double_value));
+
+  // Navigate the child again, the feature should be disabled by container
+  // policy.
+  SimulateNavigation(&child, GURL(kOrigin4));
+  EXPECT_FALSE(
+      child->IsFeatureEnabled(kParameterizedFeature, sample_double_value));
+}
+
+TEST_F(RenderFrameHostFeaturePolicyTest,
+       ParameterizedHeaderAndContainerPolicy) {
+  RenderFrameHost* parent = GetMainRFH(kOrigin1);
+
+  // Set a header policy and container policy. Check that they both take effect.
+  RefreshPageAndSetHeaderPolicy(&parent, kParameterizedFeature,
+                                {{std::string(kOrigin1), sample_double_value},
+                                 {std::string(kOrigin2), sample_double_value}});
+
+  RenderFrameHost* child = AddChildRFH(parent, kOrigin2);
+  SetContainerPolicy(parent, child, kParameterizedFeature,
+                     {{std::string(kOrigin3), sample_double_value}});
+
+  // The feature should be enabled in kOrigin2, kOrigin3 but not kOrigin4.
+  EXPECT_TRUE(
+      child->IsFeatureEnabled(kParameterizedFeature, sample_double_value));
+  SimulateNavigation(&child, GURL(kOrigin3));
+  EXPECT_TRUE(
+      child->IsFeatureEnabled(kParameterizedFeature, sample_double_value));
+  SimulateNavigation(&child, GURL(kOrigin4));
+  EXPECT_FALSE(
+      child->IsFeatureEnabled(kParameterizedFeature, sample_double_value));
+
+  // Change the header policy to turn off the feature. It should be disabled in
+  // all children.
+  RefreshPageAndSetHeaderPolicy(&parent, kParameterizedFeature,
+                                std::map<std::string, blink::PolicyValue>());
+  child = AddChildRFH(parent, kOrigin2);
+  SetContainerPolicy(parent, child, kParameterizedFeature,
+                     {{std::string(kOrigin3), sample_double_value}});
+
+  SimulateNavigation(&child, GURL(kOrigin2));
+  EXPECT_FALSE(
+      child->IsFeatureEnabled(kParameterizedFeature, sample_double_value));
+  SimulateNavigation(&child, GURL(kOrigin3));
+  EXPECT_FALSE(
+      child->IsFeatureEnabled(kParameterizedFeature, sample_double_value));
+}
+
 }  // namespace content
diff --git a/content/browser/frame_host/render_frame_host_impl.cc b/content/browser/frame_host/render_frame_host_impl.cc
index 36fe12c..7c05a9eb 100644
--- a/content/browser/frame_host/render_frame_host_impl.cc
+++ b/content/browser/frame_host/render_frame_host_impl.cc
@@ -2847,6 +2847,14 @@
              blink::PolicyValue::CreateMaxPolicyValue(feature_type));
 }
 
+bool RenderFrameHostImpl::IsFeatureEnabled(
+    blink::mojom::FeaturePolicyFeature feature,
+    blink::PolicyValue threshold_value) {
+  return feature_policy_ &&
+         feature_policy_->IsFeatureEnabledForOrigin(
+             feature, GetLastCommittedOrigin(), threshold_value);
+}
+
 void RenderFrameHostImpl::ViewSource() {
   delegate_->ViewSource(this);
 }
@@ -4114,9 +4122,6 @@
   registry_->AddInterface(
       base::BindRepeating(&FileChooserImpl::Create, base::Unretained(this)));
 
-  registry_->AddInterface(base::BindRepeating(&AudioContextManagerImpl::Create,
-                                              base::Unretained(this)));
-
   registry_->AddInterface(base::BindRepeating(&WakeLockServiceImpl::Create,
                                               base::Unretained(this)));
 
@@ -5756,6 +5761,11 @@
                           std::move(request));
 }
 
+void RenderFrameHostImpl::GetAudioContextManager(
+    blink::mojom::AudioContextManagerRequest request) {
+  AudioContextManagerImpl::Create(this, std::move(request));
+}
+
 std::unique_ptr<NavigationRequest>
 RenderFrameHostImpl::TakeNavigationRequestForSameDocumentCommit(
     const FrameHostMsg_DidCommitProvisionalLoad_Params& params) {
diff --git a/content/browser/frame_host/render_frame_host_impl.h b/content/browser/frame_host/render_frame_host_impl.h
index c3c7ef08..19bd2a2 100644
--- a/content/browser/frame_host/render_frame_host_impl.h
+++ b/content/browser/frame_host/render_frame_host_impl.h
@@ -253,6 +253,8 @@
   bool GetSuddenTerminationDisablerState(
       blink::WebSuddenTerminationDisablerType disabler_type) override;
   bool IsFeatureEnabled(blink::mojom::FeaturePolicyFeature feature) override;
+  bool IsFeatureEnabled(blink::mojom::FeaturePolicyFeature feature,
+                        blink::PolicyValue threshold_value) override;
   void ViewSource() override;
   blink::mojom::PauseSubresourceLoadingHandlePtr PauseSubresourceLoading()
       override;
@@ -1308,6 +1310,8 @@
   // blink::mojom::DocumentInterfaceBroker:
   void GetFrameHostTestInterface(
       blink::mojom::FrameHostTestInterfaceRequest request) override;
+  void GetAudioContextManager(
+      blink::mojom::AudioContextManagerRequest request) override;
 
   // Allows tests to disable the swapout event timer to simulate bugs that
   // happen before it fires (to avoid flakiness).
diff --git a/content/browser/frame_host/render_frame_host_manager.cc b/content/browser/frame_host/render_frame_host_manager.cc
index 9d8a7f4c..cc7c7b4f 100644
--- a/content/browser/frame_host/render_frame_host_manager.cc
+++ b/content/browser/frame_host/render_frame_host_manager.cc
@@ -82,8 +82,7 @@
 }
 
 RenderFrameHostManager::~RenderFrameHostManager() {
-  if (speculative_render_frame_host_)
-    UnsetSpeculativeRenderFrameHost();
+  DCHECK(!speculative_render_frame_host_);
 
   // Delete any RenderFrameProxyHosts. It is important to delete those prior to
   // deleting the current RenderFrameHost, since the CrossProcessFrameConnector
diff --git a/content/browser/frame_host/render_frame_host_manager_browsertest.cc b/content/browser/frame_host/render_frame_host_manager_browsertest.cc
index 3e08d82..022a16c 100644
--- a/content/browser/frame_host/render_frame_host_manager_browsertest.cc
+++ b/content/browser/frame_host/render_frame_host_manager_browsertest.cc
@@ -5,6 +5,7 @@
 #include <stddef.h>
 #include <stdint.h>
 
+#include <functional>
 #include <memory>
 #include <set>
 
@@ -5468,7 +5469,7 @@
         FROM_HERE,
         base::BindOnce(&AssertForegroundHelper::AssertForegroundAndRepost,
                        weak_ptr_factory_.GetWeakPtr(),
-                       base::ConstRef(renderer_process), port_provider),
+                       std::cref(renderer_process), port_provider),
         base::TimeDelta::FromMilliseconds(1));
   }
 #else   // defined(OS_MACOSX)
@@ -5479,7 +5480,7 @@
         FROM_HERE,
         base::BindOnce(&AssertForegroundHelper::AssertForegroundAndRepost,
                        weak_ptr_factory_.GetWeakPtr(),
-                       base::ConstRef(renderer_process)),
+                       std::cref(renderer_process)),
         base::TimeDelta::FromMilliseconds(1));
   }
 #endif  // defined(OS_MACOSX)
diff --git a/content/browser/frame_host/render_frame_message_filter.cc b/content/browser/frame_host/render_frame_message_filter.cc
index 4fcaadf7..09095ba 100644
--- a/content/browser/frame_host/render_frame_message_filter.cc
+++ b/content/browser/frame_host/render_frame_message_filter.cc
@@ -41,10 +41,10 @@
 #include "gpu/GLES2/gl2extchromium.h"
 #include "mojo/public/cpp/bindings/callback_helpers.h"
 #include "mojo/public/cpp/system/message_pipe.h"
-#include "net/base/registry_controlled_domains/registry_controlled_domain.h"
 #include "net/cookies/canonical_cookie.h"
 #include "net/cookies/cookie_options.h"
 #include "net/cookies/cookie_store.h"
+#include "net/cookies/cookie_util.h"
 #include "net/traffic_annotation/network_traffic_annotation.h"
 #include "net/url_request/url_request_context.h"
 #include "net/url_request/url_request_context_getter.h"
@@ -653,17 +653,10 @@
   }
 
   net::CookieOptions options;
-  if (net::registry_controlled_domains::SameDomainOrHost(
-          url, site_for_cookies,
-          net::registry_controlled_domains::INCLUDE_PRIVATE_REGISTRIES)) {
-    // TODO(mkwst): This check ought to further distinguish between frames
-    // initiated in a strict or lax same-site context.
-    options.set_same_site_cookie_mode(
-        net::CookieOptions::SameSiteCookieMode::INCLUDE_STRICT_AND_LAX);
-  } else {
-    options.set_same_site_cookie_mode(
-        net::CookieOptions::SameSiteCookieMode::DO_NOT_INCLUDE);
-  }
+  // TODO(https://crbug.com/925311): Wire initiator in here properly.
+  options.set_same_site_cookie_context(net::cookie_util::ComputeSameSiteContext(
+      url, site_for_cookies, base::nullopt));
+
   // If the embedder overrides the cookie store then always use it, even if
   // the network service is enabled, instead of the CookieManager associated
   // this process' StoragePartition.
diff --git a/content/browser/gpu/browser_gpu_channel_host_factory.cc b/content/browser/gpu/browser_gpu_channel_host_factory.cc
index 8bfcff4..d19da42b0 100644
--- a/content/browser/gpu/browser_gpu_channel_host_factory.cc
+++ b/content/browser/gpu/browser_gpu_channel_host_factory.cc
@@ -420,7 +420,7 @@
     int gpu_client_id,
     const base::FilePath& cache_dir) {
   GetShaderCacheFactorySingleton()->SetCacheInfo(gpu_client_id, cache_dir);
-  if (base::FeatureList::IsEnabled(features::kVizDisplayCompositor)) {
+  if (features::IsVizDisplayCompositorEnabled()) {
     GetShaderCacheFactorySingleton()->SetCacheInfo(
         gpu::kInProcessCommandBufferClientId, cache_dir);
   }
diff --git a/content/browser/gpu/compositor_util.cc b/content/browser/gpu/compositor_util.cc
index 931c1b7..2346669 100644
--- a/content/browser/gpu/compositor_util.cc
+++ b/content/browser/gpu/compositor_util.cc
@@ -184,7 +184,7 @@
        SafeGetFeatureStatus(gpu_feature_info,
                             gpu::GPU_FEATURE_TYPE_ANDROID_SURFACE_CONTROL),
 #if defined(OS_ANDROID)
-       !base::FeatureList::IsEnabled(features::kAndroidSurfaceControl),
+       !features::IsAndroidSurfaceControlEnabled(),
 #else
        false,
 #endif
@@ -203,7 +203,7 @@
        "WebGL2 has been disabled via blacklist or the command line.", false,
        true},
       {"viz_display_compositor", gpu::kGpuFeatureStatusEnabled,
-       !base::FeatureList::IsEnabled(features::kVizDisplayCompositor),
+       !features::IsVizDisplayCompositorEnabled(),
        "Viz service display compositor is not enabled by default.", false,
        false},
       {"skia_renderer", gpu::kGpuFeatureStatusEnabled,
diff --git a/content/browser/gpu/gpu_data_manager_impl_private.cc b/content/browser/gpu/gpu_data_manager_impl_private.cc
index 4b6c896..2b4e875 100644
--- a/content/browser/gpu/gpu_data_manager_impl_private.cc
+++ b/content/browser/gpu/gpu_data_manager_impl_private.cc
@@ -371,7 +371,7 @@
   //   Browser process: Windows
   //   GPU process: Linux and Mac
   //   N/A: Android and Chrome OS (GPU access can't be disabled)
-  if (base::FeatureList::IsEnabled(features::kVizDisplayCompositor))
+  if (features::IsVizDisplayCompositorEnabled())
     return true;
 #endif
 
@@ -862,7 +862,7 @@
     return gpu::GpuMode::HARDWARE_ACCELERATED;
   } else if (SwiftShaderAllowed()) {
     return gpu::GpuMode::SWIFTSHADER;
-  } else if (base::FeatureList::IsEnabled(features::kVizDisplayCompositor)) {
+  } else if (features::IsVizDisplayCompositorEnabled()) {
     return gpu::GpuMode::DISPLAY_COMPOSITOR;
   } else {
     return gpu::GpuMode::DISABLED;
@@ -890,7 +890,7 @@
   } else if (SwiftShaderAllowed()) {
     swiftshader_blocked_ = true;
     OnGpuBlocked();
-  } else if (base::FeatureList::IsEnabled(features::kVizDisplayCompositor)) {
+  } else if (features::IsVizDisplayCompositorEnabled()) {
     // The GPU process is frequently crashing with only the display compositor
     // running. This should never happen so something is wrong. Crash the
     // browser process to reset everything.
diff --git a/content/browser/indexed_db/indexed_db_browsertest.cc b/content/browser/indexed_db/indexed_db_browsertest.cc
index 519820c..1306df4 100644
--- a/content/browser/indexed_db/indexed_db_browsertest.cc
+++ b/content/browser/indexed_db/indexed_db_browsertest.cc
@@ -4,6 +4,8 @@
 
 #include <stddef.h>
 #include <stdint.h>
+
+#include <functional>
 #include <utility>
 
 #include "base/bind.h"
@@ -718,9 +720,8 @@
         base::WaitableEvent::ResetPolicy::AUTOMATIC,
         base::WaitableEvent::InitialState::NOT_SIGNALED);
     context->TaskRunner()->PostTask(
-        FROM_HERE,
-        base::BindOnce(&CorruptIndexedDBDatabase, base::ConstRef(context),
-                       origin, &signal_when_finished));
+        FROM_HERE, base::BindOnce(&CorruptIndexedDBDatabase, std::cref(context),
+                                  origin, &signal_when_finished));
     signal_when_finished.Wait();
 
     std::unique_ptr<net::test_server::BasicHttpResponse> http_response(
diff --git a/content/browser/loader/source_stream_to_data_pipe.cc b/content/browser/loader/source_stream_to_data_pipe.cc
index c3923e59..8061f61 100644
--- a/content/browser/loader/source_stream_to_data_pipe.cc
+++ b/content/browser/loader/source_stream_to_data_pipe.cc
@@ -73,6 +73,7 @@
   }
   dest_ = pending_write_->Complete(result);
   pending_write_ = nullptr;
+  transferred_bytes_ += result;
 
   base::SequencedTaskRunnerHandle::Get()->PostTask(
       FROM_HERE, base::BindOnce(&SourceStreamToDataPipe::ReadMore,
diff --git a/content/browser/loader/source_stream_to_data_pipe.h b/content/browser/loader/source_stream_to_data_pipe.h
index 7acd75e..7ba2daf 100644
--- a/content/browser/loader/source_stream_to_data_pipe.h
+++ b/content/browser/loader/source_stream_to_data_pipe.h
@@ -30,6 +30,7 @@
 
   // Start reading the source.
   void Start();
+  int64_t TransferredBytes() const { return transferred_bytes_; }
 
  private:
   void ReadMore();
@@ -42,6 +43,7 @@
   std::unique_ptr<net::SourceStream> source_;
   mojo::ScopedDataPipeProducerHandle dest_;
   base::OnceCallback<void(int)> completion_callback_;
+  int64_t transferred_bytes_ = 0;
 
   scoped_refptr<network::NetToMojoPendingBuffer> pending_write_;
   mojo::SimpleWatcher writable_handle_watcher_;
diff --git a/content/browser/log_console_message.cc b/content/browser/log_console_message.cc
index 328f3a4..83803ae 100644
--- a/content/browser/log_console_message.cc
+++ b/content/browser/log_console_message.cc
@@ -8,6 +8,27 @@
 
 namespace content {
 
+logging::LogSeverity ConsoleMessageLevelToLogSeverity(
+    blink::mojom::ConsoleMessageLevel level) {
+  logging::LogSeverity log_severity = logging::LOG_VERBOSE;
+  switch (level) {
+    case blink::mojom::ConsoleMessageLevel::kVerbose:
+      log_severity = logging::LOG_VERBOSE;
+      break;
+    case blink::mojom::ConsoleMessageLevel::kInfo:
+      log_severity = logging::LOG_INFO;
+      break;
+    case blink::mojom::ConsoleMessageLevel::kWarning:
+      log_severity = logging::LOG_WARNING;
+      break;
+    case blink::mojom::ConsoleMessageLevel::kError:
+      log_severity = logging::LOG_ERROR;
+      break;
+  }
+
+  return log_severity;
+}
+
 void LogConsoleMessage(int32_t level,
                        const base::string16& message,
                        int32_t line_number,
diff --git a/content/browser/log_console_message.h b/content/browser/log_console_message.h
index 55e7e67..cd550d79 100644
--- a/content/browser/log_console_message.h
+++ b/content/browser/log_console_message.h
@@ -5,12 +5,18 @@
 #ifndef CONTENT_BROWSER_LOG_CONSOLE_MESSAGE_H_
 #define CONTENT_BROWSER_LOG_CONSOLE_MESSAGE_H_
 
+#include "base/logging.h"
 #include "base/strings/string16.h"
+#include "third_party/blink/public/mojom/devtools/console_message.mojom.h"
 
 namespace content {
 
+logging::LogSeverity ConsoleMessageLevelToLogSeverity(
+    blink::mojom::ConsoleMessageLevel level);
+
 // Optionally logs a message from the console, depending on the set logging
 // levels and incognito state.
+// TODO(devlin): Update |level| to be a logging::LogSeverity.
 void LogConsoleMessage(int32_t level,
                        const base::string16& message,
                        int32_t line_number,
diff --git a/content/browser/media/android/media_resource_getter_impl.cc b/content/browser/media/android/media_resource_getter_impl.cc
index 0a2f221..b308f34 100644
--- a/content/browser/media/android/media_resource_getter_impl.cc
+++ b/content/browser/media/android/media_resource_getter_impl.cc
@@ -160,8 +160,8 @@
 
   net::CookieOptions options;
   options.set_include_httponly();
-  options.set_same_site_cookie_mode(
-      net::CookieOptions::SameSiteCookieMode::INCLUDE_STRICT_AND_LAX);
+  options.set_same_site_cookie_context(
+      net::CookieOptions::SameSiteCookieContext::SAME_SITE_STRICT);
   options.set_do_not_update_access_time();
   GetCookieServiceForContext(browser_context_)
       ->GetCookieList(
diff --git a/content/browser/media/media_color_browsertest.cc b/content/browser/media/media_color_browsertest.cc
index 94338fe..5ef1055 100644
--- a/content/browser/media/media_color_browsertest.cc
+++ b/content/browser/media/media_color_browsertest.cc
@@ -53,7 +53,14 @@
 
 #if BUILDFLAG(USE_PROPRIETARY_CODECS)
 
-IN_PROC_BROWSER_TEST_F(MediaColorTest, Yuv420pH264) {
+// This test fails on Android: http://crbug.com/938320
+// It also fails on ChromeOS https://crbug.com/938618
+#if defined(OS_ANDROID) || defined(OS_CHROMEOS)
+#define MAYBE_Yuv420pH264 DISABLED_Yuv420pH264
+#else
+#define MAYBE_Yuv420pH264 Yuv420pH264
+#endif
+IN_PROC_BROWSER_TEST_F(MediaColorTest, MAYBE_Yuv420pH264) {
 #if defined(OS_ANDROID)
   // https://crbug.com/907572
   if (base::android::BuildInfo::GetInstance()->sdk_int() <=
@@ -76,7 +83,8 @@
 }
 
 // This fails on ChromeOS: http://crbug.com/647400,
-#if defined(OS_CHROMEOS)
+// This fails on Android: http://crbug.com/938320,
+#if defined(OS_CHROMEOS) || defined(OS_ANDROID)
 #define MAYBE_Yuv420pRec709H264 DISABLED_Yuv420pRec709H264
 #else
 #define MAYBE_Yuv420pRec709H264 Yuv420pRec709H264
diff --git a/content/browser/renderer_host/compositor_impl_android.cc b/content/browser/renderer_host/compositor_impl_android.cc
index 3589208b..72fae86 100644
--- a/content/browser/renderer_host/compositor_impl_android.cc
+++ b/content/browser/renderer_host/compositor_impl_android.cc
@@ -246,8 +246,7 @@
   CompositorDependencies() : frame_sink_id_allocator(kDefaultClientId) {
     DCHECK_CURRENTLY_ON(BrowserThread::UI);
 
-    bool enable_viz =
-        base::FeatureList::IsEnabled(features::kVizDisplayCompositor);
+    bool enable_viz = features::IsVizDisplayCompositorEnabled();
     if (!enable_viz) {
       // The SharedBitmapManager can be null as software compositing is not
       // supported or used on Android.
@@ -602,8 +601,7 @@
       lock_manager_(base::ThreadTaskRunnerHandle::Get()),
       enable_surface_synchronization_(
           features::IsSurfaceSynchronizationEnabled()),
-      enable_viz_(
-          base::FeatureList::IsEnabled(features::kVizDisplayCompositor)),
+      enable_viz_(features::IsVizDisplayCompositorEnabled()),
       weak_factory_(this) {
   DCHECK(client);
 
diff --git a/content/browser/renderer_host/delegated_frame_host.cc b/content/browser/renderer_host/delegated_frame_host.cc
index 379a83bf..36d28d5d 100644
--- a/content/browser/renderer_host/delegated_frame_host.cc
+++ b/content/browser/renderer_host/delegated_frame_host.cc
@@ -51,8 +51,7 @@
                                        bool should_register_frame_sink_id)
     : frame_sink_id_(frame_sink_id),
       client_(client),
-      enable_viz_(
-          base::FeatureList::IsEnabled(features::kVizDisplayCompositor)),
+      enable_viz_(features::IsVizDisplayCompositorEnabled()),
       should_register_frame_sink_id_(should_register_frame_sink_id),
       host_frame_sink_manager_(GetHostFrameSinkManager()),
       frame_evictor_(std::make_unique<viz::FrameEvictor>(this)),
diff --git a/content/browser/renderer_host/input/mouse_latency_browsertest.cc b/content/browser/renderer_host/input/mouse_latency_browsertest.cc
index e53d9ff..9150bffa 100644
--- a/content/browser/renderer_host/input/mouse_latency_browsertest.cc
+++ b/content/browser/renderer_host/input/mouse_latency_browsertest.cc
@@ -390,16 +390,27 @@
 // (crbug.com/723618).
 // http://crbug.com/801629 : Flaky on Linux and Windows, and Mac with
 // --enable-features=VizDisplayCompositor
+#if defined(OS_ANDROID)
+#define MAYBE_CoalescedMouseMovesCorrectlyTerminated \
+  DISABLED_CoalescedMouseMovesCorrectlyTerminated
+#else
+#define MAYBE_CoalescedMouseMovesCorrectlyTerminated \
+  CoalescedMouseMovesCorrectlyTerminated
+#endif
 IN_PROC_BROWSER_TEST_F(MouseLatencyBrowserTest,
-                       DISABLED_CoalescedMouseMovesCorrectlyTerminated) {
+                       MAYBE_CoalescedMouseMovesCorrectlyTerminated) {
   LoadURL();
 
   StartTracing();
   DoSyncCoalescedMoves(gfx::PointF(100, 100), gfx::Vector2dF(150, 150),
                        gfx::Vector2dF(250, 250));
-  static_cast<TracingRenderWidgetHost*>(
-      shell()->web_contents()->GetRenderWidgetHostView()->GetRenderWidgetHost())
-      ->WaitFor("InputLatency::MouseUp");
+  // The following wait is the upper bound for gpu swap completed callback. It
+  // is two frames to account for double buffering.
+  MainThreadFrameObserver observer(RenderWidgetHostImpl::From(
+      shell()->web_contents()->GetRenderViewHost()->GetWidget()));
+  observer.Wait();
+  observer.Wait();
+
   const base::Value& trace_data = StopTracing();
 
   AssertTraceIdsBeginAndEnd(trace_data, "InputLatency::MouseMove");
diff --git a/content/browser/renderer_host/media/media_devices_manager.cc b/content/browser/renderer_host/media/media_devices_manager.cc
index 2190283..5ec40d1 100644
--- a/content/browser/renderer_host/media/media_devices_manager.cc
+++ b/content/browser/renderer_host/media/media_devices_manager.cc
@@ -8,6 +8,7 @@
 #include <stdint.h>
 
 #include <algorithm>
+#include <functional>
 #include <string>
 
 #include "base/bind.h"
@@ -152,7 +153,7 @@
              const blink::WebMediaDeviceInfo& audio_info) {
             return audio_info.label.find(video_label) != std::string::npos;
           },
-          base::ConstRef(video_label));
+          std::cref(video_label));
 
   const bool video_has_usb_model = LabelHasUSBModel(video_info.label);
   std::string video_usb_model = video_has_usb_model
@@ -167,7 +168,7 @@
                              GetUSBModelFromLabel(audio_info.label)
                        : false;
           },
-          video_has_usb_model, base::ConstRef(video_usb_model));
+          video_has_usb_model, std::cref(video_usb_model));
 
   for (auto* callback :
        {&video_label_is_included_in_audio_label, &usb_model_matches}) {
diff --git a/content/browser/renderer_host/media/ref_counted_video_capture_factory.cc b/content/browser/renderer_host/media/ref_counted_video_capture_factory.cc
deleted file mode 100644
index 0f56ad4d..0000000
--- a/content/browser/renderer_host/media/ref_counted_video_capture_factory.cc
+++ /dev/null
@@ -1,35 +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 "content/browser/renderer_host/media/ref_counted_video_capture_factory.h"
-
-namespace content {
-
-RefCountedVideoCaptureFactory::RefCountedVideoCaptureFactory(
-    video_capture::mojom::DeviceFactoryPtr device_factory,
-    video_capture::mojom::DeviceFactoryProviderPtr device_factory_provider,
-    base::OnceClosure destruction_cb)
-    : device_factory_(std::move(device_factory)),
-      device_factory_provider_(std::move(device_factory_provider)),
-      destruction_cb_(std::move(destruction_cb)),
-      weak_ptr_factory_(this) {}
-
-RefCountedVideoCaptureFactory::~RefCountedVideoCaptureFactory() {
-  std::move(destruction_cb_).Run();
-}
-
-base::WeakPtr<RefCountedVideoCaptureFactory>
-RefCountedVideoCaptureFactory::GetWeakPtr() {
-  return weak_ptr_factory_.GetWeakPtr();
-}
-
-void RefCountedVideoCaptureFactory::ShutdownServiceAsap() {
-  device_factory_provider_->ShutdownServiceAsap();
-}
-
-void RefCountedVideoCaptureFactory::ReleaseFactoryForTesting() {
-  device_factory_.reset();
-}
-
-}  // namespace content
diff --git a/content/browser/renderer_host/media/ref_counted_video_capture_factory.h b/content/browser/renderer_host/media/ref_counted_video_capture_factory.h
deleted file mode 100644
index 670e6cb..0000000
--- a/content/browser/renderer_host/media/ref_counted_video_capture_factory.h
+++ /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.
-
-#ifndef CONTENT_BROWSER_RENDERER_HOST_MEDIA_REF_COUNTED_VIDEO_CAPTURE_FACTORY_H_
-#define CONTENT_BROWSER_RENDERER_HOST_MEDIA_REF_COUNTED_VIDEO_CAPTURE_FACTORY_H_
-
-#include "base/memory/ref_counted.h"
-#include "content/common/content_export.h"
-#include "services/video_capture/public/mojom/device_factory.mojom.h"
-#include "services/video_capture/public/mojom/device_factory_provider.mojom.h"
-
-namespace content {
-
-// Enables ref-counted shared ownership of a
-// video_capture::mojom::DeviceFactoryPtr.
-// Since instances of this class do not guarantee that the connection stays open
-// for its entire lifetime, clients must verify that the connection is bound
-// before using it.
-class CONTENT_EXPORT RefCountedVideoCaptureFactory
-    : public base::RefCounted<RefCountedVideoCaptureFactory> {
- public:
-  RefCountedVideoCaptureFactory(
-      video_capture::mojom::DeviceFactoryPtr device_factory,
-      video_capture::mojom::DeviceFactoryProviderPtr device_factory_provider,
-      base::OnceClosure destruction_cb);
-
-  base::WeakPtr<RefCountedVideoCaptureFactory> GetWeakPtr();
-
-  const video_capture::mojom::DeviceFactoryPtr& device_factory() {
-    return device_factory_;
-  }
-
-  void ShutdownServiceAsap();
-
-  void ReleaseFactoryForTesting();
-
- private:
-  friend class base::RefCounted<RefCountedVideoCaptureFactory>;
-  ~RefCountedVideoCaptureFactory();
-
-  video_capture::mojom::DeviceFactoryPtr device_factory_;
-  video_capture::mojom::DeviceFactoryProviderPtr device_factory_provider_;
-  base::OnceClosure destruction_cb_;
-  base::WeakPtrFactory<RefCountedVideoCaptureFactory> weak_ptr_factory_;
-
-  DISALLOW_COPY_AND_ASSIGN(RefCountedVideoCaptureFactory);
-};
-
-}  // namespace content
-
-#endif  // CONTENT_BROWSER_RENDERER_HOST_MEDIA_REF_COUNTED_VIDEO_CAPTURE_FACTORY_H_
diff --git a/content/browser/renderer_host/media/ref_counted_video_source_provider.cc b/content/browser/renderer_host/media/ref_counted_video_source_provider.cc
new file mode 100644
index 0000000..4414c7c
--- /dev/null
+++ b/content/browser/renderer_host/media/ref_counted_video_source_provider.cc
@@ -0,0 +1,35 @@
+// 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 "content/browser/renderer_host/media/ref_counted_video_source_provider.h"
+
+namespace content {
+
+RefCountedVideoSourceProvider::RefCountedVideoSourceProvider(
+    video_capture::mojom::VideoSourceProviderPtr source_provider,
+    video_capture::mojom::DeviceFactoryProviderPtr device_factory_provider,
+    base::OnceClosure destruction_cb)
+    : source_provider_(std::move(source_provider)),
+      device_factory_provider_(std::move(device_factory_provider)),
+      destruction_cb_(std::move(destruction_cb)),
+      weak_ptr_factory_(this) {}
+
+RefCountedVideoSourceProvider::~RefCountedVideoSourceProvider() {
+  std::move(destruction_cb_).Run();
+}
+
+base::WeakPtr<RefCountedVideoSourceProvider>
+RefCountedVideoSourceProvider::GetWeakPtr() {
+  return weak_ptr_factory_.GetWeakPtr();
+}
+
+void RefCountedVideoSourceProvider::ShutdownServiceAsap() {
+  device_factory_provider_->ShutdownServiceAsap();
+}
+
+void RefCountedVideoSourceProvider::ReleaseProviderForTesting() {
+  source_provider_.reset();
+}
+
+}  // namespace content
diff --git a/content/browser/renderer_host/media/ref_counted_video_source_provider.h b/content/browser/renderer_host/media/ref_counted_video_source_provider.h
new file mode 100644
index 0000000..cb6320c
--- /dev/null
+++ b/content/browser/renderer_host/media/ref_counted_video_source_provider.h
@@ -0,0 +1,51 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CONTENT_BROWSER_RENDERER_HOST_MEDIA_REF_COUNTED_VIDEO_SOURCE_PROVIDER_H_
+#define CONTENT_BROWSER_RENDERER_HOST_MEDIA_REF_COUNTED_VIDEO_SOURCE_PROVIDER_H_
+
+#include "base/memory/ref_counted.h"
+#include "content/common/content_export.h"
+#include "services/video_capture/public/mojom/device_factory_provider.mojom.h"
+#include "services/video_capture/public/mojom/video_source_provider.mojom.h"
+
+namespace content {
+
+// Enables ref-counted shared ownership of a
+// video_capture::mojom::DeviceFactoryPtr.
+// Since instances of this class do not guarantee that the connection stays open
+// for its entire lifetime, clients must verify that the connection is bound
+// before using it.
+class CONTENT_EXPORT RefCountedVideoSourceProvider
+    : public base::RefCounted<RefCountedVideoSourceProvider> {
+ public:
+  RefCountedVideoSourceProvider(
+      video_capture::mojom::VideoSourceProviderPtr source_provider,
+      video_capture::mojom::DeviceFactoryProviderPtr device_factory_provider,
+      base::OnceClosure destruction_cb);
+
+  base::WeakPtr<RefCountedVideoSourceProvider> GetWeakPtr();
+
+  const video_capture::mojom::VideoSourceProviderPtr& source_provider() {
+    return source_provider_;
+  }
+
+  void ShutdownServiceAsap();
+  void ReleaseProviderForTesting();
+
+ private:
+  friend class base::RefCounted<RefCountedVideoSourceProvider>;
+  ~RefCountedVideoSourceProvider();
+
+  video_capture::mojom::VideoSourceProviderPtr source_provider_;
+  video_capture::mojom::DeviceFactoryProviderPtr device_factory_provider_;
+  base::OnceClosure destruction_cb_;
+  base::WeakPtrFactory<RefCountedVideoSourceProvider> weak_ptr_factory_;
+
+  DISALLOW_COPY_AND_ASSIGN(RefCountedVideoSourceProvider);
+};
+
+}  // namespace content
+
+#endif  // CONTENT_BROWSER_RENDERER_HOST_MEDIA_REF_COUNTED_VIDEO_SOURCE_PROVIDER_H_
diff --git a/content/browser/renderer_host/media/service_launched_video_capture_device.cc b/content/browser/renderer_host/media/service_launched_video_capture_device.cc
index 947804866..c52a2c9 100644
--- a/content/browser/renderer_host/media/service_launched_video_capture_device.cc
+++ b/content/browser/renderer_host/media/service_launched_video_capture_device.cc
@@ -3,19 +3,29 @@
 // found in the LICENSE file.
 
 #include "content/browser/renderer_host/media/service_launched_video_capture_device.h"
+
 #include "base/bind.h"
+#include "base/bind_helpers.h"
 
 namespace content {
 
 ServiceLaunchedVideoCaptureDevice::ServiceLaunchedVideoCaptureDevice(
-    video_capture::mojom::DevicePtr device,
+    video_capture::mojom::VideoSourcePtr source,
+    video_capture::mojom::PushVideoStreamSubscriptionPtr subscription,
     base::OnceClosure connection_lost_cb)
-    : device_(std::move(device)),
+    : source_(std::move(source)),
+      subscription_(std::move(subscription)),
       connection_lost_cb_(std::move(connection_lost_cb)) {
-  // Unretained |this| is safe, because |this| owns |device_|.
-  device_.set_connection_error_handler(base::BindOnce(
-      &ServiceLaunchedVideoCaptureDevice::OnLostConnectionToDevice,
-      base::Unretained(this)));
+  // Unretained |this| is safe, because |this| owns |source_|.
+  source_.set_connection_error_handler(
+      base::BindOnce(&ServiceLaunchedVideoCaptureDevice::
+                         OnLostConnectionToSourceOrSubscription,
+                     base::Unretained(this)));
+  // Unretained |this| is safe, because |this| owns |subscription_|.
+  subscription_.set_connection_error_handler(
+      base::BindOnce(&ServiceLaunchedVideoCaptureDevice::
+                         OnLostConnectionToSourceOrSubscription,
+                     base::Unretained(this)));
 }
 
 ServiceLaunchedVideoCaptureDevice::~ServiceLaunchedVideoCaptureDevice() {
@@ -25,7 +35,7 @@
 void ServiceLaunchedVideoCaptureDevice::GetPhotoState(
     media::VideoCaptureDevice::GetPhotoStateCallback callback) const {
   DCHECK(sequence_checker_.CalledOnValidSequence());
-  device_->GetPhotoState(base::BindOnce(
+  subscription_->GetPhotoState(base::BindOnce(
       &ServiceLaunchedVideoCaptureDevice::OnGetPhotoStateResponse,
       base::Unretained(this), std::move(callback)));
 }
@@ -34,7 +44,7 @@
     media::mojom::PhotoSettingsPtr settings,
     media::VideoCaptureDevice::SetPhotoOptionsCallback callback) {
   DCHECK(sequence_checker_.CalledOnValidSequence());
-  device_->SetPhotoOptions(
+  subscription_->SetPhotoOptions(
       std::move(settings),
       base::BindOnce(
           &ServiceLaunchedVideoCaptureDevice::OnSetPhotoOptionsResponse,
@@ -47,23 +57,25 @@
   TRACE_EVENT_INSTANT0(TRACE_DISABLED_BY_DEFAULT("video_and_image_capture"),
                        "ServiceLaunchedVideoCaptureDevice::TakePhoto",
                        TRACE_EVENT_SCOPE_PROCESS);
-  device_->TakePhoto(
+  subscription_->TakePhoto(
       base::BindOnce(&ServiceLaunchedVideoCaptureDevice::OnTakePhotoResponse,
                      base::Unretained(this), std::move(callback)));
 }
 
 void ServiceLaunchedVideoCaptureDevice::MaybeSuspendDevice() {
   DCHECK(sequence_checker_.CalledOnValidSequence());
-  device_->MaybeSuspend();
+  subscription_->Suspend(base::DoNothing());
 }
 
 void ServiceLaunchedVideoCaptureDevice::ResumeDevice() {
   DCHECK(sequence_checker_.CalledOnValidSequence());
-  device_->Resume();
+  subscription_->Resume();
 }
 
 void ServiceLaunchedVideoCaptureDevice::RequestRefreshFrame() {
-  // Ignore this call.
+  DCHECK(sequence_checker_.CalledOnValidSequence());
+  // Nothing to do here. The video capture service does not support refresh
+  // frames.
 }
 
 void ServiceLaunchedVideoCaptureDevice::SetDesktopCaptureWindowIdAsync(
@@ -78,11 +90,16 @@
 void ServiceLaunchedVideoCaptureDevice::OnUtilizationReport(
     int frame_feedback_id,
     double utilization) {
-  // Ignore this call.
+  DCHECK(sequence_checker_.CalledOnValidSequence());
+  // Nothing to do here. The video capture service does not support utilization
+  // reporting.
 }
 
-void ServiceLaunchedVideoCaptureDevice::OnLostConnectionToDevice() {
+void ServiceLaunchedVideoCaptureDevice::
+    OnLostConnectionToSourceOrSubscription() {
   DCHECK(sequence_checker_.CalledOnValidSequence());
+  source_.reset();
+  subscription_.reset();
   base::ResetAndReturn(&connection_lost_cb_).Run();
 }
 
diff --git a/content/browser/renderer_host/media/service_launched_video_capture_device.h b/content/browser/renderer_host/media/service_launched_video_capture_device.h
index 86764d76..b36917f 100644
--- a/content/browser/renderer_host/media/service_launched_video_capture_device.h
+++ b/content/browser/renderer_host/media/service_launched_video_capture_device.h
@@ -7,7 +7,7 @@
 
 #include "content/browser/renderer_host/media/video_capture_provider.h"
 #include "content/public/browser/video_capture_device_launcher.h"
-#include "services/video_capture/public/mojom/device.mojom.h"
+#include "services/video_capture/public/mojom/video_source.mojom.h"
 
 namespace content {
 
@@ -15,8 +15,10 @@
 // service.
 class ServiceLaunchedVideoCaptureDevice : public LaunchedVideoCaptureDevice {
  public:
-  ServiceLaunchedVideoCaptureDevice(video_capture::mojom::DevicePtr device,
-                                    base::OnceClosure connection_lost_cb);
+  ServiceLaunchedVideoCaptureDevice(
+      video_capture::mojom::VideoSourcePtr source,
+      video_capture::mojom::PushVideoStreamSubscriptionPtr subscription,
+      base::OnceClosure connection_lost_cb);
   ~ServiceLaunchedVideoCaptureDevice() override;
 
   // LaunchedVideoCaptureDevice implementation.
@@ -37,7 +39,7 @@
   void OnUtilizationReport(int frame_feedback_id, double utilization) override;
 
  private:
-  void OnLostConnectionToDevice();
+  void OnLostConnectionToSourceOrSubscription();
   void OnGetPhotoStateResponse(
       media::VideoCaptureDevice::GetPhotoStateCallback callback,
       media::mojom::PhotoStatePtr capabilities) const;
@@ -48,7 +50,8 @@
       media::VideoCaptureDevice::TakePhotoCallback callback,
       media::mojom::BlobPtr blob);
 
-  video_capture::mojom::DevicePtr device_;
+  video_capture::mojom::VideoSourcePtr source_;
+  video_capture::mojom::PushVideoStreamSubscriptionPtr subscription_;
   base::OnceClosure connection_lost_cb_;
   base::SequenceChecker sequence_checker_;
 };
diff --git a/content/browser/renderer_host/media/service_video_capture_device_launcher.cc b/content/browser/renderer_host/media/service_video_capture_device_launcher.cc
index a37f0b2..87aca61 100644
--- a/content/browser/renderer_host/media/service_video_capture_device_launcher.cc
+++ b/content/browser/renderer_host/media/service_video_capture_device_launcher.cc
@@ -20,37 +20,26 @@
 namespace {
 
 void ConcludeLaunchDeviceWithSuccess(
-    const media::VideoCaptureParams& params,
-    video_capture::mojom::DevicePtr device,
-    base::WeakPtr<media::VideoFrameReceiver> receiver,
+    video_capture::mojom::VideoSourcePtr source,
+    video_capture::mojom::PushVideoStreamSubscriptionPtr subscription,
     base::OnceClosure connection_lost_cb,
     VideoCaptureDeviceLauncher::Callbacks* callbacks,
     base::OnceClosure done_cb) {
-  auto receiver_adapter =
-      std::make_unique<video_capture::ReceiverMediaToMojoAdapter>(
-          std::make_unique<media::VideoFrameReceiverOnTaskRunner>(
-              std::move(receiver), base::CreateSingleThreadTaskRunnerWithTraits(
-                                       {BrowserThread::IO})));
-  video_capture::mojom::ReceiverPtr receiver_proxy;
-  mojo::MakeStrongBinding<video_capture::mojom::Receiver>(
-      std::move(receiver_adapter), mojo::MakeRequest(&receiver_proxy));
-  media::VideoCaptureParams new_params = params;
-  new_params.power_line_frequency =
-      media::VideoCaptureDevice::GetPowerLineFrequency(params);
-  device->Start(new_params, std::move(receiver_proxy));
+  subscription->Activate();
   callbacks->OnDeviceLaunched(
       std::make_unique<ServiceLaunchedVideoCaptureDevice>(
-          std::move(device), std::move(connection_lost_cb)));
+          std::move(source), std::move(subscription),
+          std::move(connection_lost_cb)));
   base::ResetAndReturn(&done_cb).Run();
 }
 
 void ConcludeLaunchDeviceWithFailure(
     bool abort_requested,
     media::VideoCaptureError error,
-    scoped_refptr<RefCountedVideoCaptureFactory> device_factory,
+    scoped_refptr<RefCountedVideoSourceProvider> service_connection,
     VideoCaptureDeviceLauncher::Callbacks* callbacks,
     base::OnceClosure done_cb) {
-  device_factory.reset();
+  service_connection.reset();
   if (abort_requested)
     callbacks->OnDeviceLaunchAborted();
   else
@@ -61,8 +50,8 @@
 }  // anonymous namespace
 
 ServiceVideoCaptureDeviceLauncher::ServiceVideoCaptureDeviceLauncher(
-    ConnectToDeviceFactoryCB connect_to_device_factory_cb)
-    : connect_to_device_factory_cb_(std::move(connect_to_device_factory_cb)),
+    ConnectToDeviceFactoryCB connect_to_source_provider_cb)
+    : connect_to_source_provider_cb_(std::move(connect_to_source_provider_cb)),
       state_(State::READY_TO_LAUNCH),
       callbacks_(nullptr) {}
 
@@ -88,16 +77,14 @@
     return;
   }
 
-  connect_to_device_factory_cb_.Run(&device_factory_);
-  if (!device_factory_->device_factory().is_bound()) {
-    // This can happen when the ServiceVideoCaptureProvider owning
-    // |device_factory_| loses connection to the service process and resets
-    // |device_factory_|.
+  connect_to_source_provider_cb_.Run(&service_connection_);
+  if (!service_connection_->source_provider().is_bound()) {
+    // This can happen when the connection to the service was lost.
     ConcludeLaunchDeviceWithFailure(
         false,
         media::VideoCaptureError::
             kServiceDeviceLauncherLostConnectionToDeviceFactoryDuringDeviceStart,
-        std::move(device_factory_), callbacks, std::move(done_cb));
+        std::move(service_connection_), callbacks, std::move(done_cb));
     return;
   }
 
@@ -105,32 +92,62 @@
     std::ostringstream string_stream;
     string_stream
         << "ServiceVideoCaptureDeviceLauncher::LaunchDeviceAsync: Asking "
-           "video capture service to create device for device_id = "
+           "video capture service to create source for device_id = "
         << device_id;
     receiver->OnLog(string_stream.str());
   }
 
-  video_capture::mojom::DevicePtr device;
-  auto device_request = mojo::MakeRequest(&device);
   // Ownership of |done_cb| is moved to |this|. It is not sufficient to attach
-  // it to the callback passed to |device_factory_->CreateDevice()|, because
-  // |device_factory_| may get torn down before the callback is invoked.
+  // it to the callback passed to CreatePushSubscription(), because the
+  // connection to the service may get torn down before |callbacks| are
+  // invoked.
   done_cb_ = std::move(done_cb);
   callbacks_ = callbacks;
+  video_capture::mojom::VideoSourcePtr source;
+  service_connection_->source_provider()->GetVideoSource(
+      device_id, mojo::MakeRequest(&source));
+
+  auto receiver_adapter =
+      std::make_unique<video_capture::ReceiverMediaToMojoAdapter>(
+          std::make_unique<media::VideoFrameReceiverOnTaskRunner>(
+              std::move(receiver), base::CreateSingleThreadTaskRunnerWithTraits(
+                                       {BrowserThread::IO})));
+  video_capture::mojom::ReceiverPtr receiver_proxy;
+  mojo::MakeStrongBinding<video_capture::mojom::Receiver>(
+      std::move(receiver_adapter), mojo::MakeRequest(&receiver_proxy));
+
+  video_capture::mojom::PushVideoStreamSubscriptionPtr subscription;
+  // Create message pipe so that we can subsequently call
+  // subscription.set_connection_error_handler().
+  auto subscription_request = mojo::MakeRequest(&subscription);
   // Use of Unretained(this) is safe, because |done_cb_| guarantees that |this|
   // stays alive.
-  device.set_connection_error_handler(
+  subscription.set_connection_error_handler(
       base::BindOnce(&ServiceVideoCaptureDeviceLauncher::
                          OnConnectionLostWhileWaitingForCallback,
                      base::Unretained(this)));
-  device_factory_->device_factory()->CreateDevice(
-      device_id, std::move(device_request),
+
+  // TODO(crbug.com/925083)
+  media::VideoCaptureParams new_params = params;
+  new_params.power_line_frequency =
+      media::VideoCaptureDevice::GetPowerLineFrequency(params);
+
+  // Note that we set |force_reopen_with_new_settings| to true in order
+  // to avoid the situation that a requests to open (or reopen) a device
+  // that has just been closed with different settings ends up getting the old
+  // settings, because from the perspective of the service, the device was still
+  // in use. In order to be able to set |force_reopen_with_new_settings|, we
+  // have to refactor code here and upstream to wait for a callback from the
+  // service indicating that the device closing is complete.
+  source->CreatePushSubscription(
+      std::move(receiver_proxy), new_params,
+      true /*force_reopen_with_new_settings*/, std::move(subscription_request),
       base::BindOnce(
           // Use of Unretained |this| is safe, because |done_cb_| guarantees
           // that |this| stays alive.
-          &ServiceVideoCaptureDeviceLauncher::OnCreateDeviceCallback,
-          base::Unretained(this), params, std::move(device),
-          std::move(receiver), std::move(connection_lost_cb)));
+          &ServiceVideoCaptureDeviceLauncher::OnCreatePushSubscriptionCallback,
+          base::Unretained(this), std::move(source), std::move(subscription),
+          std::move(connection_lost_cb)));
   state_ = State::DEVICE_START_IN_PROGRESS;
 }
 
@@ -140,40 +157,43 @@
     state_ = State::DEVICE_START_ABORTING;
 }
 
-void ServiceVideoCaptureDeviceLauncher::OnCreateDeviceCallback(
-    const media::VideoCaptureParams& params,
-    video_capture::mojom::DevicePtr device,
-    base::WeakPtr<media::VideoFrameReceiver> receiver,
+void ServiceVideoCaptureDeviceLauncher::OnCreatePushSubscriptionCallback(
+    video_capture::mojom::VideoSourcePtr source,
+    video_capture::mojom::PushVideoStreamSubscriptionPtr subscription,
     base::OnceClosure connection_lost_cb,
-    video_capture::mojom::DeviceAccessResultCode result_code) {
+    video_capture::mojom::CreatePushSubscriptionResultCode result_code,
+    const media::VideoCaptureParams& params) {
   DCHECK(sequence_checker_.CalledOnValidSequence());
   DCHECK(callbacks_);
   DCHECK(done_cb_);
-  device.set_connection_error_handler(base::DoNothing());
+  subscription.set_connection_error_handler(base::DoNothing());
   const bool abort_requested = (state_ == State::DEVICE_START_ABORTING);
   state_ = State::READY_TO_LAUNCH;
   Callbacks* callbacks = callbacks_;
   callbacks_ = nullptr;
   switch (result_code) {
-    case video_capture::mojom::DeviceAccessResultCode::SUCCESS:
+    case video_capture::mojom::CreatePushSubscriptionResultCode::
+        kCreatedWithRequestedSettings:  // Fall through.
+    case video_capture::mojom::CreatePushSubscriptionResultCode::
+        kCreatedWithDifferentSettings:
       if (abort_requested) {
-        device.reset();
-        device_factory_.reset();
+        subscription.reset();
+        source.reset();
+        service_connection_.reset();
         callbacks->OnDeviceLaunchAborted();
         base::ResetAndReturn(&done_cb_).Run();
         return;
       }
       ConcludeLaunchDeviceWithSuccess(
-          params, std::move(device), std::move(receiver),
+          std::move(source), std::move(subscription),
           std::move(connection_lost_cb), callbacks, std::move(done_cb_));
       return;
-    case video_capture::mojom::DeviceAccessResultCode::ERROR_DEVICE_NOT_FOUND:
-    case video_capture::mojom::DeviceAccessResultCode::NOT_INITIALIZED:
+    case video_capture::mojom::CreatePushSubscriptionResultCode::kFailed:
       ConcludeLaunchDeviceWithFailure(
           abort_requested,
           media::VideoCaptureError::
               kServiceDeviceLauncherServiceRespondedWithDeviceNotFound,
-          std::move(device_factory_), callbacks, std::move(done_cb_));
+          std::move(service_connection_), callbacks, std::move(done_cb_));
       return;
   }
 }
@@ -190,7 +210,7 @@
       abort_requested,
       media::VideoCaptureError::
           kServiceDeviceLauncherConnectionLostWhileWaitingForCallback,
-      std::move(device_factory_), callbacks, std::move(done_cb_));
+      std::move(service_connection_), callbacks, std::move(done_cb_));
 }
 
 }  // namespace content
diff --git a/content/browser/renderer_host/media/service_video_capture_device_launcher.h b/content/browser/renderer_host/media/service_video_capture_device_launcher.h
index afe0b95..2936448 100644
--- a/content/browser/renderer_host/media/service_video_capture_device_launcher.h
+++ b/content/browser/renderer_host/media/service_video_capture_device_launcher.h
@@ -5,7 +5,7 @@
 #ifndef CONTENT_BROWSER_RENDERER_HOST_MEDIA_SERVICE_VIDEO_CAPTURE_DEVICE_LAUNCHER_H_
 #define CONTENT_BROWSER_RENDERER_HOST_MEDIA_SERVICE_VIDEO_CAPTURE_DEVICE_LAUNCHER_H_
 
-#include "content/browser/renderer_host/media/ref_counted_video_capture_factory.h"
+#include "content/browser/renderer_host/media/ref_counted_video_source_provider.h"
 #include "content/browser/renderer_host/media/video_capture_provider.h"
 #include "content/public/browser/video_capture_device_launcher.h"
 #include "services/video_capture/public/mojom/device_factory.mojom.h"
@@ -18,12 +18,12 @@
 class CONTENT_EXPORT ServiceVideoCaptureDeviceLauncher
     : public VideoCaptureDeviceLauncher {
  public:
-  // Receives an instance via output parameter |factory|.
+  // Receives an instance via output parameter |out_provider|.
   using ConnectToDeviceFactoryCB = base::RepeatingCallback<void(
-      scoped_refptr<RefCountedVideoCaptureFactory>*)>;
+      scoped_refptr<RefCountedVideoSourceProvider>* out_provider)>;
 
   explicit ServiceVideoCaptureDeviceLauncher(
-      ConnectToDeviceFactoryCB connect_to_device_factory_cb);
+      ConnectToDeviceFactoryCB connect_to_source_provider_cb);
   ~ServiceVideoCaptureDeviceLauncher() override;
 
   // VideoCaptureDeviceLauncher implementation.
@@ -45,17 +45,17 @@
     DEVICE_START_ABORTING
   };
 
-  void OnCreateDeviceCallback(
-      const media::VideoCaptureParams& params,
-      video_capture::mojom::DevicePtr device,
-      base::WeakPtr<media::VideoFrameReceiver> receiver,
+  void OnCreatePushSubscriptionCallback(
+      video_capture::mojom::VideoSourcePtr source,
+      video_capture::mojom::PushVideoStreamSubscriptionPtr subscription,
       base::OnceClosure connection_lost_cb,
-      video_capture::mojom::DeviceAccessResultCode result_code);
+      video_capture::mojom::CreatePushSubscriptionResultCode result_code,
+      const media::VideoCaptureParams& params);
 
   void OnConnectionLostWhileWaitingForCallback();
 
-  ConnectToDeviceFactoryCB connect_to_device_factory_cb_;
-  scoped_refptr<RefCountedVideoCaptureFactory> device_factory_;
+  ConnectToDeviceFactoryCB connect_to_source_provider_cb_;
+  scoped_refptr<RefCountedVideoSourceProvider> service_connection_;
   State state_;
   base::SequenceChecker sequence_checker_;
   base::OnceClosure done_cb_;
diff --git a/content/browser/renderer_host/media/service_video_capture_device_launcher_unittest.cc b/content/browser/renderer_host/media/service_video_capture_device_launcher_unittest.cc
index d7eaf68..e7d641b6 100644
--- a/content/browser/renderer_host/media/service_video_capture_device_launcher_unittest.cc
+++ b/content/browser/renderer_host/media/service_video_capture_device_launcher_unittest.cc
@@ -9,17 +9,20 @@
 #include "base/run_loop.h"
 #include "base/test/mock_callback.h"
 #include "base/threading/thread.h"
-#include "content/browser/renderer_host/media/ref_counted_video_capture_factory.h"
+#include "content/browser/renderer_host/media/ref_counted_video_source_provider.h"
 #include "content/browser/renderer_host/media/service_launched_video_capture_device.h"
 #include "content/public/test/test_browser_thread_bundle.h"
 #include "mojo/public/cpp/bindings/binding.h"
-#include "services/video_capture/public/cpp/mock_device_factory.h"
+#include "mojo/public/cpp/bindings/binding_set.h"
+#include "services/video_capture/public/cpp/mock_push_subscription.h"
+#include "services/video_capture/public/cpp/mock_video_source.h"
+#include "services/video_capture/public/cpp/mock_video_source_provider.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
+using testing::_;
 using testing::Invoke;
 using testing::InvokeWithoutArgs;
-using testing::_;
 
 namespace content {
 
@@ -46,46 +49,87 @@
   ServiceVideoCaptureDeviceLauncherTest() {}
   ~ServiceVideoCaptureDeviceLauncherTest() override {}
 
+  void CloseSourceBinding() { source_binding_.reset(); }
+
+  void CloseSubscriptionBindings() {
+    subscription_bindings_.CloseAllBindings();
+  }
+
  protected:
   void SetUp() override {
-    video_capture::mojom::DeviceFactoryPtr device_factory;
-    factory_binding_ =
-        std::make_unique<mojo::Binding<video_capture::mojom::DeviceFactory>>(
-            &mock_device_factory_, mojo::MakeRequest(&device_factory));
-    factory_delegate_ = base::MakeRefCounted<RefCountedVideoCaptureFactory>(
-        std::move(device_factory),
+    source_provider_binding_ = std::make_unique<
+        mojo::Binding<video_capture::mojom::VideoSourceProvider>>(
+        &mock_source_provider_, mojo::MakeRequest(&source_provider_));
+    service_connection_ = base::MakeRefCounted<RefCountedVideoSourceProvider>(
+        std::move(source_provider_),
         video_capture::mojom::DeviceFactoryProviderPtr(),
         release_connection_cb_.Get());
 
     launcher_ = std::make_unique<ServiceVideoCaptureDeviceLauncher>(
         connect_to_device_factory_cb_.Get());
-    launcher_has_connected_to_device_factory_ = false;
-    launcher_has_released_device_factory_ = false;
+    launcher_has_connected_to_source_provider_ = false;
+    launcher_has_released_source_provider_ = false;
 
     ON_CALL(connect_to_device_factory_cb_, Run(_))
         .WillByDefault(Invoke(
-            [this](scoped_refptr<RefCountedVideoCaptureFactory>* out_factory) {
-              launcher_has_connected_to_device_factory_ = true;
-              *out_factory = factory_delegate_;
+            [this](scoped_refptr<RefCountedVideoSourceProvider>* out_provider) {
+              launcher_has_connected_to_source_provider_ = true;
+              *out_provider = service_connection_;
             }));
 
     ON_CALL(release_connection_cb_, Run())
         .WillByDefault(InvokeWithoutArgs([this]() {
-          launcher_has_released_device_factory_ = true;
+          launcher_has_released_source_provider_ = true;
           wait_for_release_connection_cb_.Quit();
         }));
+
+    ON_CALL(mock_source_provider_, DoGetVideoSource(kStubDeviceId, _))
+        .WillByDefault(Invoke(
+            [this](const std::string& device_id,
+                   video_capture::mojom::VideoSourceRequest* source_request) {
+              source_binding_ = std::make_unique<
+                  mojo::Binding<video_capture::mojom::VideoSource>>(
+                  &mock_source_, std::move(*source_request));
+            }));
+
+    ON_CALL(mock_source_, DoCreatePushSubscription(_, _, _, _, _))
+        .WillByDefault(Invoke(
+            [this](video_capture::mojom::ReceiverPtr& subscriber,
+                   const media::VideoCaptureParams& requested_settings,
+                   bool force_reopen_with_new_settings,
+                   video_capture::mojom::PushVideoStreamSubscriptionRequest&
+                       subscription,
+                   video_capture::mojom::VideoSource::
+                       CreatePushSubscriptionCallback& callback) {
+              subscription_bindings_.AddBinding(&mock_subscription_,
+                                                std::move(subscription));
+              std::move(callback).Run(
+                  video_capture::mojom::CreatePushSubscriptionResultCode::
+                      kCreatedWithRequestedSettings,
+                  requested_settings);
+            }));
   }
 
   void TearDown() override {}
 
   void RunLaunchingDeviceIsAbortedTest(
-      video_capture::mojom::DeviceAccessResultCode service_result_code);
+      video_capture::mojom::CreatePushSubscriptionResultCode
+          service_result_code);
+  void RunConnectionLostAfterSuccessfulStartTest(
+      base::OnceClosure close_connection_cb);
 
   TestBrowserThreadBundle thread_bundle_;
-  video_capture::MockDeviceFactory mock_device_factory_;
   MockVideoCaptureDeviceLauncherCallbacks mock_callbacks_;
-  std::unique_ptr<mojo::Binding<video_capture::mojom::DeviceFactory>>
-      factory_binding_;
+  video_capture::mojom::VideoSourceProviderPtr source_provider_;
+  video_capture::MockVideoSourceProvider mock_source_provider_;
+  std::unique_ptr<mojo::Binding<video_capture::mojom::VideoSourceProvider>>
+      source_provider_binding_;
+  video_capture::MockVideoSource mock_source_;
+  std::unique_ptr<mojo::Binding<video_capture::mojom::VideoSource>>
+      source_binding_;
+  video_capture::MockPushSubcription mock_subscription_;
+  mojo::BindingSet<video_capture::mojom::PushVideoStreamSubscription>
+      subscription_bindings_;
   std::unique_ptr<ServiceVideoCaptureDeviceLauncher> launcher_;
   base::MockCallback<base::OnceClosure> connection_lost_cb_;
   base::MockCallback<base::OnceClosure> done_cb_;
@@ -93,11 +137,11 @@
       ServiceVideoCaptureDeviceLauncher::ConnectToDeviceFactoryCB>
       connect_to_device_factory_cb_;
   base::MockCallback<base::OnceClosure> release_connection_cb_;
-  // |factory_delegate_| needs to go below |release_connection_cb_|, because its
-  // destructor will call |release_connection_cb_|.
-  scoped_refptr<RefCountedVideoCaptureFactory> factory_delegate_;
-  bool launcher_has_connected_to_device_factory_;
-  bool launcher_has_released_device_factory_;
+  // |service_connection_| needs to go below |release_connection_cb_|, because
+  // its destructor will call |release_connection_cb_|.
+  scoped_refptr<RefCountedVideoSourceProvider> service_connection_;
+  bool launcher_has_connected_to_source_provider_;
+  bool launcher_has_released_source_provider_;
   base::RunLoop wait_for_release_connection_cb_;
 
  private:
@@ -105,26 +149,6 @@
 };
 
 TEST_F(ServiceVideoCaptureDeviceLauncherTest, LaunchingDeviceSucceeds) {
-  EXPECT_CALL(mock_device_factory_, DoCreateDevice(kStubDeviceId, _, _))
-      .WillOnce(Invoke([](const std::string& device_id,
-                          video_capture::mojom::DeviceRequest* device_request,
-                          video_capture::mojom::DeviceFactory::
-                              CreateDeviceCallback& callback) {
-        // Note: We must keep |device_request| alive at least until we have
-        // sent out the callback. Otherwise, |launcher_| may interpret this
-        // as the connection having been lost before receiving the callback.
-        base::ThreadTaskRunnerHandle::Get()->PostTask(
-            FROM_HERE,
-            base::BindOnce(
-                [](video_capture::mojom::DeviceRequest device_request,
-                   video_capture::mojom::DeviceFactory::CreateDeviceCallback
-                       callback) {
-                  std::move(callback).Run(
-                      video_capture::mojom::DeviceAccessResultCode::SUCCESS);
-                },
-                std::move(*device_request), std::move(callback)));
-      }));
-
   EXPECT_CALL(mock_callbacks_, DoOnDeviceLaunched(_)).Times(1);
   EXPECT_CALL(mock_callbacks_, OnDeviceLaunchAborted()).Times(0);
   EXPECT_CALL(mock_callbacks_, OnDeviceLaunchFailed(_)).Times(0);
@@ -142,55 +166,64 @@
   wait_for_done_cb.Run();
 
   launcher_.reset();
-  factory_delegate_.reset();
+  service_connection_.reset();
   wait_for_release_connection_cb_.Run();
-  EXPECT_TRUE(launcher_has_connected_to_device_factory_);
-  EXPECT_TRUE(launcher_has_released_device_factory_);
+  EXPECT_TRUE(launcher_has_connected_to_source_provider_);
+  EXPECT_TRUE(launcher_has_released_source_provider_);
 }
 
 TEST_F(ServiceVideoCaptureDeviceLauncherTest,
        LaunchingDeviceIsAbortedBeforeServiceRespondsWithSuccess) {
   RunLaunchingDeviceIsAbortedTest(
-      video_capture::mojom::DeviceAccessResultCode::SUCCESS);
+      video_capture::mojom::CreatePushSubscriptionResultCode::
+          kCreatedWithRequestedSettings);
 }
 
 TEST_F(ServiceVideoCaptureDeviceLauncherTest,
        LaunchingDeviceIsAbortedBeforeServiceRespondsWithNotFound) {
   RunLaunchingDeviceIsAbortedTest(
-      video_capture::mojom::DeviceAccessResultCode::ERROR_DEVICE_NOT_FOUND);
+      video_capture::mojom::CreatePushSubscriptionResultCode::
+          kCreatedWithDifferentSettings);
 }
 
 TEST_F(ServiceVideoCaptureDeviceLauncherTest,
        LaunchingDeviceIsAbortedBeforeServiceRespondsWithNotInitialized) {
   RunLaunchingDeviceIsAbortedTest(
-      video_capture::mojom::DeviceAccessResultCode::NOT_INITIALIZED);
+      video_capture::mojom::CreatePushSubscriptionResultCode::kFailed);
 }
 
 void ServiceVideoCaptureDeviceLauncherTest::RunLaunchingDeviceIsAbortedTest(
-    video_capture::mojom::DeviceAccessResultCode service_result_code) {
+    video_capture::mojom::CreatePushSubscriptionResultCode
+        service_result_code) {
   base::RunLoop step_1_run_loop;
   base::RunLoop step_2_run_loop;
 
-  base::OnceClosure create_device_success_answer_cb;
-  EXPECT_CALL(mock_device_factory_, DoCreateDevice(kStubDeviceId, _, _))
-      .WillOnce(
-          Invoke([&create_device_success_answer_cb, &step_1_run_loop,
-                  service_result_code](
-                     const std::string& device_id,
-                     video_capture::mojom::DeviceRequest* device_request,
-                     video_capture::mojom::DeviceFactory::CreateDeviceCallback&
-                         callback) {
+  base::OnceClosure create_push_subscription_success_answer_cb;
+  EXPECT_CALL(mock_source_, DoCreatePushSubscription(_, _, _, _, _))
+      .WillOnce(Invoke(
+          [&create_push_subscription_success_answer_cb, &step_1_run_loop,
+           service_result_code](
+              video_capture::mojom::ReceiverPtr& subscriber,
+              const media::VideoCaptureParams& requested_settings,
+              bool force_reopen_with_new_settings,
+              video_capture::mojom::PushVideoStreamSubscriptionRequest&
+                  subscription,
+              video_capture::mojom::VideoSource::CreatePushSubscriptionCallback&
+                  callback) {
             // Prepare the callback, but save it for now instead of invoking it.
-            create_device_success_answer_cb = base::BindOnce(
-                [](video_capture::mojom::DeviceRequest device_request,
-                   video_capture::mojom::DeviceFactory::CreateDeviceCallback
-                       callback,
-                   video_capture::mojom::DeviceAccessResultCode
+            create_push_subscription_success_answer_cb = base::BindOnce(
+                [](const media::VideoCaptureParams& requested_settings,
+                   video_capture::mojom::PushVideoStreamSubscriptionRequest
+                       subscription,
+                   video_capture::mojom::VideoSource::
+                       CreatePushSubscriptionCallback callback,
+                   video_capture::mojom::CreatePushSubscriptionResultCode
                        service_result_code) {
-                  std::move(callback).Run(service_result_code);
+                  std::move(callback).Run(service_result_code,
+                                          requested_settings);
                 },
-                std::move(*device_request), std::move(callback),
-                service_result_code);
+                requested_settings, std::move(subscription),
+                std::move(callback), service_result_code);
             step_1_run_loop.Quit();
           }));
   EXPECT_CALL(mock_callbacks_, DoOnDeviceLaunched(_)).Times(0);
@@ -209,39 +242,46 @@
   step_1_run_loop.Run();
   launcher_->AbortLaunch();
 
-  std::move(create_device_success_answer_cb).Run();
+  std::move(create_push_subscription_success_answer_cb).Run();
   step_2_run_loop.Run();
 
-  factory_delegate_.reset();
+  service_connection_.reset();
 
-  EXPECT_TRUE(launcher_has_connected_to_device_factory_);
-  EXPECT_TRUE(launcher_has_released_device_factory_);
+  EXPECT_TRUE(launcher_has_connected_to_source_provider_);
+  EXPECT_TRUE(launcher_has_released_source_provider_);
 }
 
 TEST_F(ServiceVideoCaptureDeviceLauncherTest,
        LaunchingDeviceFailsBecauseDeviceNotFound) {
   base::RunLoop run_loop;
 
-  EXPECT_CALL(mock_device_factory_, DoCreateDevice(kStubDeviceId, _, _))
-      .WillOnce(
-          Invoke([](const std::string& device_id,
-                    video_capture::mojom::DeviceRequest* device_request,
-                    video_capture::mojom::DeviceFactory::CreateDeviceCallback&
-                        callback) {
-            // Note: We must keep |device_request| alive at least until we have
-            // sent out the callback. Otherwise, |launcher_| may interpret this
-            // as the connection having been lost before receiving the callback.
+  EXPECT_CALL(mock_source_, DoCreatePushSubscription(_, _, _, _, _))
+      .WillOnce(Invoke(
+          [](video_capture::mojom::ReceiverPtr& subscriber,
+             const media::VideoCaptureParams& requested_settings,
+             bool force_reopen_with_new_settings,
+             video_capture::mojom::PushVideoStreamSubscriptionRequest&
+                 subscription,
+             video_capture::mojom::VideoSource::CreatePushSubscriptionCallback&
+                 callback) {
+            // Note: We post this to the end of the message queue to make it
+            // asynchronous.
             base::ThreadTaskRunnerHandle::Get()->PostTask(
                 FROM_HERE,
                 base::BindOnce(
-                    [](video_capture::mojom::DeviceRequest device_request,
-                       video_capture::mojom::DeviceFactory::CreateDeviceCallback
-                           callback) {
+                    [](video_capture::mojom::ReceiverPtr subscriber,
+                       const media::VideoCaptureParams& requested_settings,
+                       video_capture::mojom::PushVideoStreamSubscriptionRequest
+                           subscription,
+                       video_capture::mojom::VideoSource::
+                           CreatePushSubscriptionCallback callback) {
                       std::move(callback).Run(
-                          video_capture::mojom::DeviceAccessResultCode::
-                              ERROR_DEVICE_NOT_FOUND);
+                          video_capture::mojom::
+                              CreatePushSubscriptionResultCode::kFailed,
+                          requested_settings);
                     },
-                    std::move(*device_request), std::move(callback)));
+                    std::move(subscriber), requested_settings,
+                    std::move(subscription), std::move(callback)));
           }));
   EXPECT_CALL(mock_callbacks_, DoOnDeviceLaunched(_)).Times(0);
   EXPECT_CALL(mock_callbacks_, OnDeviceLaunchAborted()).Times(0);
@@ -264,7 +304,7 @@
 }
 
 TEST_F(ServiceVideoCaptureDeviceLauncherTest,
-       LaunchingDeviceFailsBecauseFactoryIsUnbound) {
+       LaunchingDeviceFailsBecauseSourceProviderIsUnbound) {
   base::RunLoop run_loop(base::RunLoop::Type::kNestableTasksAllowed);
 
   EXPECT_CALL(mock_callbacks_, DoOnDeviceLaunched(_)).Times(0);
@@ -276,7 +316,7 @@
   }));
 
   // Exercise
-  factory_delegate_->ReleaseFactoryForTesting();
+  service_connection_->ReleaseProviderForTesting();
 
   launcher_->LaunchDeviceAsync(kStubDeviceId, blink::MEDIA_DEVICE_VIDEO_CAPTURE,
                                kArbitraryParams, kNullReceiver,
@@ -290,18 +330,22 @@
        LaunchingDeviceFailsBecauseConnectionLostWhileLaunching) {
   base::RunLoop run_loop;
 
-  video_capture::mojom::DeviceFactory::CreateDeviceCallback create_device_cb;
-  EXPECT_CALL(mock_device_factory_, DoCreateDevice(kStubDeviceId, _, _))
-      .WillOnce(
-          Invoke([&create_device_cb](
-                     const std::string& device_id,
-                     video_capture::mojom::DeviceRequest* device_request,
-                     video_capture::mojom::DeviceFactory::CreateDeviceCallback&
-                         callback) {
+  video_capture::mojom::VideoSource::CreatePushSubscriptionCallback
+      create_subscription_cb;
+  EXPECT_CALL(mock_source_, DoCreatePushSubscription(_, _, _, _, _))
+      .WillOnce(Invoke(
+          [&create_subscription_cb](
+              video_capture::mojom::ReceiverPtr& subscriber,
+              const media::VideoCaptureParams& requested_settings,
+              bool force_reopen_with_new_settings,
+              video_capture::mojom::PushVideoStreamSubscriptionRequest&
+                  subscription,
+              video_capture::mojom::VideoSource::CreatePushSubscriptionCallback&
+                  callback) {
             // Simulate connection lost by not invoking |callback| and releasing
-            // |device_request|. We have to save |callback| and invoke it later
+            // |subscription|. We have to save |callback| and invoke it later
             // to avoid hitting a DCHECK.
-            create_device_cb = std::move(callback);
+            create_subscription_cb = std::move(callback);
           }));
   EXPECT_CALL(mock_callbacks_, DoOnDeviceLaunched(_)).Times(0);
   EXPECT_CALL(mock_callbacks_, OnDeviceLaunchAborted()).Times(0);
@@ -321,36 +365,35 @@
 
   run_loop.Run();
 
-  // Cut the connection to the factory, so that the outstanding
-  // |create_device_cb| will be dropped.
-  factory_binding_.reset();
+  // Cleanup
+  // Cut the connection to the source, so that the outstanding
+  // |create_subscription_cb| will be dropped when we invoke it below.
+  source_binding_.reset();
   // We have to invoke the callback, because not doing so triggers a DCHECK.
-  const video_capture::mojom::DeviceAccessResultCode arbitrary_result_code =
-      video_capture::mojom::DeviceAccessResultCode::SUCCESS;
-  std::move(create_device_cb).Run(arbitrary_result_code);
+  const video_capture::mojom::CreatePushSubscriptionResultCode
+      arbitrary_result_code = video_capture::mojom::
+          CreatePushSubscriptionResultCode::kCreatedWithRequestedSettings;
+  std::move(create_subscription_cb)
+      .Run(arbitrary_result_code, kArbitraryParams);
 }
 
 TEST_F(ServiceVideoCaptureDeviceLauncherTest,
-       ConnectionLostAfterSuccessfulLaunch) {
-  video_capture::mojom::DeviceRequest device_request_owned_by_service;
-  EXPECT_CALL(mock_device_factory_, DoCreateDevice(kStubDeviceId, _, _))
-      .WillOnce(Invoke([&device_request_owned_by_service](
-                           const std::string& device_id,
-                           video_capture::mojom::DeviceRequest* device_request,
-                           video_capture::mojom::DeviceFactory::
-                               CreateDeviceCallback& callback) {
-        // The service holds on to the |device_request|.
-        device_request_owned_by_service = std::move(*device_request);
-        base::ThreadTaskRunnerHandle::Get()->PostTask(
-            FROM_HERE,
-            base::BindOnce(
-                [](video_capture::mojom::DeviceFactory::CreateDeviceCallback
-                       callback) {
-                  std::move(callback).Run(
-                      video_capture::mojom::DeviceAccessResultCode::SUCCESS);
-                },
-                std::move(callback)));
-      }));
+       ConnectionToSubscriptionLostAfterSuccessfulLaunch) {
+  RunConnectionLostAfterSuccessfulStartTest(
+      base::BindOnce(&ServiceVideoCaptureDeviceLauncherTest::CloseSourceBinding,
+                     base::Unretained(this)));
+}
+
+TEST_F(ServiceVideoCaptureDeviceLauncherTest,
+       ConnectionToSourceLostAfterSuccessfulLaunch) {
+  RunConnectionLostAfterSuccessfulStartTest(base::BindOnce(
+      &ServiceVideoCaptureDeviceLauncherTest::CloseSubscriptionBindings,
+      base::Unretained(this)));
+}
+
+void ServiceVideoCaptureDeviceLauncherTest::
+    RunConnectionLostAfterSuccessfulStartTest(
+        base::OnceClosure close_connection_cb) {
   std::unique_ptr<LaunchedVideoCaptureDevice> launched_device;
   EXPECT_CALL(mock_callbacks_, DoOnDeviceLaunched(_))
       .WillOnce(
@@ -376,14 +419,14 @@
     step_2_run_loop.Quit();
   }));
   // Exercise step 2: The service cuts/loses the connection
-  device_request_owned_by_service = nullptr;
+  std::move(close_connection_cb).Run();
   step_2_run_loop.Run();
 
   launcher_.reset();
-  factory_delegate_.reset();
+  service_connection_.reset();
   wait_for_release_connection_cb_.Run();
-  EXPECT_TRUE(launcher_has_connected_to_device_factory_);
-  EXPECT_TRUE(launcher_has_released_device_factory_);
+  EXPECT_TRUE(launcher_has_connected_to_source_provider_);
+  EXPECT_TRUE(launcher_has_released_source_provider_);
 }
 
 }  // namespace content
diff --git a/content/browser/renderer_host/media/service_video_capture_provider.cc b/content/browser/renderer_host/media/service_video_capture_provider.cc
index fa741990..55c56ec 100644
--- a/content/browser/renderer_host/media/service_video_capture_provider.cc
+++ b/content/browser/renderer_host/media/service_video_capture_provider.cc
@@ -59,7 +59,7 @@
     : connector_(connector ? connector->Clone() : nullptr),
       create_accelerator_factory_cb_(std::move(create_accelerator_factory_cb)),
       emit_log_message_cb_(std::move(emit_log_message_cb)),
-      launcher_has_connected_to_device_factory_(false),
+      launcher_has_connected_to_source_provider_(false),
       service_listener_binding_(this),
       weak_ptr_factory_(this) {
   base::PostTaskWithTraits(
@@ -86,7 +86,7 @@
   DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
   return std::make_unique<ServiceVideoCaptureDeviceLauncher>(
       base::BindRepeating(
-          &ServiceVideoCaptureProvider::OnLauncherConnectingToDeviceFactory,
+          &ServiceVideoCaptureProvider::OnLauncherConnectingToSourceProvider,
           weak_ptr_factory_.GetWeakPtr()));
 }
 
@@ -106,7 +106,7 @@
   mojo::MakeStrongBinding(
       std::make_unique<VirtualVideoCaptureDevicesChangedObserver>(),
       mojo::MakeRequest(&observer));
-  service_connection->device_factory()->RegisterVirtualDevicesChangedObserver(
+  service_connection->source_provider()->RegisterVirtualDevicesChangedObserver(
       std::move(observer),
       true /*raise_event_if_virtual_devices_already_present*/);
 }
@@ -144,14 +144,14 @@
   service_manager->AddListener(std::move(listener));
 }
 
-void ServiceVideoCaptureProvider::OnLauncherConnectingToDeviceFactory(
-    scoped_refptr<RefCountedVideoCaptureFactory>* out_factory) {
+void ServiceVideoCaptureProvider::OnLauncherConnectingToSourceProvider(
+    scoped_refptr<RefCountedVideoSourceProvider>* out_provider) {
   DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
-  launcher_has_connected_to_device_factory_ = true;
-  *out_factory = LazyConnectToService();
+  launcher_has_connected_to_source_provider_ = true;
+  *out_provider = LazyConnectToService();
 }
 
-scoped_refptr<RefCountedVideoCaptureFactory>
+scoped_refptr<RefCountedVideoSourceProvider>
 ServiceVideoCaptureProvider::LazyConnectToService() {
   DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
 
@@ -163,7 +163,7 @@
   video_capture::uma::LogVideoCaptureServiceEvent(
       video_capture::uma::BROWSER_CONNECTING_TO_SERVICE);
   if (time_of_last_uninitialize_ != base::TimeTicks()) {
-    if (launcher_has_connected_to_device_factory_) {
+    if (launcher_has_connected_to_source_provider_) {
       video_capture::uma::LogDurationUntilReconnectAfterCapture(
           base::TimeTicks::Now() - time_of_last_uninitialize_);
     } else {
@@ -172,7 +172,7 @@
     }
   }
 
-  launcher_has_connected_to_device_factory_ = false;
+  launcher_has_connected_to_source_provider_ = false;
   time_of_last_connect_ = base::TimeTicks::Now();
 
   video_capture::mojom::AcceleratorFactoryPtr accelerator_factory;
@@ -189,14 +189,14 @@
 
   device_factory_provider->InjectGpuDependencies(
       std::move(accelerator_factory));
-  video_capture::mojom::DeviceFactoryPtr device_factory;
-  device_factory_provider->ConnectToDeviceFactory(
-      mojo::MakeRequest(&device_factory));
-  device_factory.set_connection_error_handler(base::BindOnce(
-      &ServiceVideoCaptureProvider::OnLostConnectionToDeviceFactory,
+  video_capture::mojom::VideoSourceProviderPtr source_provider;
+  device_factory_provider->ConnectToVideoSourceProvider(
+      mojo::MakeRequest(&source_provider));
+  source_provider.set_connection_error_handler(base::BindOnce(
+      &ServiceVideoCaptureProvider::OnLostConnectionToSourceProvider,
       weak_ptr_factory_.GetWeakPtr()));
-  auto result = base::MakeRefCounted<RefCountedVideoCaptureFactory>(
-      std::move(device_factory), std::move(device_factory_provider),
+  auto result = base::MakeRefCounted<RefCountedVideoSourceProvider>(
+      std::move(source_provider), std::move(device_factory_provider),
       base::BindOnce(&ServiceVideoCaptureProvider::OnServiceConnectionClosed,
                      weak_ptr_factory_.GetWeakPtr(),
                      ReasonForDisconnect::kUnused));
@@ -211,7 +211,7 @@
   auto service_connection = LazyConnectToService();
   // Use a ScopedCallbackRunner to make sure that |result_callback| gets
   // invoked with an empty result in case that the service drops the request.
-  service_connection->device_factory()->GetDeviceInfos(
+  service_connection->source_provider()->GetSourceInfos(
       mojo::WrapCallbackWithDefaultInvokeIfNotRun(
           base::BindOnce(&ServiceVideoCaptureProvider::OnDeviceInfosReceived,
                          weak_ptr_factory_.GetWeakPtr(), service_connection,
@@ -220,7 +220,7 @@
 }
 
 void ServiceVideoCaptureProvider::OnDeviceInfosReceived(
-    scoped_refptr<RefCountedVideoCaptureFactory> service_connection,
+    scoped_refptr<RefCountedVideoSourceProvider> service_connection,
     GetDeviceInfosCallback result_callback,
     int retry_count,
     const std::vector<media::VideoCaptureDeviceInfo>& infos) {
@@ -254,10 +254,10 @@
   base::ResetAndReturn(&result_callback).Run(infos);
 }
 
-void ServiceVideoCaptureProvider::OnLostConnectionToDeviceFactory() {
+void ServiceVideoCaptureProvider::OnLostConnectionToSourceProvider() {
   DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
   emit_log_message_cb_.Run(
-      "ServiceVideoCaptureProvider::OnLostConnectionToDeviceFactory");
+      "ServiceVideoCaptureProvider::OnLostConnectionToSourceProvider");
   // This may indicate that the video capture service has crashed. Uninitialize
   // here, so that a new connection will be established when clients try to
   // reconnect.
@@ -272,7 +272,7 @@
   switch (reason) {
     case ReasonForDisconnect::kShutdown:
     case ReasonForDisconnect::kUnused:
-      if (launcher_has_connected_to_device_factory_) {
+      if (launcher_has_connected_to_source_provider_) {
         video_capture::uma::LogVideoCaptureServiceEvent(
             video_capture::uma::
                 BROWSER_CLOSING_CONNECTION_TO_SERVICE_AFTER_CAPTURE);
diff --git a/content/browser/renderer_host/media/service_video_capture_provider.h b/content/browser/renderer_host/media/service_video_capture_provider.h
index 55fb590..ed95c61 100644
--- a/content/browser/renderer_host/media/service_video_capture_provider.h
+++ b/content/browser/renderer_host/media/service_video_capture_provider.h
@@ -7,7 +7,7 @@
 
 #include "base/threading/thread_checker.h"
 #include "build/build_config.h"
-#include "content/browser/renderer_host/media/ref_counted_video_capture_factory.h"
+#include "content/browser/renderer_host/media/ref_counted_video_source_provider.h"
 #include "content/browser/renderer_host/media/video_capture_provider.h"
 #include "mojo/public/cpp/bindings/binding.h"
 #include "services/service_manager/public/cpp/connector.h"
@@ -66,31 +66,31 @@
   void RegisterServiceListenerOnIOThread();
   // Note, this needs to have void return value because of "weak_ptrs can only
   // bind to methods without return values".
-  void OnLauncherConnectingToDeviceFactory(
-      scoped_refptr<RefCountedVideoCaptureFactory>* out_factory);
-  // Discarding the returned RefCountedVideoCaptureFactory indicates that the
+  void OnLauncherConnectingToSourceProvider(
+      scoped_refptr<RefCountedVideoSourceProvider>* out_provider);
+  // Discarding the returned RefCountedVideoSourceProvider indicates that the
   // caller no longer requires the connection to the service and allows it to
   // disconnect.
-  scoped_refptr<RefCountedVideoCaptureFactory> LazyConnectToService()
+  scoped_refptr<RefCountedVideoSourceProvider> LazyConnectToService()
       WARN_UNUSED_RESULT;
 
   void GetDeviceInfosAsyncForRetry(GetDeviceInfosCallback result_callback,
                                    int retry_count);
   void OnDeviceInfosReceived(
-      scoped_refptr<RefCountedVideoCaptureFactory> service_connection,
+      scoped_refptr<RefCountedVideoSourceProvider> service_connection,
       GetDeviceInfosCallback result_callback,
       int retry_count,
       const std::vector<media::VideoCaptureDeviceInfo>& infos);
-  void OnLostConnectionToDeviceFactory();
+  void OnLostConnectionToSourceProvider();
   void OnServiceConnectionClosed(ReasonForDisconnect reason);
 
   std::unique_ptr<service_manager::Connector> connector_;
   CreateAcceleratorFactoryCallback create_accelerator_factory_cb_;
   base::RepeatingCallback<void(const std::string&)> emit_log_message_cb_;
 
-  base::WeakPtr<RefCountedVideoCaptureFactory> weak_service_connection_;
+  base::WeakPtr<RefCountedVideoSourceProvider> weak_service_connection_;
 
-  bool launcher_has_connected_to_device_factory_;
+  bool launcher_has_connected_to_source_provider_;
   base::TimeTicks time_of_last_connect_;
   base::TimeTicks time_of_last_uninitialize_;
 
diff --git a/content/browser/renderer_host/media/service_video_capture_provider_unittest.cc b/content/browser/renderer_host/media/service_video_capture_provider_unittest.cc
index 7220d9d..6246579 100644
--- a/content/browser/renderer_host/media/service_video_capture_provider_unittest.cc
+++ b/content/browser/renderer_host/media/service_video_capture_provider_unittest.cc
@@ -12,12 +12,15 @@
 #include "content/public/browser/video_capture_device_launcher.h"
 #include "content/public/test/test_browser_thread_bundle.h"
 #include "mojo/public/cpp/bindings/binding.h"
+#include "mojo/public/cpp/bindings/binding_set.h"
 #include "services/service_manager/public/cpp/binder_registry.h"
 #include "services/service_manager/public/cpp/service.h"
 #include "services/service_manager/public/cpp/service_binding.h"
 #include "services/service_manager/public/cpp/test/test_connector_factory.h"
-#include "services/video_capture/public/cpp/mock_device_factory.h"
 #include "services/video_capture/public/cpp/mock_device_factory_provider.h"
+#include "services/video_capture/public/cpp/mock_push_subscription.h"
+#include "services/video_capture/public/cpp/mock_video_source.h"
+#include "services/video_capture/public/cpp/mock_video_source_provider.h"
 #include "services/video_capture/public/mojom/constants.mojom.h"
 #include "services/video_capture/public/mojom/producer.mojom.h"
 #include "testing/gmock/include/gmock/gmock.h"
@@ -101,7 +104,7 @@
             service_manager::mojom::kServiceName)),
         factory_provider_binding_(&mock_device_factory_provider_),
         factory_provider_is_bound_(false),
-        device_factory_binding_(&mock_device_factory_) {}
+        source_provider_binding_(&mock_source_provider_) {}
   ~ServiceVideoCaptureProviderTest() override {}
 
  protected:
@@ -127,14 +130,45 @@
                       },
                       &factory_provider_is_bound_));
             }));
-    ON_CALL(mock_device_factory_provider_, DoConnectToDeviceFactory(_))
-        .WillByDefault(
-            Invoke([this](video_capture::mojom::DeviceFactoryRequest& request) {
-              if (device_factory_binding_.is_bound())
-                device_factory_binding_.Close();
-              device_factory_binding_.Bind(std::move(request));
+    ON_CALL(mock_device_factory_provider_, DoConnectToVideoSourceProvider(_))
+        .WillByDefault(Invoke(
+            [this](video_capture::mojom::VideoSourceProviderRequest& request) {
+              if (source_provider_binding_.is_bound())
+                source_provider_binding_.Close();
+              source_provider_binding_.Bind(std::move(request));
               wait_for_connection_to_service_.Quit();
             }));
+
+    ON_CALL(mock_source_provider_, DoGetSourceInfos(_))
+        .WillByDefault(Invoke([](video_capture::mojom::VideoSourceProvider::
+                                     GetSourceInfosCallback& callback) {
+          std::vector<media::VideoCaptureDeviceInfo> arbitrarily_empty_results;
+          base::ResetAndReturn(&callback).Run(arbitrarily_empty_results);
+        }));
+
+    ON_CALL(mock_source_provider_, DoGetVideoSource(_, _))
+        .WillByDefault(
+            Invoke([this](const std::string& device_id,
+                          video_capture::mojom::VideoSourceRequest* request) {
+              source_bindings_.AddBinding(&mock_source_, std::move(*request));
+            }));
+
+    ON_CALL(mock_source_, DoCreatePushSubscription(_, _, _, _, _))
+        .WillByDefault(Invoke(
+            [this](video_capture::mojom::ReceiverPtr& subscriber,
+                   const media::VideoCaptureParams& requested_settings,
+                   bool force_reopen_with_new_settings,
+                   video_capture::mojom::PushVideoStreamSubscriptionRequest&
+                       subscription,
+                   video_capture::mojom::VideoSource::
+                       CreatePushSubscriptionCallback& callback) {
+              subscription_bindings_.AddBinding(&mock_subscription_,
+                                                std::move(subscription));
+              std::move(callback).Run(
+                  video_capture::mojom::CreatePushSubscriptionResultCode::
+                      kCreatedWithRequestedSettings,
+                  requested_settings);
+            }));
   }
 
   void TearDown() override {}
@@ -147,12 +181,18 @@
   mojo::Binding<video_capture::mojom::DeviceFactoryProvider>
       factory_provider_binding_;
   bool factory_provider_is_bound_;
-  video_capture::MockDeviceFactory mock_device_factory_;
-  mojo::Binding<video_capture::mojom::DeviceFactory> device_factory_binding_;
+  video_capture::MockVideoSourceProvider mock_source_provider_;
+  mojo::Binding<video_capture::mojom::VideoSourceProvider>
+      source_provider_binding_;
+  video_capture::MockVideoSource mock_source_;
+  mojo::BindingSet<video_capture::mojom::VideoSource> source_bindings_;
+  video_capture::MockPushSubcription mock_subscription_;
+  mojo::BindingSet<video_capture::mojom::PushVideoStreamSubscription>
+      subscription_bindings_;
   std::unique_ptr<ServiceVideoCaptureProvider> provider_;
   base::MockCallback<VideoCaptureProvider::GetDeviceInfosCallback> results_cb_;
   base::MockCallback<
-      video_capture::mojom::DeviceFactory::GetDeviceInfosCallback>
+      video_capture::mojom::VideoSourceProvider::GetSourceInfosCallback>
       service_cb_;
   base::RunLoop wait_for_connection_to_service_;
 
@@ -167,14 +207,14 @@
        GetDeviceInfosAsyncInvokesCallbackWhenLosingConnection) {
   base::RunLoop run_loop;
 
-  video_capture::mojom::DeviceFactory::GetDeviceInfosCallback
+  video_capture::mojom::VideoSourceProvider::GetSourceInfosCallback
       callback_to_be_called_by_service;
   base::RunLoop wait_for_call_to_arrive_at_service;
-  EXPECT_CALL(mock_device_factory_, DoGetDeviceInfos(_))
+  EXPECT_CALL(mock_source_provider_, DoGetSourceInfos(_))
       .WillOnce(Invoke(
           [&callback_to_be_called_by_service,
            &wait_for_call_to_arrive_at_service](
-              video_capture::mojom::DeviceFactory::GetDeviceInfosCallback&
+              video_capture::mojom::VideoSourceProvider::GetSourceInfosCallback&
                   callback) {
             // Hold on to the callback so we can drop it later.
             callback_to_be_called_by_service = std::move(callback);
@@ -194,7 +234,7 @@
   wait_for_call_to_arrive_at_service.Run();
 
   // Simulate that the service goes down by cutting the connections.
-  device_factory_binding_.Close();
+  source_provider_binding_.Close();
   factory_provider_binding_.Close();
 
   wait_for_callback_from_service.Run();
@@ -205,14 +245,14 @@
 TEST_F(ServiceVideoCaptureProviderTest,
        ClosesServiceConnectionAfterGetDeviceInfos) {
   // Setup part 1
-  video_capture::mojom::DeviceFactory::GetDeviceInfosCallback
+  video_capture::mojom::VideoSourceProvider::GetSourceInfosCallback
       callback_to_be_called_by_service;
   base::RunLoop wait_for_call_to_arrive_at_service;
-  EXPECT_CALL(mock_device_factory_, DoGetDeviceInfos(_))
+  EXPECT_CALL(mock_source_provider_, DoGetSourceInfos(_))
       .WillOnce(Invoke(
           [&callback_to_be_called_by_service,
            &wait_for_call_to_arrive_at_service](
-              video_capture::mojom::DeviceFactory::GetDeviceInfosCallback&
+              video_capture::mojom::VideoSourceProvider::GetSourceInfosCallback&
                   callback) {
             // Hold on to the callback so we can drop it later.
             callback_to_be_called_by_service = std::move(callback);
@@ -225,11 +265,11 @@
 
   // Setup part 2: Now that the connection to the service is established, we can
   // listen for disconnects.
-  base::RunLoop wait_for_connection_to_device_factory_to_close;
+  base::RunLoop wait_for_connection_to_source_provider_to_close;
   base::RunLoop wait_for_connection_to_device_factory_provider_to_close;
-  device_factory_binding_.set_connection_error_handler(
+  source_provider_binding_.set_connection_error_handler(
       base::BindOnce([](base::RunLoop* run_loop) { run_loop->Quit(); },
-                     &wait_for_connection_to_device_factory_to_close));
+                     &wait_for_connection_to_source_provider_to_close));
   factory_provider_binding_.set_connection_error_handler(
       base::BindOnce([](base::RunLoop* run_loop) { run_loop->Quit(); },
                      &wait_for_connection_to_device_factory_provider_to_close));
@@ -240,7 +280,7 @@
       .Run(arbitrarily_empty_results);
 
   // Verification: Expect |provider_| to close the connection to the service.
-  wait_for_connection_to_device_factory_to_close.Run();
+  wait_for_connection_to_source_provider_to_close.Run();
   if (factory_provider_is_bound_) {
     wait_for_connection_to_device_factory_provider_to_close.Run();
   }
@@ -252,21 +292,6 @@
 // soon as the last VideoCaptureDeviceLauncher instance is released.
 TEST_F(ServiceVideoCaptureProviderTest,
        KeepsServiceConnectionWhileDeviceLauncherAlive) {
-  ON_CALL(mock_device_factory_, DoGetDeviceInfos(_))
-      .WillByDefault(Invoke([](video_capture::mojom::DeviceFactory::
-                                   GetDeviceInfosCallback& callback) {
-        std::vector<media::VideoCaptureDeviceInfo> arbitrarily_empty_results;
-        base::ResetAndReturn(&callback).Run(arbitrarily_empty_results);
-      }));
-  ON_CALL(mock_device_factory_, DoCreateDevice(_, _, _))
-      .WillByDefault(
-          Invoke([](const std::string& device_id,
-                    video_capture::mojom::DeviceRequest* device_request,
-                    video_capture::mojom::DeviceFactory::CreateDeviceCallback&
-                        callback) {
-            base::ResetAndReturn(&callback).Run(
-                video_capture::mojom::DeviceAccessResultCode::SUCCESS);
-          }));
   MockVideoCaptureDeviceLauncherCallbacks mock_callbacks;
 
   // Exercise part 1: Create a device launcher and hold on to it.
@@ -281,7 +306,7 @@
 
   // Monitor if connection gets closed
   bool connection_has_been_closed = false;
-  device_factory_binding_.set_connection_error_handler(base::BindOnce(
+  source_provider_binding_.set_connection_error_handler(base::BindOnce(
       [](bool* connection_has_been_closed) {
         *connection_has_been_closed = true;
       },
@@ -335,7 +360,7 @@
   }
   ASSERT_FALSE(connection_has_been_closed);
 
-  // Exercise part 3: Release the initial device launcher.
+  // Exercise part 4: Release the initial device launcher.
   device_launcher_1.reset();
   {
     base::RunLoop give_provider_chance_to_disconnect;
@@ -352,12 +377,12 @@
        DoesNotCloseServiceConnectionWhileGetDeviceInfoResponsePending) {
   // When GetDeviceInfos gets called, hold on to the callbacks, but do not
   // yet invoke them.
-  std::vector<video_capture::mojom::DeviceFactory::GetDeviceInfosCallback>
+  std::vector<video_capture::mojom::VideoSourceProvider::GetSourceInfosCallback>
       callbacks_to_be_called_by_service;
-  ON_CALL(mock_device_factory_, DoGetDeviceInfos(_))
+  ON_CALL(mock_source_provider_, DoGetSourceInfos(_))
       .WillByDefault(Invoke(
           [&callbacks_to_be_called_by_service](
-              video_capture::mojom::DeviceFactory::GetDeviceInfosCallback&
+              video_capture::mojom::VideoSourceProvider::GetSourceInfosCallback&
                   callback) {
             callbacks_to_be_called_by_service.push_back(std::move(callback));
           }));
@@ -377,7 +402,7 @@
 
   // Monitor if connection gets closed
   bool connection_has_been_closed = false;
-  device_factory_binding_.set_connection_error_handler(base::BindOnce(
+  source_provider_binding_.set_connection_error_handler(base::BindOnce(
       [](bool* connection_has_been_closed) {
         *connection_has_been_closed = true;
       },
diff --git a/content/browser/renderer_host/media/video_capture_browsertest.cc b/content/browser/renderer_host/media/video_capture_browsertest.cc
index 9e789cb..d649aaa 100644
--- a/content/browser/renderer_host/media/video_capture_browsertest.cc
+++ b/content/browser/renderer_host/media/video_capture_browsertest.cc
@@ -245,7 +245,8 @@
 }
 
 // Flaky on MSAN. https://crbug.com/840294
-#if defined(MEMORY_SANITIZER)
+// Flaky on MacOS 10.12. https://crbug.com/938074
+#if defined(MEMORY_SANITIZER) || defined(MAC_OS_X_VERSION_10_12)
 #define MAYBE_ReceiveFramesFromFakeCaptureDevice \
   DISABLED_ReceiveFramesFromFakeCaptureDevice
 #else
diff --git a/content/browser/renderer_host/media/virtual_video_capture_devices_changed_observer.h b/content/browser/renderer_host/media/virtual_video_capture_devices_changed_observer.h
index f40e0f9e..591c3d7 100644
--- a/content/browser/renderer_host/media/virtual_video_capture_devices_changed_observer.h
+++ b/content/browser/renderer_host/media/virtual_video_capture_devices_changed_observer.h
@@ -6,6 +6,7 @@
 #define CONTENT_BROWSER_RENDERER_HOST_MEDIA_VIRTUAL_VIDEO_CAPTURE_DEVICES_CHANGED_OBSERVER_H_
 
 #include "services/video_capture/public/mojom/device_factory.mojom.h"
+#include "services/video_capture/public/mojom/devices_changed_observer.mojom.h"
 
 namespace content {
 
diff --git a/content/browser/renderer_host/render_process_host_impl.cc b/content/browser/renderer_host/render_process_host_impl.cc
index c6feebd6..7b3ce17ad 100644
--- a/content/browser/renderer_host/render_process_host_impl.cc
+++ b/content/browser/renderer_host/render_process_host_impl.cc
@@ -3090,9 +3090,6 @@
 #if BUILDFLAG(ENABLE_PLUGINS)
     switches::kEnablePepperTesting,
 #endif
-#if BUILDFLAG(ENABLE_RUNTIME_MEDIA_RENDERER_SELECTION)
-    switches::kDisableMojoRenderer,
-#endif
     switches::kDisableWebRtcHWDecoding,
     switches::kDisableWebRtcHWEncoding,
     switches::kEnableWebRtcSrtpAesGcm,
diff --git a/content/browser/renderer_host/render_widget_host_impl.cc b/content/browser/renderer_host/render_widget_host_impl.cc
index bb1096d..7b7be300 100644
--- a/content/browser/renderer_host/render_widget_host_impl.cc
+++ b/content/browser/renderer_host/render_widget_host_impl.cc
@@ -494,7 +494,7 @@
   }
 
   enable_surface_synchronization_ = features::IsSurfaceSynchronizationEnabled();
-  enable_viz_ = base::FeatureList::IsEnabled(features::kVizDisplayCompositor);
+  enable_viz_ = features::IsVizDisplayCompositorEnabled();
 
   if (!enable_viz_) {
 #if !defined(OS_ANDROID)
diff --git a/content/browser/renderer_host/render_widget_host_view_aura_unittest.cc b/content/browser/renderer_host/render_widget_host_view_aura_unittest.cc
index 6361d4d93..71d20f15 100644
--- a/content/browser/renderer_host/render_widget_host_view_aura_unittest.cc
+++ b/content/browser/renderer_host/render_widget_host_view_aura_unittest.cc
@@ -3056,7 +3056,7 @@
 TEST_F(RenderWidgetHostViewAuraTest, TwoOutputSurfaces) {
   // TODO(jonross): Delete this test once Viz launches as it will be obsolete.
   // https://crbug.com/844469
-  if (base::FeatureList::IsEnabled(features::kVizDisplayCompositor) ||
+  if (features::IsVizDisplayCompositorEnabled() ||
       features::IsMultiProcessMash()) {
     return;
   }
@@ -3425,7 +3425,7 @@
        CompositorFrameSinkChange) {
   // TODO(jonross): Delete this test once Viz launches as it will be obsolete.
   // https://crbug.com/844469
-  if (base::FeatureList::IsEnabled(features::kVizDisplayCompositor) ||
+  if (features::IsVizDisplayCompositorEnabled() ||
       features::IsMultiProcessMash()) {
     return;
   }
@@ -3691,7 +3691,7 @@
 TEST_F(RenderWidgetHostViewAuraTest, ForwardsBeginFrameAcks) {
   // TODO(jonross): Delete this test once Viz launches as it will be obsolete.
   // https://crbug.com/844469
-  if (base::FeatureList::IsEnabled(features::kVizDisplayCompositor) ||
+  if (features::IsVizDisplayCompositorEnabled() ||
       features::IsMultiProcessMash()) {
     return;
   }
@@ -5737,7 +5737,7 @@
 TEST_F(RenderWidgetHostViewAuraTest, HitTestRegionListSubmitted) {
   // TODO(jonross): Delete this test once Viz launches as it will be obsolete.
   // https://crbug.com/844469
-  if (base::FeatureList::IsEnabled(features::kVizDisplayCompositor) ||
+  if (features::IsVizDisplayCompositorEnabled() ||
       features::IsMultiProcessMash()) {
     return;
   }
diff --git a/content/browser/renderer_host/render_widget_host_view_child_frame.cc b/content/browser/renderer_host/render_widget_host_view_child_frame.cc
index 5492855a..1e2a66d 100644
--- a/content/browser/renderer_host/render_widget_host_view_child_frame.cc
+++ b/content/browser/renderer_host/render_widget_host_view_child_frame.cc
@@ -72,8 +72,7 @@
           base::checked_cast<uint32_t>(widget_host->GetProcess()->GetID()),
           base::checked_cast<uint32_t>(widget_host->GetRoutingID())),
       frame_connector_(nullptr),
-      enable_viz_(
-          base::FeatureList::IsEnabled(features::kVizDisplayCompositor)),
+      enable_viz_(features::IsVizDisplayCompositorEnabled()),
       enable_surface_synchronization_(
           features::IsSurfaceSynchronizationEnabled()),
       weak_factory_(this) {
diff --git a/content/browser/renderer_host/render_widget_host_view_mac.mm b/content/browser/renderer_host/render_widget_host_view_mac.mm
index 9daf8d2..f6937e3 100644
--- a/content/browser/renderer_host/render_widget_host_view_mac.mm
+++ b/content/browser/renderer_host/render_widget_host_view_mac.mm
@@ -240,7 +240,7 @@
   // Any renderer that will produce frames needs to have begin frames sent to
   // it. So unless it is never visible, start this value at true here to avoid
   // startup raciness and decrease latency.
-  if (!base::FeatureList::IsEnabled(features::kVizDisplayCompositor)) {
+  if (!features::IsVizDisplayCompositorEnabled()) {
     needs_begin_frames_ = needs_begin_frames;
     UpdateNeedsBeginFramesInternal();
   }
diff --git a/content/browser/service_worker/embedded_worker_instance.cc b/content/browser/service_worker/embedded_worker_instance.cc
index 2bc4e6bd..51f5000 100644
--- a/content/browser/service_worker/embedded_worker_instance.cc
+++ b/content/browser/service_worker/embedded_worker_instance.cc
@@ -687,7 +687,7 @@
   DCHECK_CURRENTLY_ON(BrowserThread::IO);
   devtools_proxy_.reset();
   if (registry_->GetWorker(embedded_worker_id_))
-    registry_->RemoveWorker(process_id(), embedded_worker_id_);
+    registry_->RemoveWorker(embedded_worker_id_);
   ReleaseProcess();
 }
 
@@ -846,7 +846,6 @@
   params->provider_info->cache_storage = std::move(cache_storage);
 
   client_->StartWorker(std::move(params));
-  registry_->BindWorkerToProcess(process_id(), embedded_worker_id());
 
   starting_phase_ = is_script_streaming ? SCRIPT_STREAMING : SENT_START_WORKER;
   for (auto& observer : listener_list_)
@@ -944,8 +943,6 @@
   if (!devtools_attached_)
     lifetime_tracker_ = std::make_unique<ScopedLifetimeTracker>();
 
-  if (!registry_->OnWorkerStarted(process_id(), embedded_worker_id_))
-    return;
   // Stop was requested before OnStarted was sent back from the worker. Just
   // pretend startup didn't happen, so observers don't try to use the running
   // worker as it will stop soon.
@@ -981,8 +978,6 @@
 }
 
 void EmbeddedWorkerInstance::OnStopped() {
-  registry_->OnWorkerStopped(process_id(), embedded_worker_id_);
-
   EmbeddedWorkerStatus old_status = status_;
   ReleaseProcess();
   for (auto& observer : listener_list_)
@@ -993,7 +988,6 @@
   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
   if (status() == EmbeddedWorkerStatus::STOPPED)
     return;
-  registry_->DetachWorker(process_id(), embedded_worker_id());
 
   EmbeddedWorkerStatus old_status = status_;
   ReleaseProcess();
@@ -1028,7 +1022,7 @@
 
 void EmbeddedWorkerInstance::OnReportConsoleMessage(
     int source_identifier,
-    int message_level,
+    blink::mojom::ConsoleMessageLevel message_level,
     const base::string16& message,
     int line_number,
     const GURL& source_url) {
diff --git a/content/browser/service_worker/embedded_worker_instance.h b/content/browser/service_worker/embedded_worker_instance.h
index 92618e25..79be4bbe 100644
--- a/content/browser/service_worker/embedded_worker_instance.h
+++ b/content/browser/service_worker/embedded_worker_instance.h
@@ -27,6 +27,7 @@
 #include "mojo/public/cpp/bindings/associated_binding.h"
 #include "third_party/blink/public/common/service_worker/service_worker_status_code.h"
 #include "third_party/blink/public/mojom/cache_storage/cache_storage.mojom.h"
+#include "third_party/blink/public/mojom/devtools/console_message.mojom.h"
 #include "third_party/blink/public/mojom/service_worker/controller_service_worker.mojom.h"
 #include "third_party/blink/public/mojom/service_worker/embedded_worker.mojom.h"
 #include "third_party/blink/public/mojom/service_worker/service_worker.mojom.h"
@@ -111,11 +112,12 @@
                                    int line_number,
                                    int column_number,
                                    const GURL& source_url) {}
-    virtual void OnReportConsoleMessage(int source_identifier,
-                                        int message_level,
-                                        const base::string16& message,
-                                        int line_number,
-                                        const GURL& source_url) {}
+    virtual void OnReportConsoleMessage(
+        int source_identifier,
+        blink::mojom::ConsoleMessageLevel message_level,
+        const base::string16& message,
+        int line_number,
+        const GURL& source_url) {}
   };
 
   ~EmbeddedWorkerInstance() override;
@@ -292,7 +294,7 @@
                          int column_number,
                          const GURL& source_url) override;
   void OnReportConsoleMessage(int source_identifier,
-                              int message_level,
+                              blink::mojom::ConsoleMessageLevel message_level,
                               const base::string16& message,
                               int line_number,
                               const GURL& source_url) override;
diff --git a/content/browser/service_worker/embedded_worker_instance_unittest.cc b/content/browser/service_worker/embedded_worker_instance_unittest.cc
index a2812a2..3ccfae1 100644
--- a/content/browser/service_worker/embedded_worker_instance_unittest.cc
+++ b/content/browser/service_worker/embedded_worker_instance_unittest.cc
@@ -343,43 +343,6 @@
   EXPECT_EQ(EmbeddedWorkerStatus::STOPPED, worker->status());
 }
 
-// Test that the removal of a worker from the registry doesn't remove
-// other workers in the same process.
-TEST_F(EmbeddedWorkerInstanceTest, RemoveWorkerInSharedProcess) {
-  const GURL scope("http://example.com/");
-  const GURL url("http://example.com/worker.js");
-
-  RegistrationAndVersionPair pair1 = PrepareRegistrationAndVersion(scope, url);
-  std::unique_ptr<EmbeddedWorkerInstance> worker1 =
-      embedded_worker_registry()->CreateWorker(pair1.second.get());
-  RegistrationAndVersionPair pair2 = PrepareRegistrationAndVersion(scope, url);
-  std::unique_ptr<EmbeddedWorkerInstance> worker2 =
-      embedded_worker_registry()->CreateWorker(pair2.second.get());
-
-  int process_id = helper_->mock_render_process_id();
-
-  // Start workers.
-  StartWorker(worker1.get(), CreateStartParams(pair1.second));
-  StartWorker(worker2.get(), CreateStartParams(pair2.second));
-
-  // The two workers share the same process.
-  EXPECT_EQ(worker1->process_id(), worker2->process_id());
-
-  // Destroy worker1. It removes itself from the registry.
-  int worker1_id = worker1->embedded_worker_id();
-  worker1->Stop();
-  worker1.reset();
-
-  // Only worker1 should be removed from the registry's process_map.
-  EmbeddedWorkerRegistry* registry =
-      helper_->context()->embedded_worker_registry();
-  EXPECT_EQ(0UL, registry->worker_process_map_[process_id].count(worker1_id));
-  EXPECT_EQ(1UL, registry->worker_process_map_[process_id].count(
-                     worker2->embedded_worker_id()));
-
-  worker2->Stop();
-}
-
 TEST_F(EmbeddedWorkerInstanceTest, DetachDuringProcessAllocation) {
   const GURL scope("http://example.com/");
   const GURL url("http://example.com/worker.js");
@@ -583,22 +546,13 @@
   RegistrationAndVersionPair pair = PrepareRegistrationAndVersion(scope, url);
   std::unique_ptr<EmbeddedWorkerInstance> worker =
       embedded_worker_registry()->CreateWorker(pair.second.get());
-  worker->AddObserver(this);
 
   // Start the worker.
-  base::RunLoop run_loop;
   StartWorker(worker.get(), CreateStartParams(pair.second));
 
   // Detach.
-  int process_id = worker->process_id();
   worker->Detach();
   EXPECT_EQ(EmbeddedWorkerStatus::STOPPED, worker->status());
-
-  // Send the registry a message from the detached worker. Nothing should
-  // happen.
-  embedded_worker_registry()->OnWorkerStarted(process_id,
-                                              worker->embedded_worker_id());
-  EXPECT_EQ(EmbeddedWorkerStatus::STOPPED, worker->status());
 }
 
 // Test for when sending the start IPC failed.
diff --git a/content/browser/service_worker/embedded_worker_registry.cc b/content/browser/service_worker/embedded_worker_registry.cc
index 4d7d73a6..f74289d 100644
--- a/content/browser/service_worker/embedded_worker_registry.cc
+++ b/content/browser/service_worker/embedded_worker_registry.cc
@@ -4,17 +4,8 @@
 
 #include "content/browser/service_worker/embedded_worker_registry.h"
 
-#include "base/bind_helpers.h"
-#include "base/metrics/histogram_macros.h"
-#include "base/stl_util.h"
-#include "content/browser/renderer_host/render_widget_helper.h"
 #include "content/browser/service_worker/embedded_worker_instance.h"
 #include "content/browser/service_worker/service_worker_context_core.h"
-#include "content/browser/service_worker/service_worker_context_wrapper.h"
-#include "content/browser/service_worker/service_worker_dispatcher_host.h"
-#include "content/public/browser/browser_thread.h"
-#include "ipc/ipc_message.h"
-#include "ipc/ipc_sender.h"
 
 namespace content {
 
@@ -43,27 +34,6 @@
   return worker;
 }
 
-void EmbeddedWorkerRegistry::Shutdown() {
-  for (auto it = worker_map_.begin(); it != worker_map_.end(); ++it) {
-    it->second->Stop();
-  }
-}
-
-bool EmbeddedWorkerRegistry::OnWorkerStarted(int process_id,
-                                             int embedded_worker_id) {
-  if (!base::ContainsKey(worker_process_map_, process_id) ||
-      !base::ContainsKey(worker_process_map_[process_id], embedded_worker_id)) {
-    return false;
-  }
-
-  return true;
-}
-
-void EmbeddedWorkerRegistry::OnWorkerStopped(int process_id,
-                                             int embedded_worker_id) {
-  worker_process_map_[process_id].erase(embedded_worker_id);
-}
-
 EmbeddedWorkerInstance* EmbeddedWorkerRegistry::GetWorker(
     int embedded_worker_id) {
   auto found = worker_map_.find(embedded_worker_id);
@@ -80,36 +50,11 @@
       initial_embedded_worker_id_(initial_embedded_worker_id) {
 }
 
-EmbeddedWorkerRegistry::~EmbeddedWorkerRegistry() {
-  Shutdown();
-}
+EmbeddedWorkerRegistry::~EmbeddedWorkerRegistry() = default;
 
-void EmbeddedWorkerRegistry::BindWorkerToProcess(int process_id,
-                                                 int embedded_worker_id) {
-  DCHECK(GetWorker(embedded_worker_id));
-  DCHECK_EQ(GetWorker(embedded_worker_id)->process_id(), process_id);
-  DCHECK(
-      !base::ContainsKey(worker_process_map_, process_id) ||
-      !base::ContainsKey(worker_process_map_[process_id], embedded_worker_id));
-
-  worker_process_map_[process_id].insert(embedded_worker_id);
-}
-
-void EmbeddedWorkerRegistry::RemoveWorker(int process_id,
-                                          int embedded_worker_id) {
+void EmbeddedWorkerRegistry::RemoveWorker(int embedded_worker_id) {
   DCHECK(base::ContainsKey(worker_map_, embedded_worker_id));
-  DetachWorker(process_id, embedded_worker_id);
   worker_map_.erase(embedded_worker_id);
 }
 
-void EmbeddedWorkerRegistry::DetachWorker(int process_id,
-                                          int embedded_worker_id) {
-  DCHECK(base::ContainsKey(worker_map_, embedded_worker_id));
-  if (!base::ContainsKey(worker_process_map_, process_id))
-    return;
-  worker_process_map_[process_id].erase(embedded_worker_id);
-  if (worker_process_map_[process_id].empty())
-    worker_process_map_.erase(process_id);
-}
-
 }  // namespace content
diff --git a/content/browser/service_worker/embedded_worker_registry.h b/content/browser/service_worker/embedded_worker_registry.h
index d6a6980..a9b4d8b 100644
--- a/content/browser/service_worker/embedded_worker_registry.h
+++ b/content/browser/service_worker/embedded_worker_registry.h
@@ -7,16 +7,12 @@
 
 #include <map>
 #include <memory>
-#include <set>
-#include <vector>
 
 #include "base/gtest_prod_util.h"
 #include "base/macros.h"
 #include "base/memory/ref_counted.h"
 #include "base/memory/weak_ptr.h"
-#include "base/strings/string16.h"
 #include "content/common/content_export.h"
-#include "third_party/blink/public/common/service_worker/service_worker_status_code.h"
 
 namespace content {
 
@@ -24,9 +20,6 @@
 class ServiceWorkerContextCore;
 class ServiceWorkerVersion;
 
-// Acts as a thin stub between MessageFilter and each EmbeddedWorkerInstance,
-// which sends/receives messages to/from each EmbeddedWorker in child process.
-//
 // Hangs off ServiceWorkerContextCore (its reference is also held by each
 // EmbeddedWorkerInstance).  Operated only on IO thread.
 class CONTENT_EXPORT EmbeddedWorkerRegistry
@@ -46,20 +39,11 @@
   std::unique_ptr<EmbeddedWorkerInstance> CreateWorker(
       ServiceWorkerVersion* owner_version);
 
-  // Stop all running workers, even if they're handling events.
-  void Shutdown();
-
-  // Called by EmbeddedWorkerInstance when it starts or stops. This registry
-  // keeps track of running workers.
-  bool OnWorkerStarted(int process_id, int embedded_worker_id);
-  void OnWorkerStopped(int process_id, int embedded_worker_id);
-
   // Returns an embedded worker instance for given |embedded_worker_id|.
   EmbeddedWorkerInstance* GetWorker(int embedded_worker_id);
 
  private:
   friend class base::RefCounted<EmbeddedWorkerRegistry>;
-  friend class MojoEmbeddedWorkerInstanceTest;
   friend class EmbeddedWorkerInstance;
   friend class EmbeddedWorkerInstanceTest;
   FRIEND_TEST_ALL_PREFIXES(EmbeddedWorkerInstanceTest,
@@ -72,29 +56,13 @@
       int initial_embedded_worker_id);
   ~EmbeddedWorkerRegistry();
 
-  // Called when EmbeddedWorkerInstance is ready for IPC. This function
-  // prepares a route to the child worker thread.
-  // TODO(shimazu): Remove this function once mojofication is completed.
-  void BindWorkerToProcess(int process_id, int embedded_worker_id);
-
   // RemoveWorker is called when EmbeddedWorkerInstance is destructed.
-  // |process_id| could be invalid (i.e. ChildProcessHost::kInvalidUniqueID)
-  // if it's not running.
-  void RemoveWorker(int process_id, int embedded_worker_id);
-
-  // Removes the bookkeeping that binds the worker to the process.  This is
-  // called instead of WorkerStopped() in cases when the worker could not be
-  // cleanly stopped, e.g., because connection with the renderer was lost.
-  void DetachWorker(int process_id, int embedded_worker_id);
+  void RemoveWorker(int embedded_worker_id);
 
   base::WeakPtr<ServiceWorkerContextCore> context_;
 
   WorkerInstanceMap worker_map_;
 
-  // Map from process_id to embedded_worker_id.
-  // This map only contains starting and running workers.
-  std::map<int, std::set<int> > worker_process_map_;
-
   int next_embedded_worker_id_;
   const int initial_embedded_worker_id_;
 
diff --git a/content/browser/service_worker/service_worker_browsertest.cc b/content/browser/service_worker/service_worker_browsertest.cc
index 8f1a9070..a87fb9c 100644
--- a/content/browser/service_worker/service_worker_browsertest.cc
+++ b/content/browser/service_worker/service_worker_browsertest.cc
@@ -490,7 +490,7 @@
 class ConsoleListener : public EmbeddedWorkerInstance::Listener {
  public:
   void OnReportConsoleMessage(int source_identifier,
-                              int message_level,
+                              blink::mojom::ConsoleMessageLevel message_level,
                               const base::string16& message,
                               int line_number,
                               const GURL& source_url) override {
diff --git a/content/browser/service_worker/service_worker_context_core.cc b/content/browser/service_worker/service_worker_context_core.cc
index 16709a9..dc2a1f6f 100644
--- a/content/browser/service_worker/service_worker_context_core.cc
+++ b/content/browser/service_worker/service_worker_context_core.cc
@@ -836,7 +836,7 @@
 void ServiceWorkerContextCore::OnReportConsoleMessage(
     ServiceWorkerVersion* version,
     int source_identifier,
-    int message_level,
+    blink::mojom::ConsoleMessageLevel message_level,
     const base::string16& message,
     int line_number,
     const GURL& source_url) {
@@ -848,8 +848,8 @@
   // BrowserContext and call ContentBrowserClient::IsBuiltinComponent().
   const bool is_builtin_component = HasWebUIScheme(source_url);
 
-  LogConsoleMessage(message_level, message, line_number, is_builtin_component,
-                    wrapper_->is_incognito(),
+  LogConsoleMessage(ConsoleMessageLevelToLogSeverity(message_level), message,
+                    line_number, is_builtin_component, wrapper_->is_incognito(),
                     base::UTF8ToUTF16(source_url.spec()));
 
   observer_list_->Notify(
diff --git a/content/browser/service_worker/service_worker_context_core.h b/content/browser/service_worker/service_worker_context_core.h
index f2bb084..1ffb335 100644
--- a/content/browser/service_worker/service_worker_context_core.h
+++ b/content/browser/service_worker/service_worker_context_core.h
@@ -143,7 +143,7 @@
                        const GURL& source_url) override;
   void OnReportConsoleMessage(ServiceWorkerVersion* version,
                               int source_identifier,
-                              int message_level,
+                              blink::mojom::ConsoleMessageLevel message_level,
                               const base::string16& message,
                               int line_number,
                               const GURL& source_url) override;
diff --git a/content/browser/service_worker/service_worker_context_watcher.cc b/content/browser/service_worker/service_worker_context_watcher.cc
index 319a07b..820ffc2 100644
--- a/content/browser/service_worker/service_worker_context_watcher.cc
+++ b/content/browser/service_worker/service_worker_context_watcher.cc
@@ -15,6 +15,7 @@
 #include "content/public/browser/browser_thread.h"
 #include "content/public/browser/console_message.h"
 #include "content/public/common/console_message_level.h"
+#include "third_party/blink/public/mojom/devtools/console_message.mojom.h"
 #include "third_party/blink/public/mojom/service_worker/service_worker_object.mojom.h"
 #include "third_party/blink/public/mojom/service_worker/service_worker_registration.mojom.h"
 #include "url/gurl.h"
@@ -318,7 +319,7 @@
     int64_t version_id,
     const ConsoleMessage& message) {
   DCHECK_CURRENTLY_ON(BrowserThread::IO);
-  if (message.message_level != CONSOLE_MESSAGE_LEVEL_ERROR)
+  if (message.message_level != blink::mojom::ConsoleMessageLevel::kError)
     return;
   int64_t registration_id = blink::mojom::kInvalidServiceWorkerRegistrationId;
   auto it = version_info_map_.find(version_id);
diff --git a/content/browser/service_worker/service_worker_internals_ui.cc b/content/browser/service_worker/service_worker_internals_ui.cc
index 3b69bf1..e365ea5 100644
--- a/content/browser/service_worker/service_worker_internals_ui.cc
+++ b/content/browser/service_worker/service_worker_internals_ui.cc
@@ -288,7 +288,7 @@
     args.push_back(std::make_unique<Value>(base::NumberToString(version_id)));
     auto value = std::make_unique<DictionaryValue>();
     value->SetInteger("sourceIdentifier", message.source_identifier);
-    value->SetInteger("message_level", message.message_level);
+    value->SetInteger("message_level", static_cast<int>(message.message_level));
     value->SetString("message", message.message);
     value->SetInteger("lineNumber", message.line_number);
     value->SetString("sourceURL", message.source_url.spec());
diff --git a/content/browser/service_worker/service_worker_version.cc b/content/browser/service_worker/service_worker_version.cc
index 1ccdaf6a..56b725f 100644
--- a/content/browser/service_worker/service_worker_version.cc
+++ b/content/browser/service_worker/service_worker_version.cc
@@ -1056,11 +1056,12 @@
   }
 }
 
-void ServiceWorkerVersion::OnReportConsoleMessage(int source_identifier,
-                                                  int message_level,
-                                                  const base::string16& message,
-                                                  int line_number,
-                                                  const GURL& source_url) {
+void ServiceWorkerVersion::OnReportConsoleMessage(
+    int source_identifier,
+    blink::mojom::ConsoleMessageLevel message_level,
+    const base::string16& message,
+    int line_number,
+    const GURL& source_url) {
   for (auto& observer : observers_) {
     observer.OnReportConsoleMessage(this, source_identifier, message_level,
                                     message, line_number, source_url);
diff --git a/content/browser/service_worker/service_worker_version.h b/content/browser/service_worker/service_worker_version.h
index a4437b18..cd292a7 100644
--- a/content/browser/service_worker/service_worker_version.h
+++ b/content/browser/service_worker/service_worker_version.h
@@ -161,12 +161,13 @@
                                  int line_number,
                                  int column_number,
                                  const GURL& source_url) {}
-    virtual void OnReportConsoleMessage(ServiceWorkerVersion* version,
-                                        int source_identifier,
-                                        int message_level,
-                                        const base::string16& message,
-                                        int line_number,
-                                        const GURL& source_url) {}
+    virtual void OnReportConsoleMessage(
+        ServiceWorkerVersion* version,
+        int source_identifier,
+        blink::mojom::ConsoleMessageLevel message_level,
+        const base::string16& message,
+        int line_number,
+        const GURL& source_url) {}
     // OnControlleeAdded/Removed are called asynchronously. It is possible the
     // provider host identified by |client_uuid| was already destroyed when they
     // are called.
@@ -681,7 +682,7 @@
                          int column_number,
                          const GURL& source_url) override;
   void OnReportConsoleMessage(int source_identifier,
-                              int message_level,
+                              blink::mojom::ConsoleMessageLevel message_level,
                               const base::string16& message,
                               int line_number,
                               const GURL& source_url) override;
diff --git a/content/browser/site_per_process_browsertest.cc b/content/browser/site_per_process_browsertest.cc
index aa4e763..770cdea 100644
--- a/content/browser/site_per_process_browsertest.cc
+++ b/content/browser/site_per_process_browsertest.cc
@@ -479,29 +479,58 @@
 
 // Helper function to generate a feature policy for a single feature and a list
 // of origins. (Equivalent to the declared policy "feature origin1 origin2...".)
-// TODO(loonybear): Add a test for non-bool type PolicyValue.
-blink::ParsedFeaturePolicy CreateFPHeader(
+void SetParsedFeaturePolicyDeclaration(
+    blink::ParsedFeaturePolicyDeclaration* declaration,
     blink::mojom::FeaturePolicyFeature feature,
     const std::vector<GURL>& origins) {
-  blink::ParsedFeaturePolicy result(1);
-  result[0].feature = feature;
-  result[0].fallback_value = blink::PolicyValue(false);
-  result[0].opaque_value = blink::PolicyValue(false);
+  declaration->feature = feature;
+  blink::mojom::PolicyValueType feature_type =
+      blink::FeaturePolicy::GetDefaultFeatureList().at(feature).second;
+  declaration->fallback_value =
+      blink::PolicyValue::CreateMinPolicyValue(feature_type);
+  declaration->opaque_value = declaration->fallback_value;
+  if (feature == blink::mojom::FeaturePolicyFeature::kOversizedImages) {
+    declaration->fallback_value.SetDoubleValue(2.0);
+    declaration->opaque_value.SetDoubleValue(2.0);
+  }
   DCHECK(!origins.empty());
-  for (const GURL& origin : origins)
-    result[0].values.insert(std::pair<url::Origin, blink::PolicyValue>(
-        url::Origin::Create(origin), blink::PolicyValue(true)));
+  for (const auto origin : origins)
+    declaration->values.insert(std::pair<url::Origin, blink::PolicyValue>(
+        url::Origin::Create(origin),
+        blink::PolicyValue::CreateMaxPolicyValue(feature_type)));
+}
+
+blink::ParsedFeaturePolicy CreateFPHeader(
+    blink::mojom::FeaturePolicyFeature feature1,
+    blink::mojom::FeaturePolicyFeature feature2,
+    const std::vector<GURL>& origins) {
+  blink::ParsedFeaturePolicy result(2);
+  SetParsedFeaturePolicyDeclaration(&(result[0]), feature1, origins);
+  SetParsedFeaturePolicyDeclaration(&(result[1]), feature2, origins);
   return result;
 }
 
 // Helper function to generate a feature policy for a single feature which
-// matches every origin. (Equivalent to the declared policy "feature *".)
+// matches every origin. (Equivalent to the declared policy "feature1 *;
+// feature2 *".)
 blink::ParsedFeaturePolicy CreateFPHeaderMatchesAll(
-    blink::mojom::FeaturePolicyFeature feature) {
-  blink::ParsedFeaturePolicy result(1);
-  result[0].feature = feature;
-  result[0].fallback_value = blink::PolicyValue(true);
-  result[0].opaque_value = blink::PolicyValue(true);
+    blink::mojom::FeaturePolicyFeature feature1,
+    blink::mojom::FeaturePolicyFeature feature2) {
+  blink::ParsedFeaturePolicy result(2);
+  blink::mojom::PolicyValueType feature_type1 =
+      blink::FeaturePolicy::GetDefaultFeatureList().at(feature1).second;
+  blink::mojom::PolicyValueType feature_type2 =
+      blink::FeaturePolicy::GetDefaultFeatureList().at(feature2).second;
+  blink::PolicyValue max_value1 =
+      blink::PolicyValue::CreateMaxPolicyValue(feature_type1);
+  blink::PolicyValue max_value2 =
+      blink::PolicyValue::CreateMaxPolicyValue(feature_type2);
+  result[0].feature = feature1;
+  result[0].fallback_value = max_value1;
+  result[0].opaque_value = max_value1;
+  result[1].feature = feature2;
+  result[1].fallback_value = max_value2;
+  result[1].opaque_value = max_value2;
   return result;
 }
 
@@ -714,6 +743,21 @@
   }
 };
 
+// SitePerProcessFeaturePolicyBrowserTest
+
+class SitePerProcessFeaturePolicyBrowserTest
+    : public SitePerProcessBrowserTest {
+ public:
+  SitePerProcessFeaturePolicyBrowserTest() = default;
+
+  // Enable tests for parameterized features.
+  void SetUpCommandLine(base::CommandLine* command_line) override {
+    SitePerProcessBrowserTest::SetUpCommandLine(command_line);
+    command_line->AppendSwitchASCII("enable-blink-features",
+                                    "ExperimentalProductivityFeatures");
+  }
+};
+
 // SitePerProcessFeaturePolicyJavaScriptBrowserTest
 
 class SitePerProcessFeaturePolicyJavaScriptBrowserTest
@@ -724,8 +768,9 @@
   // Enable the feature policy JavaScript interface
   void SetUpCommandLine(base::CommandLine* command_line) override {
     SitePerProcessBrowserTest::SetUpCommandLine(command_line);
-    command_line->AppendSwitchASCII("enable-blink-features",
-                                    "FeaturePolicyJavaScriptInterface");
+    command_line->AppendSwitchASCII(
+        "enable-blink-features",
+        "FeaturePolicyJavaScriptInterface,ExperimentalProductivityFeatures");
   }
 };
 
@@ -8431,8 +8476,8 @@
   EXPECT_EQ(expected_title, title_watcher.WaitAndGetTitle());
 }
 
-IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest,
-                       TestFeaturePolicyReplicationOnSameOriginNavigation) {
+IN_PROC_BROWSER_TEST_F(SitePerProcessFeaturePolicyBrowserTest,
+                       TestPolicyReplicationOnSameOriginNavigation) {
   GURL start_url(
       embedded_test_server()->GetURL("a.com", "/feature-policy1.html"));
   GURL first_nav_url(
@@ -8443,6 +8488,7 @@
 
   FrameTreeNode* root = web_contents()->GetFrameTree()->root();
   EXPECT_EQ(CreateFPHeader(blink::mojom::FeaturePolicyFeature::kGeolocation,
+                           blink::mojom::FeaturePolicyFeature::kOversizedImages,
                            {start_url.GetOrigin()}),
             root->current_replication_state().feature_policy_header);
 
@@ -8450,7 +8496,8 @@
   // overwrite the old one.
   EXPECT_TRUE(NavigateToURL(shell(), first_nav_url));
   EXPECT_EQ(CreateFPHeaderMatchesAll(
-                blink::mojom::FeaturePolicyFeature::kGeolocation),
+                blink::mojom::FeaturePolicyFeature::kGeolocation,
+                blink::mojom::FeaturePolicyFeature::kOversizedImages),
             root->current_replication_state().feature_policy_header);
 
   // When the main frame navigates to a page without a policy, the replicated
@@ -8459,8 +8506,8 @@
   EXPECT_TRUE(root->current_replication_state().feature_policy_header.empty());
 }
 
-IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest,
-                       TestFeaturePolicyReplicationOnCrossOriginNavigation) {
+IN_PROC_BROWSER_TEST_F(SitePerProcessFeaturePolicyBrowserTest,
+                       TestPolicyReplicationOnCrossOriginNavigation) {
   GURL start_url(
       embedded_test_server()->GetURL("a.com", "/feature-policy1.html"));
   GURL first_nav_url(
@@ -8471,6 +8518,7 @@
 
   FrameTreeNode* root = web_contents()->GetFrameTree()->root();
   EXPECT_EQ(CreateFPHeader(blink::mojom::FeaturePolicyFeature::kGeolocation,
+                           blink::mojom::FeaturePolicyFeature::kOversizedImages,
                            {start_url.GetOrigin()}),
             root->current_replication_state().feature_policy_header);
 
@@ -8478,7 +8526,8 @@
   // overwrite the old one.
   EXPECT_TRUE(NavigateToURL(shell(), first_nav_url));
   EXPECT_EQ(CreateFPHeaderMatchesAll(
-                blink::mojom::FeaturePolicyFeature::kGeolocation),
+                blink::mojom::FeaturePolicyFeature::kGeolocation,
+                blink::mojom::FeaturePolicyFeature::kOversizedImages),
             root->current_replication_state().feature_policy_header);
 
   // When the main frame navigates to a page without a policy, the replicated
@@ -8489,8 +8538,8 @@
 
 // Test that the replicated feature policy header is correct in subframes as
 // they navigate.
-IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest,
-                       TestFeaturePolicyReplicationFromRemoteFrames) {
+IN_PROC_BROWSER_TEST_F(SitePerProcessFeaturePolicyBrowserTest,
+                       TestPolicyReplicationFromRemoteFrames) {
   GURL main_url(
       embedded_test_server()->GetURL("a.com", "/feature-policy-main.html"));
   GURL first_nav_url(
@@ -8501,11 +8550,13 @@
 
   FrameTreeNode* root = web_contents()->GetFrameTree()->root();
   EXPECT_EQ(CreateFPHeader(blink::mojom::FeaturePolicyFeature::kGeolocation,
+                           blink::mojom::FeaturePolicyFeature::kOversizedImages,
                            {main_url.GetOrigin(), GURL("http://example.com/")}),
             root->current_replication_state().feature_policy_header);
   EXPECT_EQ(1UL, root->child_count());
   EXPECT_EQ(
       CreateFPHeader(blink::mojom::FeaturePolicyFeature::kGeolocation,
+                     blink::mojom::FeaturePolicyFeature::kOversizedImages,
                      {main_url.GetOrigin()}),
       root->child_at(0)->current_replication_state().feature_policy_header);
 
@@ -8513,7 +8564,8 @@
   NavigateFrameToURL(root->child_at(0), first_nav_url);
   EXPECT_EQ(
       CreateFPHeaderMatchesAll(
-          blink::mojom::FeaturePolicyFeature::kGeolocation),
+          blink::mojom::FeaturePolicyFeature::kGeolocation,
+          blink::mojom::FeaturePolicyFeature::kOversizedImages),
       root->child_at(0)->current_replication_state().feature_policy_header);
 
   // Navigate the iframe to another location, this one with no policy header
@@ -8526,7 +8578,8 @@
   NavigateFrameToURL(root->child_at(0), first_nav_url);
   EXPECT_EQ(
       CreateFPHeaderMatchesAll(
-          blink::mojom::FeaturePolicyFeature::kGeolocation),
+          blink::mojom::FeaturePolicyFeature::kGeolocation,
+          blink::mojom::FeaturePolicyFeature::kOversizedImages),
       root->child_at(0)->current_replication_state().feature_policy_header);
 }
 
@@ -8556,7 +8609,8 @@
   NavigateFrameToURL(root->child_at(1), first_nav_url);
   EXPECT_EQ(
       CreateFPHeaderMatchesAll(
-          blink::mojom::FeaturePolicyFeature::kGeolocation),
+          blink::mojom::FeaturePolicyFeature::kGeolocation,
+          blink::mojom::FeaturePolicyFeature::kOversizedImages),
       root->child_at(1)->current_replication_state().feature_policy_header);
 
   EXPECT_EQ(1UL, root->child_at(1)->child_count());
@@ -8568,6 +8622,7 @@
   EXPECT_EQ(true,
             EvalJs(root->child_at(1)->child_at(0),
                    "document.featurePolicy.allowsFeature('geolocation')"));
+  // TODO(loonybear): Add JS test for parameterized features.
 
   // Now navigate the iframe to a page with no policy, and the same nested
   // cross-site iframe. The policy should be cleared in the proxy.
diff --git a/content/browser/tracing/background_tracing_manager_impl.cc b/content/browser/tracing/background_tracing_manager_impl.cc
index cd743a1..7e838b7 100644
--- a/content/browser/tracing/background_tracing_manager_impl.cc
+++ b/content/browser/tracing/background_tracing_manager_impl.cc
@@ -730,7 +730,7 @@
           "benchmark,toplevel,ipc,base,browser,navigation,omnibox,ui,shutdown,"
           "safe_browsing,Java,EarlyJava,loading,startup,mojom,renderer_host,"
           "disabled-by-default-system_stats,disabled-by-default-cpu_profiler,"
-          "dwrite,font_loader,font_service",
+          "dwrite,fonts",
           record_mode);
       // Filter only browser process events.
       base::trace_event::TraceConfig::ProcessFilterConfig process_config(
@@ -741,8 +741,7 @@
     case BackgroundTracingConfigImpl::CategoryPreset::BENCHMARK_RENDERERS:
       return TraceConfig(
           "benchmark,toplevel,ipc,base,ui,v8,renderer,blink,blink_gc,mojom,"
-          "latency,latencyInfo,renderer_host,cc,memory,dwrite,font_loader,"
-          "font_service,"
+          "latency,latencyInfo,renderer_host,cc,memory,dwrite,fonts,"
           "disabled-by-default-v8.gc,"
           "disabled-by-default-blink_gc,"
           "disabled-by-default-renderer.scheduler,"
diff --git a/content/browser/web_contents/web_contents_impl.cc b/content/browser/web_contents/web_contents_impl.cc
index 09c35a2b..28984b3 100644
--- a/content/browser/web_contents/web_contents_impl.cc
+++ b/content/browser/web_contents/web_contents_impl.cc
@@ -5067,6 +5067,8 @@
 void WebContentsImpl::LoadingStateChanged(bool to_different_document,
                                           bool due_to_interstitial,
                                           LoadNotificationDetails* details) {
+  if (is_being_destroyed_)
+    return;
   // Do not send notifications about loading changes in the FrameTree while the
   // interstitial page is pausing the throbber.
   if (ShowingInterstitialPage() && interstitial_page_->pause_throbber() &&
@@ -5792,6 +5794,8 @@
 }
 
 void WebContentsImpl::DidChangeLoadProgress() {
+  if (is_being_destroyed_)
+    return;
   double load_progress = frame_tree_.load_progress();
 
   // The delegate is notified immediately for the first and last updates. Also,
diff --git a/content/browser/web_contents/web_contents_ns_view_bridge.h b/content/browser/web_contents/web_contents_ns_view_bridge.h
index 09b460fe6..d2424f9 100644
--- a/content/browser/web_contents/web_contents_ns_view_bridge.h
+++ b/content/browser/web_contents/web_contents_ns_view_bridge.h
@@ -47,6 +47,10 @@
   void SetVisible(bool visible) override;
   void MakeFirstResponder() override;
   void TakeFocus(bool reverse) override;
+  void StartDrag(const DropData& drop_data,
+                 uint32_t operation_mask,
+                 const gfx::ImageSkia& image,
+                 const gfx::Vector2d& image_offset) override;
 
  private:
   base::scoped_nsobject<WebContentsViewCocoa> cocoa_view_;
diff --git a/content/browser/web_contents/web_contents_ns_view_bridge.mm b/content/browser/web_contents/web_contents_ns_view_bridge.mm
index ae91398..99b7e157 100644
--- a/content/browser/web_contents/web_contents_ns_view_bridge.mm
+++ b/content/browser/web_contents/web_contents_ns_view_bridge.mm
@@ -5,6 +5,8 @@
 #include "content/browser/web_contents/web_contents_ns_view_bridge.h"
 
 #import "content/browser/web_contents/web_contents_view_cocoa.h"
+#include "content/browser/web_contents/web_contents_view_mac.h"
+#include "ui/gfx/image/image_skia_util_mac.h"
 
 namespace content {
 
@@ -13,7 +15,7 @@
     mojom::WebContentsNSViewClientAssociatedPtr client)
     : client_(std::move(client)) {
   cocoa_view_.reset(
-      [[WebContentsViewCocoa alloc] initWithWebContentsViewMac:nullptr]);
+      [[WebContentsViewCocoa alloc] initWithViewsHostableView:nullptr]);
   [cocoa_view_ setClient:client_.get()];
   view_id_ =
       std::make_unique<ui::ScopedNSViewIdMapping>(view_id, cocoa_view_.get());
@@ -23,7 +25,8 @@
     uint64_t view_id,
     WebContentsViewMac* web_contents_view) {
   cocoa_view_.reset([[WebContentsViewCocoa alloc]
-      initWithWebContentsViewMac:web_contents_view]);
+      initWithViewsHostableView:web_contents_view]);
+  [cocoa_view_ setClient:web_contents_view];
   view_id_ =
       std::make_unique<ui::ScopedNSViewIdMapping>(view_id, cocoa_view_.get());
 }
@@ -33,7 +36,8 @@
   // while the user was operating a UI control which resulted in a
   // close.  In that case, the Cocoa view outlives the
   // WebContentsViewMac instance due to Cocoa retain count.
-  [cocoa_view_ clearWebContentsView];
+  [cocoa_view_ setClient:nullptr];
+  [cocoa_view_ clearViewsHostableView];
   [cocoa_view_ removeFromSuperview];
 }
 
@@ -82,4 +86,16 @@
     [[cocoa_view_ window] selectNextKeyView:cocoa_view_];
 }
 
+void WebContentsNSViewBridge::StartDrag(const DropData& drop_data,
+                                        uint32_t operation_mask,
+                                        const gfx::ImageSkia& image,
+                                        const gfx::Vector2d& image_offset) {
+  NSPoint offset = NSPointFromCGPoint(
+      gfx::PointAtOffsetFromOrigin(image_offset).ToCGPoint());
+  [cocoa_view_ startDragWithDropData:drop_data
+                   dragOperationMask:operation_mask
+                               image:gfx::NSImageFromImageSkia(image)
+                              offset:offset];
+}
+
 }  // namespace content
diff --git a/content/browser/web_contents/web_contents_view_cocoa.h b/content/browser/web_contents/web_contents_view_cocoa.h
index 51a22dd0..ebba3e62 100644
--- a/content/browser/web_contents/web_contents_view_cocoa.h
+++ b/content/browser/web_contents/web_contents_view_cocoa.h
@@ -12,9 +12,6 @@
 
 namespace content {
 struct DropData;
-class RenderWidgetHostImpl;
-class WebContentsImpl;
-class WebContentsViewMac;
 namespace mojom {
 class WebContentsNSViewClient;
 }  // namespace mojom
@@ -25,18 +22,22 @@
 CONTENT_EXPORT
 @interface WebContentsViewCocoa : BaseView <ViewsHostable> {
  @private
-  // Instances of this class are owned by both webContentsView_ and AppKit. It
-  // is possible for an instance to outlive its webContentsView_. The
-  // webContentsView_ must call -clearWebContentsView in its destructor.
-  content::WebContentsViewMac* webContentsView_;
+  // Instances of this class are owned by both client_ and AppKit. It is
+  // possible for an instance to outlive its webContentsView_. The client_ must
+  // call -clearClientAndView in its destructor.
   content::mojom::WebContentsNSViewClient* client_;
+
+  // The interface exported to views::Views that embed this as a sub-view.
+  ui::ViewsHostableView* viewsHostableView_;
+
   base::scoped_nsobject<WebDragSource> dragSource_;
   base::scoped_nsobject<id> accessibilityParent_;
   BOOL mouseDownCanMoveWindow_;
 }
 
-// The mojo interface through which to communicate with the browser process.
-@property(nonatomic, assign) content::mojom::WebContentsNSViewClient* client;
+// Set or un-set the mojo interface through which to communicate with the
+// browser process.
+- (void)setClient:(content::mojom::WebContentsNSViewClient*)client;
 
 - (void)setMouseDownCanMoveWindow:(BOOL)canMove;
 
@@ -53,17 +54,15 @@
 
 // Private interface.
 // TODO(ccameron): Document these functions.
-- (id)initWithWebContentsViewMac:(content::WebContentsViewMac*)w;
+- (id)initWithViewsHostableView:(ui::ViewsHostableView*)v;
 - (void)registerDragTypes;
 - (void)startDragWithDropData:(const content::DropData&)dropData
-                    sourceRWH:(content::RenderWidgetHostImpl*)sourceRWH
             dragOperationMask:(NSDragOperation)operationMask
                         image:(NSImage*)image
                        offset:(NSPoint)offset;
-- (void)clearWebContentsView;
+- (void)clearViewsHostableView;
 - (void)updateWebContentsVisibility;
 - (void)viewDidBecomeFirstResponder:(NSNotification*)notification;
-- (content::WebContentsImpl*)webContents;
 @end
 
 #endif  // CONTENT_BROWSER_WEB_CONTENTS_WEB_CONTENTS_VIEW_COCOA_H_
diff --git a/content/browser/web_contents/web_contents_view_cocoa.mm b/content/browser/web_contents/web_contents_view_cocoa.mm
index 9160ced4..5fdace19 100644
--- a/content/browser/web_contents/web_contents_view_cocoa.mm
+++ b/content/browser/web_contents/web_contents_view_cocoa.mm
@@ -6,12 +6,8 @@
 
 #import "base/mac/mac_util.h"
 #include "base/mac/sdk_forward_declarations.h"
-#include "content/browser/web_contents/web_contents_impl.h"
-#include "content/browser/web_contents/web_contents_view_mac.h"
 #import "content/browser/web_contents/web_drag_dest_mac.h"
 #import "content/browser/web_contents/web_drag_source_mac.h"
-#include "content/public/browser/web_contents_delegate.h"
-#include "content/public/browser/web_contents_view_delegate.h"
 #include "content/public/common/web_contents_ns_view_bridge.mojom.h"
 #import "third_party/mozilla/NSPasteboard+Utils.h"
 #include "ui/base/clipboard/clipboard_constants.h"
@@ -21,20 +17,16 @@
 
 using content::mojom::DraggingInfo;
 using content::DropData;
-using content::WebContentsImpl;
-using content::WebContentsViewMac;
 
 ////////////////////////////////////////////////////////////////////////////////
 // WebContentsViewCocoa
 
 @implementation WebContentsViewCocoa
 
-@synthesize client = client_;
-
-- (id)initWithWebContentsViewMac:(WebContentsViewMac*)w {
+- (id)initWithViewsHostableView:(ui::ViewsHostableView*)v {
   self = [super initWithFrame:NSZeroRect];
   if (self != nil) {
-    webContentsView_ = w;
+    viewsHostableView_ = v;
     [self registerDragTypes];
 
     [[NSNotificationCenter defaultCenter]
@@ -99,12 +91,6 @@
   [self registerForDraggedTypes:types];
 }
 
-- (WebContentsImpl*)webContents {
-  if (!webContentsView_)
-    return nullptr;
-  return webContentsView_->web_contents();
-}
-
 - (void)mouseEvent:(NSEvent*)theEvent {
   if (!client_)
     return;
@@ -131,17 +117,15 @@
 }
 
 - (void)startDragWithDropData:(const DropData&)dropData
-                    sourceRWH:(content::RenderWidgetHostImpl*)sourceRWH
             dragOperationMask:(NSDragOperation)operationMask
                         image:(NSImage*)image
                        offset:(NSPoint)offset {
-  if (![self webContents])
+  if (!client_)
     return;
   dragSource_.reset([[WebDragSource alloc]
-       initWithContents:[self webContents]
+         initWithClient:client_
                    view:self
                dropData:&dropData
-              sourceRWH:sourceRWH
                   image:image
                  offset:offset
              pasteboard:[NSPasteboard pasteboardWithName:NSDragPboard]
@@ -232,10 +216,14 @@
   return result;
 }
 
-- (void)clearWebContentsView {
-  webContentsView_ = nullptr;
-  client_ = nullptr;
-  [dragSource_ clearWebContentsView];
+- (void)clearViewsHostableView {
+  viewsHostableView_ = nullptr;
+}
+
+- (void)setClient:(content::mojom::WebContentsNSViewClient*)client {
+  if (!client)
+    [dragSource_ clearClientAndWebContentsView];
+  client_ = client;
 }
 
 - (void)viewDidBecomeFirstResponder:(NSNotification*)notification {
@@ -337,7 +325,7 @@
 
 // ViewsHostable protocol implementation.
 - (ui::ViewsHostableView*)viewsHostableView {
-  return webContentsView_;
+  return viewsHostableView_;
 }
 
 // NSAccessibility informal protocol implementation.
diff --git a/content/browser/web_contents/web_contents_view_mac.h b/content/browser/web_contents/web_contents_view_mac.h
index 8e928b3..be73ba6 100644
--- a/content/browser/web_contents/web_contents_view_mac.h
+++ b/content/browser/web_contents/web_contents_view_mac.h
@@ -147,6 +147,13 @@
                        uint32_t* out_result) override;
   bool PerformDragOperation(mojom::DraggingInfoPtr dragging_info,
                             bool* out_result) override;
+  bool DragPromisedFileTo(const base::FilePath& file_path,
+                          const DropData& drop_data,
+                          const GURL& download_url,
+                          base::FilePath* out_file_path) override;
+  void EndDrag(uint32_t drag_opeation,
+               const gfx::PointF& local_point,
+               const gfx::PointF& screen_point) override;
 
   // mojom::WebContentsNSViewClient, synchronous methods:
   void DraggingEntered(mojom::DraggingInfoPtr dragging_info,
@@ -155,6 +162,10 @@
                        DraggingUpdatedCallback callback) override;
   void PerformDragOperation(mojom::DraggingInfoPtr dragging_info,
                             PerformDragOperationCallback callback) override;
+  void DragPromisedFileTo(const base::FilePath& file_path,
+                          const DropData& drop_data,
+                          const GURL& download_url,
+                          DragPromisedFileToCallback callback) override;
 
   // Return the list of child RenderWidgetHostViewMacs. This will remove any
   // destroyed instances before returning.
@@ -166,6 +177,9 @@
   // Destination for drag-drop.
   base::scoped_nsobject<WebDragDest> drag_dest_;
 
+  // Tracks the RenderWidgetHost where the current drag started.
+  base::WeakPtr<content::RenderWidgetHostImpl> drag_source_start_rwh_;
+
   // Our optional delegate.
   std::unique_ptr<WebContentsViewDelegate> delegate_;
 
diff --git a/content/browser/web_contents/web_contents_view_mac.mm b/content/browser/web_contents/web_contents_view_mac.mm
index 70e3362..92ac389 100644
--- a/content/browser/web_contents/web_contents_view_mac.mm
+++ b/content/browser/web_contents/web_contents_view_mac.mm
@@ -13,6 +13,9 @@
 #include "base/mac/sdk_forward_declarations.h"
 #include "base/message_loop/message_loop_current.h"
 #import "base/message_loop/message_pump_mac.h"
+#include "base/threading/thread_restrictions.h"
+#include "content/browser/download/drag_download_file.h"
+#include "content/browser/download/drag_download_util.h"
 #include "content/browser/frame_host/popup_menu_helper_mac.h"
 #include "content/browser/renderer_host/display_util.h"
 #include "content/browser/renderer_host/render_view_host_factory.h"
@@ -30,7 +33,6 @@
 #include "mojo/public/cpp/bindings/interface_request.h"
 #include "ui/base/cocoa/cocoa_base_utils.h"
 #include "ui/base/cocoa/ns_view_ids.h"
-#include "ui/gfx/image/image_skia_util_mac.h"
 #include "ui/gfx/mac/coordinate_conversion.h"
 
 using blink::WebDragOperation;
@@ -51,9 +53,17 @@
 STATIC_ASSERT_ENUM(NSDragOperationEvery, blink::kWebDragOperationEvery);
 
 namespace content {
-
 namespace {
 
+// This helper's sole task is to write out data for a promised file; the caller
+// is responsible for opening the file. It takes the drop data and an open file
+// stream.
+void PromiseWriterHelper(const DropData& drop_data, base::File file) {
+  DCHECK(file.IsValid());
+  file.WriteAtCurrentPos(drop_data.file_contents.data(),
+                         drop_data.file_contents.length());
+}
+
 WebContentsViewMac::RenderWidgetHostViewCreateFunction
     g_create_render_widget_host_view = nullptr;
 
@@ -143,14 +153,17 @@
   base::MessageLoopCurrent::ScopedNestableTaskAllower allow;
   NSDragOperation mask = static_cast<NSDragOperation>(allowed_operations) &
                          ~NSDragOperationGeneric;
-  NSPoint offset = NSPointFromCGPoint(
-      gfx::PointAtOffsetFromOrigin(image_offset).ToCGPoint());
   [drag_dest_ setDragStartTrackersForProcess:source_rwh->GetProcess()->GetID()];
-  [cocoa_view() startDragWithDropData:drop_data
-                            sourceRWH:source_rwh
-                    dragOperationMask:mask
-                                image:gfx::NSImageFromImageSkia(image)
-                               offset:offset];
+  drag_source_start_rwh_ = source_rwh->GetWeakPtr();
+
+  if (ns_view_bridge_remote_) {
+    // TODO(https://crbug.com/898608): Non-trivial gfx::ImageSkias fail to
+    // serialize.
+    ns_view_bridge_remote_->StartDrag(drop_data, mask, gfx::ImageSkia(),
+                                      image_offset);
+  } else {
+    ns_view_bridge_local_->StartDrag(drop_data, mask, image, image_offset);
+  }
 }
 
 void WebContentsViewMac::SizeContents(const gfx::Size& size) {
@@ -299,7 +312,6 @@
     const gfx::Size& initial_size, gfx::NativeView context) {
   ns_view_bridge_local_ =
       std::make_unique<WebContentsNSViewBridge>(ns_view_id_, this);
-  [cocoa_view() setClient:this];
 
   drag_dest_.reset([[WebDragDest alloc] initWithWebContentsImpl:web_contents_]);
   if (delegate_)
@@ -502,6 +514,75 @@
   return true;
 }
 
+bool WebContentsViewMac::DragPromisedFileTo(const base::FilePath& file_path,
+                                            const DropData& drop_data,
+                                            const GURL& download_url,
+                                            base::FilePath* out_file_path) {
+  *out_file_path = file_path;
+  // This is called by -namesOfPromisedFilesDroppedAtDestination, which is
+  // requesting, on the UI thread, the name of the file that will be written
+  // by a drag operation. To know the name of this file, it is necessary to
+  // query the filesystem before returning, which will block the UI thread.
+  base::ScopedAllowBlocking allow_blocking;
+  base::File file(content::CreateFileForDrop(out_file_path));
+  if (!file.IsValid()) {
+    *out_file_path = base::FilePath();
+    return true;
+  }
+
+  if (download_url.is_valid() && web_contents_) {
+    scoped_refptr<DragDownloadFile> drag_file_downloader(new DragDownloadFile(
+        *out_file_path, std::move(file), download_url,
+        content::Referrer(web_contents_->GetLastCommittedURL(),
+                          drop_data.referrer_policy),
+        web_contents_->GetEncoding(), web_contents_));
+
+    // The finalizer will take care of closing and deletion.
+    drag_file_downloader->Start(
+        new PromiseFileFinalizer(drag_file_downloader.get()));
+  } else {
+    // The writer will take care of closing and deletion.
+    base::PostTaskWithTraits(
+        FROM_HERE, {base::TaskPriority::USER_VISIBLE, base::MayBlock()},
+        base::BindOnce(&PromiseWriterHelper, drop_data, std::move(file)));
+  }
+
+  // The DragDownloadFile constructor may have altered the value of
+  // |*out_file_path| if, say, an existing file at the drop site has the same
+  // name. Return the actual name that was used to write the file.
+  *out_file_path = file_path;
+  return true;
+}
+
+void WebContentsViewMac::EndDrag(uint32_t drag_operation,
+                                 const gfx::PointF& local_point,
+                                 const gfx::PointF& screen_point) {
+  web_contents_->SystemDragEnded(drag_source_start_rwh_.get());
+
+  // |localPoint| and |screenPoint| are in the root coordinate space, for
+  // non-root RenderWidgetHosts they need to be transformed.
+  gfx::PointF transformed_point = local_point;
+  gfx::PointF transformed_screen_point = screen_point;
+  if (drag_source_start_rwh_ && web_contents_->GetRenderWidgetHostView()) {
+    content::RenderWidgetHostViewBase* contentsViewBase =
+        static_cast<content::RenderWidgetHostViewBase*>(
+            web_contents_->GetRenderWidgetHostView());
+    content::RenderWidgetHostViewBase* dragStartViewBase =
+        static_cast<content::RenderWidgetHostViewBase*>(
+            drag_source_start_rwh_->GetView());
+    contentsViewBase->TransformPointToCoordSpaceForView(
+        local_point, dragStartViewBase, &transformed_point);
+    contentsViewBase->TransformPointToCoordSpaceForView(
+        screen_point, dragStartViewBase, &transformed_screen_point);
+  }
+
+  web_contents_->DragSourceEndedAt(
+      transformed_point.x(), transformed_point.y(),
+      transformed_screen_point.x(), transformed_screen_point.y(),
+      static_cast<blink::WebDragOperation>(drag_operation),
+      drag_source_start_rwh_.get());
+}
+
 void WebContentsViewMac::DraggingEntered(mojom::DraggingInfoPtr dragging_info,
                                          DraggingEnteredCallback callback) {
   uint32_t result = 0;
@@ -524,6 +605,16 @@
   std::move(callback).Run(result);
 }
 
+void WebContentsViewMac::DragPromisedFileTo(
+    const base::FilePath& file_path,
+    const DropData& drop_data,
+    const GURL& download_url,
+    DragPromisedFileToCallback callback) {
+  base::FilePath actual_file_path;
+  DragPromisedFileTo(file_path, drop_data, download_url, &actual_file_path);
+  std::move(callback).Run(actual_file_path);
+}
+
 ////////////////////////////////////////////////////////////////////////////////
 // WebContentsViewMac, ViewsHostableView:
 
diff --git a/content/browser/web_contents/web_drag_source_mac.h b/content/browser/web_contents/web_drag_source_mac.h
index a488e75..169686fc7 100644
--- a/content/browser/web_contents/web_drag_source_mac.h
+++ b/content/browser/web_contents/web_drag_source_mac.h
@@ -17,20 +17,20 @@
 #include "url/gurl.h"
 
 namespace content {
-class RenderWidgetHostImpl;
-class WebContentsImpl;
 struct DropData;
-}
+namespace mojom {
+class WebContentsNSViewClient;
+}  // namespace mojom
+}  // namespace content
 
 // A class that handles tracking and event processing for a drag and drop
 // originating from the content area.
 CONTENT_EXPORT
 @interface WebDragSource : NSObject {
  @private
-  // Our contents. Weak reference (owns or co-owns us).
-  // An instance of this class may outlive |contents_|. The destructor of
-  // |contents_| must set this ivar to |nullptr|.
-  content::WebContentsImpl* contents_;
+  // The client through which to communicate with the WebContentsImpl. Owns
+  // |self| and resets |client_| via clearClientAndWebContentsView.
+  content::mojom::WebContentsNSViewClient* client_;
 
   // The view from which the drag was initiated. Weak reference.
   // An instance of this class may outlive |contentsView_|. The destructor of
@@ -60,25 +60,21 @@
 
   // The file UTI associated with the file drag, if any.
   base::ScopedCFTypeRef<CFStringRef> fileUTI_;
-
-  // Tracks the RenderWidgetHost where the current drag started.
-  base::WeakPtr<content::RenderWidgetHostImpl> dragStartRWH_;
 }
 
 // Initialize a WebDragSource object for a drag (originating on the given
 // contentsView and with the given dropData and pboard). Fill the pasteboard
 // with data types appropriate for dropData.
-- (id)initWithContents:(content::WebContentsImpl*)contents
-                  view:(NSView*)contentsView
-              dropData:(const content::DropData*)dropData
-             sourceRWH:(content::RenderWidgetHostImpl*)sourceRWH
-                 image:(NSImage*)image
-                offset:(NSPoint)offset
-            pasteboard:(NSPasteboard*)pboard
-     dragOperationMask:(NSDragOperation)dragOperationMask;
+- (id)initWithClient:(content::mojom::WebContentsNSViewClient*)client
+                view:(NSView*)contentsView
+            dropData:(const content::DropData*)dropData
+               image:(NSImage*)image
+              offset:(NSPoint)offset
+          pasteboard:(NSPasteboard*)pboard
+   dragOperationMask:(NSDragOperation)dragOperationMask;
 
 // Call when the web contents is gone.
-- (void)clearWebContentsView;
+- (void)clearClientAndWebContentsView;
 
 // Returns a mask of the allowed drag operations.
 - (NSDragOperation)draggingSourceOperationMaskForLocal:(BOOL)isLocal;
diff --git a/content/browser/web_contents/web_drag_source_mac.mm b/content/browser/web_contents/web_drag_source_mac.mm
index 0435002..8c75ab5 100644
--- a/content/browser/web_contents/web_drag_source_mac.mm
+++ b/content/browser/web_contents/web_drag_source_mac.mm
@@ -16,18 +16,12 @@
 #include "base/strings/string_util.h"
 #include "base/strings/sys_string_conversions.h"
 #include "base/strings/utf_string_conversions.h"
-#include "base/task/post_task.h"
-#include "base/threading/thread.h"
-#include "base/threading/thread_restrictions.h"
 #include "content/browser/download/drag_download_file.h"
 #include "content/browser/download/drag_download_util.h"
-#include "content/browser/renderer_host/render_view_host_impl.h"
-#include "content/browser/renderer_host/render_widget_host_impl.h"
-#include "content/browser/renderer_host/render_widget_host_view_base.h"
-#include "content/browser/web_contents/web_contents_impl.h"
 #include "content/public/browser/content_browser_client.h"
 #include "content/public/common/content_client.h"
 #include "content/public/common/drop_data.h"
+#include "content/public/common/web_contents_ns_view_bridge.mojom.h"
 #include "net/base/escape.h"
 #include "net/base/filename_util.h"
 #include "net/base/mime_util.h"
@@ -43,26 +37,7 @@
 using base::SysNSStringToUTF8;
 using base::SysUTF8ToNSString;
 using base::SysUTF16ToNSString;
-using content::BrowserThread;
-using content::DragDownloadFile;
 using content::DropData;
-using content::PromiseFileFinalizer;
-using content::RenderViewHostImpl;
-
-namespace {
-
-// This helper's sole task is to write out data for a promised file; the caller
-// is responsible for opening the file. It takes the drop data and an open file
-// stream.
-void PromiseWriterHelper(const DropData& drop_data,
-                         base::File file) {
-  DCHECK(file.IsValid());
-  file.WriteAtCurrentPos(drop_data.file_contents.data(),
-                         drop_data.file_contents.length());
-}
-
-}  // namespace
-
 
 @interface WebDragSource(Private)
 
@@ -74,17 +49,15 @@
 
 @implementation WebDragSource
 
-- (id)initWithContents:(content::WebContentsImpl*)contents
-                  view:(NSView*)contentsView
-              dropData:(const DropData*)dropData
-             sourceRWH:(content::RenderWidgetHostImpl*)sourceRWH
-                 image:(NSImage*)image
-                offset:(NSPoint)offset
-            pasteboard:(NSPasteboard*)pboard
-     dragOperationMask:(NSDragOperation)dragOperationMask {
+- (id)initWithClient:(content::mojom::WebContentsNSViewClient*)client
+                view:(NSView*)contentsView
+            dropData:(const DropData*)dropData
+               image:(NSImage*)image
+              offset:(NSPoint)offset
+          pasteboard:(NSPasteboard*)pboard
+   dragOperationMask:(NSDragOperation)dragOperationMask {
   if ((self = [super init])) {
-    contents_ = contents;
-    DCHECK(contents_);
+    client_ = client;
 
     contentsView_ = contentsView;
     DCHECK(contentsView_);
@@ -92,7 +65,6 @@
     dropData_.reset(new DropData(*dropData));
     DCHECK(dropData_.get());
 
-    dragStartRWH_ = sourceRWH->GetWeakPtr();
     dragImage_.reset([image retain]);
     imageOffset_ = offset;
 
@@ -107,8 +79,8 @@
   return self;
 }
 
-- (void)clearWebContentsView {
-  contents_ = nil;
+- (void)clearClientAndWebContentsView {
+  client_ = nullptr;
   contentsView_ = nil;
 }
 
@@ -235,11 +207,9 @@
 
 - (void)endDragAt:(NSPoint)screenPoint
         operation:(NSDragOperation)operation {
-  if (!contents_ || !contentsView_)
+  if (!client_ || !contentsView_)
     return;
 
-  contents_->SystemDragEnded(dragStartRWH_.get());
-
   if (dragImage_) {
     screenPoint.x += imageOffset_.x;
     // Deal with Cocoa's flipped coordinate system.
@@ -251,10 +221,8 @@
   if ([contentsView_ window])
     localPoint = [self convertScreenPoint:screenPoint];
   NSRect viewFrame = [contentsView_ frame];
-  localPoint.y = viewFrame.size.height - localPoint.y;
   // Flip |screenPoint|.
   NSRect screenFrame = [[[contentsView_ window] screen] frame];
-  screenPoint.y = screenFrame.size.height - screenPoint.y;
 
   // If AppKit returns a copy and move operation, mask off the move bit
   // because WebCore does not understand what it means to do both, which
@@ -262,73 +230,26 @@
   if (operation == (NSDragOperationMove | NSDragOperationCopy))
     operation &= ~NSDragOperationMove;
 
-  // |localPoint| and |screenPoint| are in the root coordinate space, for
-  // non-root RenderWidgetHosts they need to be transformed.
-  gfx::PointF transformedPoint = gfx::PointF(localPoint.x, localPoint.y);
-  gfx::PointF transformedScreenPoint =
-      gfx::PointF(screenPoint.x, screenPoint.y);
-  if (dragStartRWH_ && contents_->GetRenderWidgetHostView()) {
-    content::RenderWidgetHostViewBase* contentsViewBase =
-        static_cast<content::RenderWidgetHostViewBase*>(
-            contents_->GetRenderWidgetHostView());
-    content::RenderWidgetHostViewBase* dragStartViewBase =
-        static_cast<content::RenderWidgetHostViewBase*>(
-            dragStartRWH_->GetView());
-    contentsViewBase->TransformPointToCoordSpaceForView(
-        gfx::PointF(localPoint.x, localPoint.y), dragStartViewBase,
-        &transformedPoint);
-    contentsViewBase->TransformPointToCoordSpaceForView(
-        gfx::PointF(screenPoint.x, screenPoint.y), dragStartViewBase,
-        &transformedScreenPoint);
-  }
-
-  contents_->DragSourceEndedAt(
-      transformedPoint.x(), transformedPoint.y(), transformedScreenPoint.x(),
-      transformedScreenPoint.y(),
-      static_cast<blink::WebDragOperation>(operation), dragStartRWH_.get());
+  client_->EndDrag(
+      operation,
+      gfx::PointF(localPoint.x, viewFrame.size.height - localPoint.y),
+      gfx::PointF(screenPoint.x, screenFrame.size.height - screenPoint.y));
 
   // Make sure the pasteboard owner isn't us.
   [pasteboard_ declareTypes:[NSArray array] owner:nil];
 }
 
 - (NSString*)dragPromisedFileTo:(NSString*)path {
+  if (!client_)
+    return nil;
   // Be extra paranoid; avoid crashing.
   if (!dropData_) {
     NOTREACHED() << "No drag-and-drop data available for promised file.";
     return nil;
   }
-
   base::FilePath filePath(SysNSStringToUTF8(path));
   filePath = filePath.Append(downloadFileName_);
-
-  // CreateFileForDrop() will call base::PathExists(),
-  // which is blocking.  Since this operation is already blocking the
-  // UI thread on OSX, it should be reasonable to let it happen.
-  base::ThreadRestrictions::ScopedAllowIO allowIO;
-  base::File file(content::CreateFileForDrop(&filePath));
-  if (!file.IsValid())
-    return nil;
-
-  if (downloadURL_.is_valid() && contents_) {
-    scoped_refptr<DragDownloadFile> dragFileDownloader(
-        new DragDownloadFile(filePath, std::move(file), downloadURL_,
-                             content::Referrer(contents_->GetLastCommittedURL(),
-                                               dropData_->referrer_policy),
-                             contents_->GetEncoding(), contents_));
-
-    // The finalizer will take care of closing and deletion.
-    dragFileDownloader->Start(new PromiseFileFinalizer(
-        dragFileDownloader.get()));
-  } else {
-    // The writer will take care of closing and deletion.
-    base::PostTaskWithTraits(
-        FROM_HERE, {base::TaskPriority::USER_VISIBLE, base::MayBlock()},
-        base::BindOnce(&PromiseWriterHelper, *dropData_, std::move(file)));
-  }
-
-  // The DragDownloadFile constructor may have altered the value of |filePath|
-  // if, say, an existing file at the drop site has the same name. Return the
-  // actual name that was used to write the file.
+  client_->DragPromisedFileTo(filePath, *dropData_, downloadURL_, &filePath);
   return SysUTF8ToNSString(filePath.BaseName().value());
 }
 
@@ -359,6 +280,8 @@
   // File.
   if (!dropData_->file_contents.empty() ||
       !dropData_->download_metadata.empty()) {
+    // TODO(https://crbug.com/898608): The |downloadFileName_| and
+    // |downloadURL_| values should be computed by the caller.
     if (dropData_->download_metadata.empty()) {
       base::Optional<base::FilePath> suggestedFilename =
           dropData_->GetSafeFilenameForImageFileContents();
diff --git a/content/browser/web_contents/web_drag_source_mac_unittest.mm b/content/browser/web_contents/web_drag_source_mac_unittest.mm
index 29083eca..fe42657 100644
--- a/content/browser/web_contents/web_drag_source_mac_unittest.mm
+++ b/content/browser/web_contents/web_drag_source_mac_unittest.mm
@@ -24,13 +24,11 @@
   std::unique_ptr<DropData> dropData(new DropData);
   dropData->url = GURL("javascript:%");
 
-  WebContentsImpl* contentsImpl = static_cast<WebContentsImpl*>(contents.get());
   scoped_refptr<ui::UniquePasteboard> pasteboard1 = new ui::UniquePasteboard;
   base::scoped_nsobject<WebDragSource> source([[WebDragSource alloc]
-       initWithContents:contentsImpl
+         initWithClient:nullptr
                    view:view
                dropData:dropData.get()
-              sourceRWH:contentsImpl->GetRenderViewHost()->GetWidget()
                   image:nil
                  offset:NSZeroPoint
              pasteboard:pasteboard1->get()
diff --git a/content/browser/web_package/signed_exchange_handler.cc b/content/browser/web_package/signed_exchange_handler.cc
index bac0e279..044820f 100644
--- a/content/browser/web_package/signed_exchange_handler.cc
+++ b/content/browser/web_package/signed_exchange_handler.cc
@@ -309,6 +309,7 @@
   }
 
   header_read_buf_->DidConsume(read_result);
+  exchange_header_length_ += read_result;
   if (header_read_buf_->BytesRemaining() == 0) {
     SignedExchangeLoadResult result = SignedExchangeLoadResult::kSuccess;
     switch (state_) {
diff --git a/content/browser/web_package/signed_exchange_handler.h b/content/browser/web_package/signed_exchange_handler.h
index b046d17..5f61b93 100644
--- a/content/browser/web_package/signed_exchange_handler.h
+++ b/content/browser/web_package/signed_exchange_handler.h
@@ -99,6 +99,8 @@
       base::RepeatingCallback<int(void)> frame_tree_node_id_getter);
   ~SignedExchangeHandler();
 
+  int64_t GetExchangeHeaderLength() const { return exchange_header_length_; }
+
  protected:
   SignedExchangeHandler();
 
@@ -143,6 +145,7 @@
   scoped_refptr<net::IOBuffer> header_buf_;
   // Wrapper around |header_buf_| to progressively read fixed-size data.
   scoped_refptr<net::DrainableIOBuffer> header_read_buf_;
+  int64_t exchange_header_length_ = 0;
 
   signed_exchange_prologue::BeforeFallbackUrl prologue_before_fallback_url_;
   signed_exchange_prologue::FallbackUrlAndAfter
diff --git a/content/browser/web_package/signed_exchange_loader.cc b/content/browser/web_package/signed_exchange_loader.cc
index 71d9d62..86d0026e9 100644
--- a/content/browser/web_package/signed_exchange_loader.cc
+++ b/content/browser/web_package/signed_exchange_loader.cc
@@ -237,8 +237,10 @@
 
 void SignedExchangeLoader::OnComplete(
     const network::URLLoaderCompletionStatus& status) {
-  DCHECK(!encoded_data_length_);
-  encoded_data_length_ = status.encoded_data_length;
+  DCHECK(!outer_response_length_info_);
+  outer_response_length_info_ = OuterResponseLengthInfo();
+  outer_response_length_info_->encoded_data_length = status.encoded_data_length;
+  outer_response_length_info_->decoded_body_length = status.decoded_body_length;
   NotifyClientOnCompleteIfReady();
 }
 
@@ -364,21 +366,23 @@
 }
 
 void SignedExchangeLoader::NotifyClientOnCompleteIfReady() {
-  // If |encoded_data_length_| or |decoded_body_read_result_| is unavailable, do
-  // nothing and rely on the subsequent call to notify client.
-  if (!encoded_data_length_ || !decoded_body_read_result_)
+  // If |outer_response_length_info_| or |decoded_body_read_result_| is
+  // unavailable, do nothing and rely on the subsequent call to notify client.
+  if (!outer_response_length_info_ || !decoded_body_read_result_)
     return;
 
   ReportLoadResult(*decoded_body_read_result_ == net::OK
                        ? SignedExchangeLoadResult::kSuccess
                        : SignedExchangeLoadResult::kMerkleIntegrityError);
 
-  // TODO(https://crbug.com/803774): Fill the data length information (
-  // encoded_body_length, decoded_body_length) too.
   network::URLLoaderCompletionStatus status;
   status.error_code = *decoded_body_read_result_;
   status.completion_time = base::TimeTicks::Now();
-  status.encoded_data_length = *encoded_data_length_;
+  status.encoded_data_length = outer_response_length_info_->encoded_data_length;
+  status.encoded_body_length =
+      outer_response_length_info_->decoded_body_length -
+      signed_exchange_handler_->GetExchangeHeaderLength();
+  status.decoded_body_length = body_data_pipe_adapter_->TransferredBytes();
 
   if (ssl_info_) {
     DCHECK((url_loader_options_ &
diff --git a/content/browser/web_package/signed_exchange_loader.h b/content/browser/web_package/signed_exchange_loader.h
index fba35c82..e27140f 100644
--- a/content/browser/web_package/signed_exchange_loader.h
+++ b/content/browser/web_package/signed_exchange_loader.h
@@ -169,8 +169,13 @@
   base::Optional<GURL> fallback_url_;
   base::Optional<GURL> inner_request_url_;
 
+  struct OuterResponseLengthInfo {
+    int64_t encoded_data_length;
+    int64_t decoded_body_length;
+  };
   // Set when URLLoaderClient::OnComplete() is called.
-  base::Optional<int64_t> encoded_data_length_;
+  base::Optional<OuterResponseLengthInfo> outer_response_length_info_;
+
   // Set when |body_data_pipe_adapter_| finishes loading the decoded body.
   base::Optional<int> decoded_body_read_result_;
   const std::string accept_langs_;
diff --git a/content/browser/webrtc/webrtc_video_capture_service_enumeration_browsertest.cc b/content/browser/webrtc/webrtc_video_capture_service_enumeration_browsertest.cc
index baf2e0e..725df9a 100644
--- a/content/browser/webrtc/webrtc_video_capture_service_enumeration_browsertest.cc
+++ b/content/browser/webrtc/webrtc_video_capture_service_enumeration_browsertest.cc
@@ -20,6 +20,7 @@
 #include "services/video_capture/public/mojom/constants.mojom.h"
 #include "services/video_capture/public/mojom/device_factory.mojom.h"
 #include "services/video_capture/public/mojom/device_factory_provider.mojom.h"
+#include "services/video_capture/public/mojom/devices_changed_observer.mojom.h"
 #include "services/video_capture/public/mojom/virtual_device.mojom.h"
 
 namespace content {
diff --git a/content/browser/webrtc/webrtc_video_capture_shared_device_browsertest.cc b/content/browser/webrtc/webrtc_video_capture_shared_device_browsertest.cc
new file mode 100644
index 0000000..bdad1dd
--- /dev/null
+++ b/content/browser/webrtc/webrtc_video_capture_shared_device_browsertest.cc
@@ -0,0 +1,174 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/command_line.h"
+#include "base/run_loop.h"
+#include "base/test/scoped_feature_list.h"
+#include "content/public/common/content_features.h"
+#include "content/public/common/content_switches.h"
+#include "content/public/common/service_manager_connection.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"
+#include "media/base/media_switches.h"
+#include "services/service_manager/public/cpp/connector.h"
+#include "services/video_capture/public/cpp/mock_receiver.h"
+#include "services/video_capture/public/mojom/constants.mojom.h"
+#include "services/video_capture/public/mojom/device_factory_provider.mojom.h"
+#include "services/video_capture/public/mojom/video_source.mojom.h"
+#include "services/video_capture/public/mojom/video_source_provider.mojom.h"
+#include "testing/gmock/include/gmock/gmock.h"
+
+using testing::_;
+using testing::AtLeast;
+using testing::InvokeWithoutArgs;
+using testing::Return;
+
+namespace content {
+
+namespace {
+
+static const char kVideoCaptureHtmlFile[] = "/media/video_capture_test.html";
+static const char kStartVideoCaptureAndVerify[] =
+    "startVideoCaptureAndVerifySize(%d, %d)";
+static const gfx::Size kVideoSize(320, 200);
+
+}  // namespace
+
+// Integration test sets up a single fake device and obtains a connection to the
+// video capture service via the Browser process' service manager. It then
+// opens the device from clients. One client is the test calling into the
+// video capture service directly. The second client is the Browser, which the
+// test exercises through JavaScript.
+class WebRtcVideoCaptureSharedDeviceBrowserTest : public ContentBrowserTest {
+ public:
+  WebRtcVideoCaptureSharedDeviceBrowserTest() : weak_factory_(this) {
+    scoped_feature_list_.InitAndEnableFeature(features::kMojoVideoCapture);
+  }
+
+  ~WebRtcVideoCaptureSharedDeviceBrowserTest() override {}
+
+  void OpenDeviceViaService(base::OnceClosure done_cb) {
+    connector_->BindInterface(video_capture::mojom::kServiceName,
+                              &device_factory_provider_);
+    device_factory_provider_->ConnectToVideoSourceProvider(
+        mojo::MakeRequest(&video_source_provider_));
+
+    video_source_provider_->GetSourceInfos(base::BindOnce(
+        &WebRtcVideoCaptureSharedDeviceBrowserTest::OnSourceInfosReceived,
+        weak_factory_.GetWeakPtr(), std::move(done_cb)));
+  }
+
+  void OpenDeviceInRendererAndWaitForPlaying() {
+    DCHECK(main_task_runner_->RunsTasksInCurrentSequence());
+    embedded_test_server()->StartAcceptingConnections();
+    GURL url(embedded_test_server()->GetURL(kVideoCaptureHtmlFile));
+    NavigateToURL(shell(), url);
+
+    const std::string javascript_to_execute = base::StringPrintf(
+        kStartVideoCaptureAndVerify, kVideoSize.width(), kVideoSize.height());
+    std::string result;
+    // Start video capture and wait until it started rendering
+    ASSERT_TRUE(
+        ExecuteScriptAndExtractString(shell(), javascript_to_execute, &result));
+    ASSERT_EQ("OK", result);
+  }
+
+ protected:
+  void SetUpCommandLine(base::CommandLine* command_line) override {
+    command_line->AppendSwitch(switches::kUseFakeDeviceForMediaStream);
+    command_line->AppendSwitch(switches::kUseFakeUIForMediaStream);
+  }
+
+  void SetUp() override {
+    ASSERT_TRUE(embedded_test_server()->InitializeAndListen());
+    EnablePixelOutput();
+    ContentBrowserTest::SetUp();
+  }
+
+  void Initialize() {
+    DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
+    main_task_runner_ = base::ThreadTaskRunnerHandle::Get();
+
+    auto* connection = content::ServiceManagerConnection::GetForProcess();
+    ASSERT_TRUE(connection);
+    auto* connector = connection->GetConnector();
+    ASSERT_TRUE(connector);
+    // We need to clone it so that we can use the clone on a different thread.
+    connector_ = connector->Clone();
+
+    mock_receiver_ = std::make_unique<video_capture::MockReceiver>(
+        mojo::MakeRequest(&receiver_proxy_));
+  }
+
+  scoped_refptr<base::TaskRunner> main_task_runner_;
+  std::unique_ptr<service_manager::Connector> connector_;
+  std::unique_ptr<video_capture::MockReceiver> mock_receiver_;
+
+ private:
+  void OnSourceInfosReceived(
+      base::OnceClosure done_cb,
+      const std::vector<media::VideoCaptureDeviceInfo>& infos) {
+    ASSERT_FALSE(infos.empty());
+    video_source_provider_->GetVideoSource(infos[0].descriptor.device_id,
+                                           mojo::MakeRequest(&video_source_));
+
+    media::VideoCaptureParams requestable_settings;
+    ASSERT_FALSE(infos[0].supported_formats.empty());
+    requestable_settings.requested_format = infos[0].supported_formats[0];
+    requestable_settings.requested_format.frame_size = kVideoSize;
+
+    video_capture::mojom::PushVideoStreamSubscriptionPtr subscription;
+    video_source_->CreatePushSubscription(
+        std::move(receiver_proxy_), requestable_settings,
+        false /*force_reopen_with_new_settings*/,
+        mojo::MakeRequest(&subscription_),
+        base::BindOnce(&WebRtcVideoCaptureSharedDeviceBrowserTest::
+                           OnCreatePushSubscriptionCallback,
+                       weak_factory_.GetWeakPtr(), std::move(done_cb)));
+  }
+
+  void OnCreatePushSubscriptionCallback(
+      base::OnceClosure done_cb,
+      video_capture::mojom::CreatePushSubscriptionResultCode result_code,
+      const media::VideoCaptureParams& params) {
+    ASSERT_EQ(video_capture::mojom::CreatePushSubscriptionResultCode::
+                  kCreatedWithRequestedSettings,
+              result_code);
+    subscription_->Activate();
+    std::move(done_cb).Run();
+  }
+
+  base::test::ScopedFeatureList scoped_feature_list_;
+  video_capture::mojom::DeviceFactoryProviderPtr device_factory_provider_;
+  video_capture::mojom::VideoSourceProviderPtr video_source_provider_;
+  video_capture::mojom::VideoSourcePtr video_source_;
+  video_capture::mojom::PushVideoStreamSubscriptionPtr subscription_;
+  video_capture::mojom::ReceiverPtr receiver_proxy_;
+  base::WeakPtrFactory<WebRtcVideoCaptureSharedDeviceBrowserTest> weak_factory_;
+
+  DISALLOW_COPY_AND_ASSIGN(WebRtcVideoCaptureSharedDeviceBrowserTest);
+};
+
+IN_PROC_BROWSER_TEST_F(WebRtcVideoCaptureSharedDeviceBrowserTest,
+                       ReceiveFrameFromServiceAndInRenderer) {
+  Initialize();
+
+  base::RunLoop receive_frame_from_service_wait_loop;
+  EXPECT_CALL(*mock_receiver_, DoOnFrameReadyInBuffer(_, _, _, _))
+      .WillOnce(InvokeWithoutArgs([&receive_frame_from_service_wait_loop]() {
+        receive_frame_from_service_wait_loop.Quit();
+      }))
+      .WillRepeatedly(Return());
+
+  base::RunLoop open_device_via_service_run_loop;
+  OpenDeviceViaService(open_device_via_service_run_loop.QuitClosure());
+  open_device_via_service_run_loop.Run();
+
+  OpenDeviceInRendererAndWaitForPlaying();
+
+  receive_frame_from_service_wait_loop.Run();
+}
+
+}  // namespace content
diff --git a/content/browser/webui/shared_resources_data_source.cc b/content/browser/webui/shared_resources_data_source.cc
index a177036a..8e84bf1 100644
--- a/content/browser/webui/shared_resources_data_source.cc
+++ b/content/browser/webui/shared_resources_data_source.cc
@@ -99,6 +99,9 @@
   return std::map<int, std::string> {
     {IDR_MOJO_MOJO_BINDINGS_JS, "js/mojo_bindings.js"},
         {IDR_MOJO_MOJO_BINDINGS_LITE_JS, "js/mojo_bindings_lite.js"},
+        {IDR_MOJO_BIG_BUFFER_MOJOM_LITE_JS, "js/big_buffer.mojom-lite.js"},
+        {IDR_MOJO_FILE_MOJOM_LITE_JS, "js/file.mojom-lite.js"},
+        {IDR_MOJO_STRING16_MOJOM_LITE_JS, "js/string16.mojom-lite.js"},
 #if defined(OS_WIN) || defined(OS_MACOSX) || defined(OS_LINUX)
         {IDR_MOJO_TIME_MOJOM_LITE_JS, "js/time.mojom-lite.js"},
 #endif  // defined(OS_WIN) || defined(OS_MACOSX) || defined(OS_LINUX)
diff --git a/content/child/runtime_features.cc b/content/child/runtime_features.cc
index feaaf54..98977d2a 100644
--- a/content/child/runtime_features.cc
+++ b/content/child/runtime_features.cc
@@ -340,8 +340,14 @@
   WebRuntimeFeatures::EnableResourceLoadScheduler(
       base::FeatureList::IsEnabled(features::kResourceLoadScheduler));
 
-  if (base::FeatureList::IsEnabled(features::kLayeredAPI))
-    WebRuntimeFeatures::EnableLayeredAPI(true);
+  if (base::FeatureList::IsEnabled(features::kBuiltInModuleAll))
+    WebRuntimeFeatures::EnableBuiltInModuleAll(true);
+
+  if (base::FeatureList::IsEnabled(features::kBuiltInModuleInfra))
+    WebRuntimeFeatures::EnableBuiltInModuleInfra(true);
+
+  if (base::FeatureList::IsEnabled(features::kBuiltInModuleKvStorage))
+    WebRuntimeFeatures::EnableBuiltInModuleKvStorage(true);
 
   if (base::FeatureList::IsEnabled(blink::features::kLayoutNG))
     WebRuntimeFeatures::EnableLayoutNG(true);
@@ -362,9 +368,6 @@
   WebRuntimeFeatures::EnableModernMediaControls(
       base::FeatureList::IsEnabled(media::kUseModernMediaControls));
 
-  WebRuntimeFeatures::EnableScheduledScriptStreaming(
-      base::FeatureList::IsEnabled(features::kScheduledScriptStreaming));
-
   WebRuntimeFeatures::EnableScriptStreamingOnPreload(
       base::FeatureList::IsEnabled(features::kScriptStreamingOnPreload));
 
@@ -430,7 +433,7 @@
   if (command_line.HasSwitch(switches::kEnableAccessibilityObjectModel))
     WebRuntimeFeatures::EnableAccessibilityObjectModel(true);
 
-  if (base::FeatureList::IsEnabled(blink::features::kWritableFilesAPI))
+  if (base::FeatureList::IsEnabled(blink::features::kNativeFilesystemAPI))
     WebRuntimeFeatures::EnableFeatureFromString("WritableFiles", true);
 
   if (base::FeatureList::IsEnabled(
diff --git a/content/common/content_param_traits.cc b/content/common/content_param_traits.cc
index 9296444..ef41ec4e7 100644
--- a/content/common/content_param_traits.cc
+++ b/content/common/content_param_traits.cc
@@ -99,6 +99,9 @@
     case blink::mojom::PolicyValueType::kBool:
       WriteParam(m, p.BoolValue());
       break;
+    case blink::mojom::PolicyValueType::kDecDouble:
+      WriteParam(m, p.DoubleValue());
+      break;
     case blink::mojom::PolicyValueType::kNull:
       break;
   }
@@ -112,6 +115,7 @@
     return false;
   blink::mojom::PolicyValueType type =
       static_cast<blink::mojom::PolicyValueType>(int_type);
+  r->SetType(type);
   switch (type) {
     case blink::mojom::PolicyValueType::kBool: {
       bool b;
@@ -120,6 +124,13 @@
       r->SetBoolValue(b);
       break;
     }
+    case blink::mojom::PolicyValueType::kDecDouble: {
+      double d;
+      if (!ReadParam(m, iter, &d))
+        return false;
+      r->SetDoubleValue(d, type);
+      break;
+    }
     case blink::mojom::PolicyValueType::kNull:
       break;
   }
diff --git a/content/gpu/gpu_child_thread.cc b/content/gpu/gpu_child_thread.cc
index 937f1396..5d8413f 100644
--- a/content/gpu/gpu_child_thread.cc
+++ b/content/gpu/gpu_child_thread.cc
@@ -167,10 +167,12 @@
 viz::VizMainImpl::ExternalDependencies CreateVizMainDependencies(
     service_manager::Connector* connector) {
   viz::VizMainImpl::ExternalDependencies deps;
-  deps.create_display_compositor =
-      base::FeatureList::IsEnabled(features::kVizDisplayCompositor);
-  if (GetContentClient()->gpu())
+  deps.create_display_compositor = features::IsVizDisplayCompositorEnabled();
+  if (GetContentClient()->gpu()) {
     deps.sync_point_manager = GetContentClient()->gpu()->GetSyncPointManager();
+    deps.shared_image_manager =
+        GetContentClient()->gpu()->GetSharedImageManager();
+  }
   auto* process = ChildProcess::current();
   deps.shutdown_event = process->GetShutDownEvent();
   deps.io_thread_task_runner = process->io_task_runner();
diff --git a/content/public/android/javatests/src/org/chromium/content/browser/TestsJavaScriptEvalTest.java b/content/public/android/javatests/src/org/chromium/content/browser/TestsJavaScriptEvalTest.java
index e357b1c..d2d94b43 100644
--- a/content/public/android/javatests/src/org/chromium/content/browser/TestsJavaScriptEvalTest.java
+++ b/content/public/android/javatests/src/org/chromium/content/browser/TestsJavaScriptEvalTest.java
@@ -11,6 +11,7 @@
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
+import org.chromium.base.ThreadUtils;
 import org.chromium.base.test.BaseJUnit4ClassRunner;
 import org.chromium.base.test.util.Feature;
 import org.chromium.base.test.util.UrlUtils;
@@ -50,7 +51,8 @@
         for (int i = 0; i < 30; ++i) {
             for (int j = 0; j < 10; ++j) {
                 // Start evaluation of a JavaScript script -- we don't need a result.
-                webContents.evaluateJavaScriptForTests("foobar();", null);
+                ThreadUtils.runOnUiThreadBlocking(
+                        () -> webContents.evaluateJavaScriptForTests("foobar();", null));
             }
             // DOMUtils does need to evaluate a JavaScript and get its result to get DOM bounds.
             Assert.assertNotNull(
diff --git a/content/public/browser/console_message.h b/content/public/browser/console_message.h
index 18f3eb25..2bef8a1 100644
--- a/content/public/browser/console_message.h
+++ b/content/public/browser/console_message.h
@@ -6,6 +6,7 @@
 #define CONTENT_PUBLIC_BROWSER_CONSOLE_MESSAGE_H_
 
 #include "base/strings/string16.h"
+#include "third_party/blink/public/mojom/devtools/console_message.mojom.h"
 #include "url/gurl.h"
 
 namespace content {
@@ -14,7 +15,7 @@
 // console.
 struct ConsoleMessage {
   ConsoleMessage(int source_identifier,
-                 int message_level,
+                 blink::mojom::ConsoleMessageLevel message_level,
                  const base::string16& message,
                  int line_number,
                  const GURL& source_url)
@@ -27,9 +28,8 @@
   // The type of source this came from. In practice, this maps to
   // blink::MessageSource.
   const int source_identifier;
-  // TODO(devlin): This should use blink::mojom or content's
-  // ConsoleMessageLevel.
-  const int message_level;
+  // The severity of the console message.
+  const blink::mojom::ConsoleMessageLevel message_level;
   // The message that was logged to the console.
   const base::string16 message;
   // The line in the script file that the log was emitted at.
diff --git a/content/public/browser/gpu_utils.cc b/content/public/browser/gpu_utils.cc
index 39f045b..1d40587 100644
--- a/content/public/browser/gpu_utils.cc
+++ b/content/public/browser/gpu_utils.cc
@@ -56,12 +56,7 @@
 #if !defined(OS_ANDROID)
   return false;
 #else
-  if (!base::FeatureList::IsEnabled(features::kVizDisplayCompositor))
-    return false;
-  if (!base::FeatureList::IsEnabled(features::kAImageReaderMediaPlayer))
-    return false;
-
-  return base::FeatureList::IsEnabled(features::kAndroidSurfaceControl);
+  return features::IsAndroidSurfaceControlEnabled();
 #endif
 }
 
diff --git a/content/public/browser/render_frame_host.h b/content/public/browser/render_frame_host.h
index 1d96dbc..7d35016 100644
--- a/content/public/browser/render_frame_host.h
+++ b/content/public/browser/render_frame_host.h
@@ -304,6 +304,11 @@
   // RenderFrameHost and is allowed to be used by it. Use this in the browser
   // process to determine whether access to a feature is allowed.
   virtual bool IsFeatureEnabled(blink::mojom::FeaturePolicyFeature feature) = 0;
+  // Returns true if the given |threshold_value| is above the threshold value
+  // specified in the policy for |feature| for this RenderFrameHost. Use this
+  // in the browser process to determine whether access to a feature is allowed.
+  virtual bool IsFeatureEnabled(blink::mojom::FeaturePolicyFeature feature,
+                                blink::PolicyValue threshold_value) = 0;
 
   // Opens view-source tab for the document last committed in this
   // RenderFrameHost.
diff --git a/content/public/common/BUILD.gn b/content/public/common/BUILD.gn
index cd52885..a3f06f2 100644
--- a/content/public/common/BUILD.gn
+++ b/content/public/common/BUILD.gn
@@ -377,7 +377,10 @@
       "ns_view_bridge_factory.mojom",
       "web_contents_ns_view_bridge.mojom",
     ]
-    public_deps += [ "//ui/gfx/geometry/mojo" ]
+    public_deps += [
+      "//ui/gfx/geometry/mojo",
+      "//ui/gfx/image/mojo:interfaces",
+    ]
   }
 
   component_output_prefix = "content_public_common_mojo_bindings"
diff --git a/content/public/common/content_features.cc b/content/public/common/content_features.cc
index 0fe852b..76452b9a 100644
--- a/content/public/common/content_features.cc
+++ b/content/public/common/content_features.cc
@@ -230,8 +230,14 @@
                                     base::FEATURE_DISABLED_BY_DEFAULT};
 const char kIsolateOriginsFieldTrialParamName[] = "OriginsList";
 
-const base::Feature kLayeredAPI{"LayeredAPI",
-                                base::FEATURE_DISABLED_BY_DEFAULT};
+const base::Feature kBuiltInModuleKvStorage{"BuiltInModuleKvStorage",
+                                            base::FEATURE_DISABLED_BY_DEFAULT};
+
+const base::Feature kBuiltInModuleAll{"BuiltInModuleAll",
+                                      base::FEATURE_DISABLED_BY_DEFAULT};
+
+const base::Feature kBuiltInModuleInfra{"BuiltInModuleInfra",
+                                        base::FEATURE_DISABLED_BY_DEFAULT};
 
 const base::Feature kLazyFrameLoading{"LazyFrameLoading",
                                       base::FEATURE_DISABLED_BY_DEFAULT};
@@ -652,10 +658,6 @@
 const base::Feature kWipeCorruptV2IDBDatabases{
     "WipeCorruptV2IDBDatabases", base::FEATURE_ENABLED_BY_DEFAULT};
 
-// Enabled scheduler use for script streaming.
-const base::Feature kScheduledScriptStreaming{"ScheduledScriptStreaming",
-                                              base::FEATURE_ENABLED_BY_DEFAULT};
-
 // Start streaming scripts on script preload.
 const base::Feature kScriptStreamingOnPreload{
     "ScriptStreamingOnPreload", base::FEATURE_DISABLED_BY_DEFAULT};
diff --git a/content/public/common/content_features.h b/content/public/common/content_features.h
index 342a639..6876635 100644
--- a/content/public/common/content_features.h
+++ b/content/public/common/content_features.h
@@ -59,7 +59,9 @@
 CONTENT_EXPORT extern const base::Feature kInputPredictorTypeChoice;
 CONTENT_EXPORT extern const base::Feature kIsolateOrigins;
 CONTENT_EXPORT extern const char kIsolateOriginsFieldTrialParamName[];
-CONTENT_EXPORT extern const base::Feature kLayeredAPI;
+CONTENT_EXPORT extern const base::Feature kBuiltInModuleAll;
+CONTENT_EXPORT extern const base::Feature kBuiltInModuleInfra;
+CONTENT_EXPORT extern const base::Feature kBuiltInModuleKvStorage;
 CONTENT_EXPORT extern const base::Feature kLazyFrameLoading;
 CONTENT_EXPORT extern const base::Feature kLazyFrameVisibleLoadTimeMetrics;
 CONTENT_EXPORT extern const base::Feature kLazyImageLoading;
@@ -143,7 +145,6 @@
 CONTENT_EXPORT extern const base::Feature kWebXrGamepadSupport;
 CONTENT_EXPORT extern const base::Feature kWebXrHitTest;
 CONTENT_EXPORT extern const base::Feature kWipeCorruptV2IDBDatabases;
-CONTENT_EXPORT extern const base::Feature kScheduledScriptStreaming;
 CONTENT_EXPORT extern const base::Feature kScriptStreamingOnPreload;
 
 #if defined(OS_ANDROID)
diff --git a/content/public/common/content_switches.cc b/content/public/common/content_switches.cc
index f4dc570c..a6e748b 100644
--- a/content/public/common/content_switches.cc
+++ b/content/public/common/content_switches.cc
@@ -25,6 +25,10 @@
 const char kAllowLoopbackInPeerConnection[] =
     "allow-loopback-in-peer-connection";
 
+// Allow a page to show popups during its unloading.
+// TODO(https://crbug.com/937569): Remove this in Chrome 82.
+const char kAllowPopupsDuringPageUnload[] = "allow-popups-during-page-unload";
+
 // Uses the android SkFontManager on linux. The specified directory should
 // include the configuration xml file with the name "fonts.xml".
 // This is used in blimp to emulate android fonts on linux.
diff --git a/content/public/common/content_switches.h b/content/public/common/content_switches.h
index 1e6023ae..b298c312 100644
--- a/content/public/common/content_switches.h
+++ b/content/public/common/content_switches.h
@@ -19,6 +19,7 @@
 CONTENT_EXPORT extern const char kAllowFileAccessFromFiles[];
 CONTENT_EXPORT extern const char kAllowInsecureLocalhost[];
 CONTENT_EXPORT extern const char kAllowLoopbackInPeerConnection[];
+CONTENT_EXPORT extern const char kAllowPopupsDuringPageUnload[];
 CONTENT_EXPORT extern const char kAndroidFontsPath[];
 CONTENT_EXPORT extern const char kBlinkSettings[];
 CONTENT_EXPORT extern const char kBrowserCrashTest[];
diff --git a/content/public/common/web_contents_ns_view_bridge.mojom b/content/public/common/web_contents_ns_view_bridge.mojom
index 3eef400..5489e37 100644
--- a/content/public/common/web_contents_ns_view_bridge.mojom
+++ b/content/public/common/web_contents_ns_view_bridge.mojom
@@ -5,7 +5,9 @@
 module content.mojom;
 
 import "content/public/common/drop_data.mojom";
+import "mojo/public/mojom/base/file_path.mojom";
 import "ui/gfx/geometry/mojo/geometry.mojom";
+import "ui/gfx/image/mojo/image.mojom";
 import "url/mojom/url.mojom";
 
 // Interface through which a WebContentsViewMac communicates with its NSView in
@@ -33,6 +35,12 @@
   // iterated past the last or first focusable element on the page). The
   // iteration direction is in |reverse|.
   TakeFocus(bool reverse);
+
+  // Initiate a drag from the web contents area.
+  StartDrag(DropData drop_data,
+            uint32 operation_mask,
+            gfx.mojom.ImageSkia? image,
+            gfx.mojom.Vector2d image_offset);
 };
 
 // The method through which a window was focused (directly focused, or by
@@ -108,10 +116,25 @@
   [Sync]
   DraggingUpdated(DraggingInfo dragging_info) => (uint32 result);
 
-  // Called in response to the -[NSDraggingDestination performDragOperation]
+  // Called in response to the -[NSDraggingDestination performDragOperation:]
   // method being called on the NSView. Returns the result of the operation in
   // |result|.
   [Sync]
   PerformDragOperation(DraggingInfo dragging_info) => (bool result);
+
+  // Called in response to the -namesOfPromisedFilesDroppedAtDestination method
+  // being called on the NSView. The |file_path| input argument is the
+  // requested destination file, and the output |file_path| is the actual
+  // destination file.
+  [Sync]
+  DragPromisedFileTo(mojo_base.mojom.FilePath file_path,
+                     DropData drop_data,
+                     url.mojom.Url download_url) =>
+                         (mojo_base.mojom.FilePath file_path);
+
+  // Called in to the -draggedImage: method being called on the NSView.
+  EndDrag(uint32 drag_operation,
+          gfx.mojom.PointF local_point,
+          gfx.mojom.PointF screen_point);
 };
 
diff --git a/content/public/gpu/content_gpu_client.cc b/content/public/gpu/content_gpu_client.cc
index b354769..19addbb 100644
--- a/content/public/gpu/content_gpu_client.cc
+++ b/content/public/gpu/content_gpu_client.cc
@@ -14,6 +14,10 @@
   return nullptr;
 }
 
+gpu::SharedImageManager* ContentGpuClient::GetSharedImageManager() {
+  return nullptr;
+}
+
 #if BUILDFLAG(ENABLE_LIBRARY_CDMS)
 std::unique_ptr<media::CdmProxy> ContentGpuClient::CreateCdmProxy(
     const base::Token& cdm_guid) {
diff --git a/content/public/gpu/content_gpu_client.h b/content/public/gpu/content_gpu_client.h
index 28f79700..20e31e1b 100644
--- a/content/public/gpu/content_gpu_client.h
+++ b/content/public/gpu/content_gpu_client.h
@@ -20,6 +20,7 @@
 
 namespace gpu {
 struct GpuPreferences;
+class SharedImageManager;
 class SyncPointManager;
 }
 
@@ -52,9 +53,10 @@
   virtual void PostCompositorThreadCreated(
       base::SingleThreadTaskRunner* task_runner) {}
 
-  // Allows client to supply a SyncPointManager instance instead of having
-  // content internally create one.
+  // Allows client to supply SyncPointManager and SharedImageManager instance
+  // instead of having content internally create one.
   virtual gpu::SyncPointManager* GetSyncPointManager();
+  virtual gpu::SharedImageManager* GetSharedImageManager();
 
 #if BUILDFLAG(ENABLE_LIBRARY_CDMS)
   // Creates a media::CdmProxy for the type of Content Decryption Module (CDM)
diff --git a/content/public/renderer/render_frame_media_playback_options.h b/content/public/renderer/render_frame_media_playback_options.h
index fff91eba..32b53942 100644
--- a/content/public/renderer/render_frame_media_playback_options.h
+++ b/content/public/renderer/render_frame_media_playback_options.h
@@ -28,6 +28,9 @@
 
   // Whether background video optimization is supported on current platform.
   bool is_background_video_track_optimization_supported = true;
+
+  // Whether MojoRenderer should be used for given |render_frame|.
+  bool is_mojo_renderer_enabled = true;
 };
 
 }  // namespace content
diff --git a/content/public/test/android/javatests/src/org/chromium/content_public/browser/test/util/TestCallbackHelperContainer.java b/content/public/test/android/javatests/src/org/chromium/content_public/browser/test/util/TestCallbackHelperContainer.java
index 03f94af..4c8ab50 100644
--- a/content/public/test/android/javatests/src/org/chromium/content_public/browser/test/util/TestCallbackHelperContainer.java
+++ b/content/public/test/android/javatests/src/org/chromium/content_public/browser/test/util/TestCallbackHelperContainer.java
@@ -123,7 +123,8 @@
                 }
             };
             mJsonResult = null;
-            webContents.evaluateJavaScriptForTests(code, callback);
+            ThreadUtils.runOnUiThreadBlocking(
+                    () -> webContents.evaluateJavaScriptForTests(code, callback));
         }
 
         /**
diff --git a/content/public/test/cache_test_util.cc b/content/public/test/cache_test_util.cc
index befeb8eb..05920c5 100644
--- a/content/public/test/cache_test_util.cc
+++ b/content/public/test/cache_test_util.cc
@@ -3,6 +3,9 @@
 // found in the LICENSE file.
 
 #include "content/public/test/cache_test_util.h"
+
+#include <functional>
+
 #include "base/bind.h"
 #include "base/synchronization/waitable_event.h"
 #include "base/task/post_task.h"
@@ -41,7 +44,7 @@
   base::PostTaskWithTraits(
       FROM_HERE, {BrowserThread::IO},
       base::BindOnce(&CacheTestUtil::CreateCacheEntriesOnIOThread,
-                     base::Unretained(this), base::ConstRef(keys)));
+                     base::Unretained(this), std::cref(keys)));
   WaitForTasksOnIOThread();
 }
 
diff --git a/content/public/test/test_browser_thread_bundle.cc b/content/public/test/test_browser_thread_bundle.cc
index 4ec3e1f..7ddf0c62 100644
--- a/content/public/test/test_browser_thread_bundle.cc
+++ b/content/public/test/test_browser_thread_bundle.cc
@@ -31,27 +31,32 @@
 namespace content {
 
 TestBrowserThreadBundle::~TestBrowserThreadBundle() {
-  // This is required to ensure we run all remaining MessageLoop and
-  // TaskScheduler tasks in an atomic step. This is a bit different than
-  // production where the main thread is not flushed after it's done running
-  // but this approach is preferred in unit tests as running more tasks can
-  // merely uncover more issues (e.g. if a bad tasks is posted but never
-  // blocked upon it could make a test flaky whereas by flushing we guarantee
-  // it will blow up).
-  RunUntilIdle();
+  // To ensure a clean teardown, each thread's message loop must be flushed
+  // just before the thread is destroyed. But stopping a fake thread does not
+  // automatically flush the message loop, so we have to do it manually.
+  // See http://crbug.com/247525 for discussion.
+  base::RunLoop().RunUntilIdle();
+  io_thread_->Stop();
+  base::RunLoop().RunUntilIdle();
+  ui_thread_->Stop();
+  base::RunLoop().RunUntilIdle();
 
-  // When REAL_IO_THREAD, we need to stop the IO thread explicitly and flush
-  // again.
-  if (real_io_thread_) {
-    io_thread_->Stop();
-    RunUntilIdle();
+  // Skip the following steps when RunAllTasksUntilIdle might result in a hang
+  // (ExecutionMode::QUEUED) or for MainThreadType::MOCK_TIME where we haven't
+  // enforced there being no pending tasks.
+  if (main_thread_type() != MainThreadType::MOCK_TIME &&
+      execution_control_mode() != ExecutionMode::QUEUED) {
+    // This is required to ensure we run all remaining MessageLoop and
+    // TaskScheduler tasks in an atomic step. This is a bit different than
+    // production where the main thread is not flushed after it's done running
+    // but this approach is preferred in unit tests as running more tasks can
+    // merely uncover more issues (e.g. if a bad tasks is posted but never
+    // blocked upon it could make a test flaky whereas by flushing we guarantee
+    // it will blow up).
+    RunAllTasksUntilIdle();
+    CHECK(MainThreadIsIdle()) << sequence_manager()->DescribeAllPendingTasks();
   }
 
-  // The only way this check can fail after RunUntilIdle() is if a test is
-  // running its own base::Thread's. Such tests should make sure to coalesce
-  // independent threads before this point.
-  CHECK(MainThreadIsIdle()) << sequence_manager()->DescribeAllPendingTasks();
-
   BrowserTaskExecutor::ResetForTesting();
 
   // Run DestructionObservers before our fake threads go away to ensure
diff --git a/content/renderer/accessibility/ax_image_annotator.cc b/content/renderer/accessibility/ax_image_annotator.cc
index 469b7995..f30ec9e4 100644
--- a/content/renderer/accessibility/ax_image_annotator.cc
+++ b/content/renderer/accessibility/ax_image_annotator.cc
@@ -148,8 +148,8 @@
   for (auto& key_value : image_annotations_) {
     blink::WebAXObject image = blink::WebAXObject::FromWebDocumentByID(
         render_accessibility_->GetMainDocument(), key_value.first);
-    DCHECK(!image.IsDetached());
-    render_accessibility_->MarkWebAXObjectDirty(image, false /* subtree */);
+    if (!image.IsDetached())
+      render_accessibility_->MarkWebAXObjectDirty(image, false /* subtree */);
   }
   image_annotations_.clear();
 }
diff --git a/content/renderer/compositor/layer_tree_view.cc b/content/renderer/compositor/layer_tree_view.cc
index 3a18950..defdfb3 100644
--- a/content/renderer/compositor/layer_tree_view.cc
+++ b/content/renderer/compositor/layer_tree_view.cc
@@ -470,8 +470,8 @@
   return layer_tree_host_->DeferMainFrameUpdate();
 }
 
-void LayerTreeView::StartDeferringCommits() {
-  layer_tree_host_->StartDeferringCommits();
+void LayerTreeView::StartDeferringCommits(base::TimeDelta timeout) {
+  layer_tree_host_->StartDeferringCommits(timeout);
 }
 
 void LayerTreeView::StopDeferringCommits() {
diff --git a/content/renderer/compositor/layer_tree_view.h b/content/renderer/compositor/layer_tree_view.h
index b03b670..cda68a9c2 100644
--- a/content/renderer/compositor/layer_tree_view.h
+++ b/content/renderer/compositor/layer_tree_view.h
@@ -150,7 +150,7 @@
   void UpdateAllLifecyclePhasesAndCompositeForTesting(bool do_raster) override;
   std::unique_ptr<cc::ScopedDeferMainFrameUpdate> DeferMainFrameUpdate()
       override;
-  void StartDeferringCommits() override;
+  void StartDeferringCommits(base::TimeDelta timeout) override;
   void StopDeferringCommits() override;
   void SetMutatorClient(std::unique_ptr<cc::LayerTreeMutator>) override;
   void SetPaintWorkletLayerPainterClient(
diff --git a/content/renderer/fetchers/resource_fetcher_impl.cc b/content/renderer/fetchers/resource_fetcher_impl.cc
index d1a7c11..ea90561 100644
--- a/content/renderer/fetchers/resource_fetcher_impl.cc
+++ b/content/renderer/fetchers/resource_fetcher_impl.cc
@@ -181,7 +181,7 @@
     // Existing callers need URL and HTTP status code. URL is already set in
     // Start().
     if (response_head.headers)
-      response_.SetHTTPStatusCode(response_head.headers->response_code());
+      response_.SetHttpStatusCode(response_head.headers->response_code());
   }
   void OnReceiveRedirect(
       const net::RedirectInfo& redirect_info,
diff --git a/content/renderer/loader/navigation_body_loader.cc b/content/renderer/loader/navigation_body_loader.cc
index 399a730..b0af429b 100644
--- a/content/renderer/loader/navigation_body_loader.cc
+++ b/content/renderer/loader/navigation_body_loader.cc
@@ -51,7 +51,7 @@
         url, redirect_response, &redirect.redirect_response,
         false /* report_security_info */, request_id);
     if (url.SchemeIs(url::kDataScheme))
-      redirect.redirect_response.SetHTTPStatusCode(200);
+      redirect.redirect_response.SetHttpStatusCode(200);
     redirect.new_url = redirect_info.new_url;
     redirect.new_referrer =
         blink::WebString::FromUTF8(redirect_info.new_referrer);
@@ -67,7 +67,7 @@
                                         false /* report_security_info */,
                                         request_id);
   if (url.SchemeIs(url::kDataScheme))
-    navigation_params->response.SetHTTPStatusCode(200);
+    navigation_params->response.SetHttpStatusCode(200);
 
   if (url_loader_client_endpoints) {
     navigation_params->body_loader.reset(new NavigationBodyLoader(
diff --git a/content/renderer/loader/web_url_loader_impl.cc b/content/renderer/loader/web_url_loader_impl.cc
index 66db6e8..b0e49d2 100644
--- a/content/renderer/loader/web_url_loader_impl.cc
+++ b/content/renderer/loader/web_url_loader_impl.cc
@@ -1325,7 +1325,7 @@
   else if (headers->GetHttpVersion() == net::HttpVersion(2, 0))
     version = WebURLResponse::kHTTPVersion_2_0;
   response->SetHTTPVersion(version);
-  response->SetHTTPStatusCode(headers->response_code());
+  response->SetHttpStatusCode(headers->response_code());
   response->SetHTTPStatusText(WebString::FromLatin1(headers->GetStatusText()));
 
   // Build up the header map.
diff --git a/content/renderer/media/media_factory.cc b/content/renderer/media/media_factory.cc
index 269180b..3088609 100644
--- a/content/renderer/media/media_factory.cc
+++ b/content/renderer/media/media_factory.cc
@@ -330,7 +330,10 @@
   base::WeakPtr<media::MediaObserver> media_observer;
 
   auto factory_selector = CreateRendererFactorySelector(
-      media_log.get(), use_media_player_renderer, GetDecoderFactory(),
+      media_log.get(), use_media_player_renderer,
+      render_frame_->GetRenderFrameMediaPlaybackOptions()
+          .is_mojo_renderer_enabled,
+      GetDecoderFactory(),
       std::make_unique<media::RemotePlaybackClientWrapperImpl>(client),
       &media_observer);
 
@@ -416,6 +419,7 @@
 MediaFactory::CreateRendererFactorySelector(
     media::MediaLog* media_log,
     bool use_media_player,
+    bool enable_mojo_renderer,
     media::DecoderFactory* decoder_factory,
     std::unique_ptr<media::RemotePlaybackClientWrapper> client_wrapper,
     base::WeakPtr<media::MediaObserver>* out_media_observer) {
@@ -470,13 +474,7 @@
 
   bool use_mojo_renderer_factory = false;
 #if BUILDFLAG(ENABLE_MOJO_RENDERER)
-#if BUILDFLAG(ENABLE_RUNTIME_MEDIA_RENDERER_SELECTION)
-  use_mojo_renderer_factory =
-      !base::CommandLine::ForCurrentProcess()->HasSwitch(
-          switches::kDisableMojoRenderer);
-#else
-  use_mojo_renderer_factory = true;
-#endif  // BUILDFLAG(ENABLE_RUNTIME_MEDIA_RENDERER_SELECTION)
+  use_mojo_renderer_factory = enable_mojo_renderer;
   if (use_mojo_renderer_factory) {
     auto mojo_renderer_factory = std::make_unique<media::MojoRendererFactory>(
         base::Bind(&RenderThreadImpl::GetGpuFactories,
diff --git a/content/renderer/media/media_factory.h b/content/renderer/media/media_factory.h
index 33028756..b40de60 100644
--- a/content/renderer/media/media_factory.h
+++ b/content/renderer/media/media_factory.h
@@ -119,6 +119,7 @@
   std::unique_ptr<media::RendererFactorySelector> CreateRendererFactorySelector(
       media::MediaLog* media_log,
       bool use_media_player,
+      bool enable_mojo_renderer,
       media::DecoderFactory* decoder_factory,
       std::unique_ptr<media::RemotePlaybackClientWrapper> client_wrapper,
       base::WeakPtr<media::MediaObserver>* out_media_observer);
diff --git a/content/renderer/media/stream/media_stream_audio_processor_options.cc b/content/renderer/media/stream/media_stream_audio_processor_options.cc
index eec15bd..d8d88dd6 100644
--- a/content/renderer/media/stream/media_stream_audio_processor_options.cc
+++ b/content/renderer/media/stream/media_stream_audio_processor_options.cc
@@ -79,9 +79,7 @@
 }
 
 bool AudioProcessingProperties::EchoCancellationIsWebRtcProvided() const {
-  return echo_cancellation_type ==
-             EchoCancellationType::kEchoCancellationAec2 ||
-         echo_cancellation_type == EchoCancellationType::kEchoCancellationAec3;
+  return echo_cancellation_type == EchoCancellationType::kEchoCancellationAec3;
 }
 
 media::AudioProcessingSettings
@@ -92,8 +90,6 @@
     switch (type) {
       case EchoCancellationType::kEchoCancellationDisabled:
         return media::EchoCancellationType::kDisabled;
-      case EchoCancellationType::kEchoCancellationAec2:
-        return media::EchoCancellationType::kAec2;
       case EchoCancellationType::kEchoCancellationAec3:
         return media::EchoCancellationType::kAec3;
       case EchoCancellationType::kEchoCancellationSystem:
diff --git a/content/renderer/media/stream/media_stream_audio_processor_options.h b/content/renderer/media/stream/media_stream_audio_processor_options.h
index c84ce46..d7888e87 100644
--- a/content/renderer/media/stream/media_stream_audio_processor_options.h
+++ b/content/renderer/media/stream/media_stream_audio_processor_options.h
@@ -45,8 +45,6 @@
   enum class EchoCancellationType {
     // Echo cancellation disabled.
     kEchoCancellationDisabled,
-    // The WebRTC-provided AEC2 echo canceller.
-    kEchoCancellationAec2,
     // The WebRTC-provided AEC3 echo canceller.
     kEchoCancellationAec3,
     // System echo canceller, for example an OS-provided or hardware echo
@@ -75,7 +73,7 @@
   media::AudioProcessingSettings ToAudioProcessingSettings() const;
 
   EchoCancellationType echo_cancellation_type =
-      EchoCancellationType::kEchoCancellationAec2;
+      EchoCancellationType::kEchoCancellationAec3;
   bool disable_hw_noise_suppression = false;
   bool goog_audio_mirroring = false;
   bool goog_auto_gain_control = true;
diff --git a/content/renderer/media/stream/media_stream_constraints_util_audio.cc b/content/renderer/media/stream/media_stream_constraints_util_audio.cc
index 3b10468..14a8824 100644
--- a/content/renderer/media/stream/media_stream_constraints_util_audio.cc
+++ b/content/renderer/media/stream/media_stream_constraints_util_audio.cc
@@ -73,7 +73,6 @@
     kDisabled = 1,
     kSystem = 2,
     kAec3 = 3,
-    kAec2 = 4
   };
 
   explicit Score(double fitness,
@@ -415,23 +414,21 @@
         return Score::EcModeScore::kSystem;
       case EchoCancellationType::kEchoCancellationAec3:
         return Score::EcModeScore::kAec3;
-      case EchoCancellationType::kEchoCancellationAec2:
-        return Score::EcModeScore::kAec2;
     }
   }
 
   static base::Optional<EchoCancellationType> ToEchoCancellationType(
       const char* blink_type) {
     std::string blink_type_str = std::string(blink_type);
-    if (blink_type_str == blink::kEchoCancellationTypeBrowser) {
-      return EchoCancellationType::kEchoCancellationAec2;
-    } else if (blink_type_str == blink::kEchoCancellationTypeAec3) {
+    if (blink_type_str == blink::kEchoCancellationTypeBrowser ||
+        blink_type_str == blink::kEchoCancellationTypeAec3) {
       return EchoCancellationType::kEchoCancellationAec3;
-    } else if (blink_type_str == blink::kEchoCancellationTypeSystem) {
-      return EchoCancellationType::kEchoCancellationSystem;
-    } else {
-      return base::nullopt;
     }
+
+    if (blink_type_str == blink::kEchoCancellationTypeSystem)
+      return EchoCancellationType::kEchoCancellationSystem;
+
+    return base::nullopt;
   }
 
   static std::vector<EchoCancellationType> ToEchoCancellationTypes(
@@ -462,10 +459,10 @@
       types.push_back(EchoCancellationType::kEchoCancellationDisabled);
 
     if (ec_set.Contains(true)) {
-      if (ec_type_set.Contains(blink::kEchoCancellationTypeBrowser))
-        types.push_back(EchoCancellationType::kEchoCancellationAec2);
-      if (ec_type_set.Contains(blink::kEchoCancellationTypeAec3))
+      if (ec_type_set.Contains(blink::kEchoCancellationTypeBrowser) ||
+          ec_type_set.Contains(blink::kEchoCancellationTypeAec3)) {
         types.push_back(EchoCancellationType::kEchoCancellationAec3);
+      }
       if (ec_type_set.Contains(blink::kEchoCancellationTypeSystem))
         types.push_back(EchoCancellationType::kEchoCancellationSystem);
     }
@@ -513,17 +510,6 @@
       return EchoCancellationType::kEchoCancellationSystem;
     }
 
-    base::Optional<bool> override_aec3 = GetOverrideAec3();
-    bool use_aec3 = override_aec3.value_or(
-        base::FeatureList::IsEnabled(features::kWebRtcUseEchoCanceller3));
-    if ((use_aec3 && ec_mode_allowed_values_.Contains(
-                         EchoCancellationType::kEchoCancellationAec3)) ||
-        (!use_aec3 && ec_mode_allowed_values_.Contains(
-                          EchoCancellationType::kEchoCancellationAec2))) {
-      return use_aec3 ? EchoCancellationType::kEchoCancellationAec3
-                      : EchoCancellationType::kEchoCancellationAec2;
-    }
-
     // If the previous tie breakers were not enough to determine the selected
     // mode, the resolution is based on pre-defined priorities assigned to the
     // available echo cancellation modes.
@@ -587,8 +573,6 @@
 
     if (ec) {
       return ec_mode_allowed_values_.Contains(
-                 EchoCancellationType::kEchoCancellationAec2) ||
-             ec_mode_allowed_values_.Contains(
                  EchoCancellationType::kEchoCancellationAec3) ||
              ec_mode_allowed_values_.Contains(
                  EchoCancellationType::kEchoCancellationSystem);
@@ -642,8 +626,7 @@
       const media::AudioParameters& device_parameters) {
     return ProcessingBasedContainer(
         ProcessingType::kApmProcessed,
-        {EchoCancellationType::kEchoCancellationAec2,
-         EchoCancellationType::kEchoCancellationAec3,
+        {EchoCancellationType::kEchoCancellationAec3,
          EchoCancellationType::kEchoCancellationDisabled},
         BoolSet(), /* goog_audio_mirroring_set */
         BoolSet(), /* goog_auto_gain_control_set */
diff --git a/content/renderer/media/stream/media_stream_constraints_util_audio_unittest.cc b/content/renderer/media/stream/media_stream_constraints_util_audio_unittest.cc
index d7f995e..3bfb113 100644
--- a/content/renderer/media/stream/media_stream_constraints_util_audio_unittest.cc
+++ b/content/renderer/media/stream/media_stream_constraints_util_audio_unittest.cc
@@ -444,7 +444,7 @@
   EchoCancellationType GetEchoCancellationTypeFromConstraintString(
       const blink::WebString& constraint_string) {
     if (constraint_string == kEchoCancellationTypeValues[0])
-      return EchoCancellationType::kEchoCancellationAec2;
+      return EchoCancellationType::kEchoCancellationAec3;
     if (constraint_string == kEchoCancellationTypeValues[1])
       return EchoCancellationType::kEchoCancellationAec3;
     if (constraint_string == kEchoCancellationTypeValues[2])
@@ -1932,7 +1932,7 @@
     constraint_factory_.Reset();
     constraint_factory_.basic().echo_cancellation.SetExact(
         properties.echo_cancellation_type ==
-        EchoCancellationType::kEchoCancellationAec2);
+        EchoCancellationType::kEchoCancellationAec3);
     auto result = SelectSettingsAudioCapture(
         source.get(), constraint_factory_.CreateWebMediaConstraints());
     EXPECT_TRUE(result.HasValue());
@@ -1940,7 +1940,7 @@
     constraint_factory_.Reset();
     constraint_factory_.basic().echo_cancellation.SetExact(
         properties.echo_cancellation_type !=
-        EchoCancellationType::kEchoCancellationAec2);
+        EchoCancellationType::kEchoCancellationAec3);
     result = SelectSettingsAudioCapture(
         source.get(), constraint_factory_.CreateWebMediaConstraints());
     EXPECT_FALSE(result.HasValue());
@@ -2030,7 +2030,6 @@
 
   const EchoCancellationType kEchoCancellationTypes[] = {
       EchoCancellationType::kEchoCancellationDisabled,
-      EchoCancellationType::kEchoCancellationAec2,
       EchoCancellationType::kEchoCancellationAec3,
       EchoCancellationType::kEchoCancellationSystem};
 
@@ -2107,7 +2106,7 @@
     EXPECT_TRUE(result.HasValue());
     EXPECT_EQ(result.device_id(), processed_source->device().id);
     EXPECT_EQ(result.audio_processing_properties().echo_cancellation_type,
-              EchoCancellationType::kEchoCancellationAec2);
+              EchoCancellationType::kEchoCancellationAec3);
   }
 }
 
diff --git a/content/renderer/media/stream/processed_local_audio_source.cc b/content/renderer/media/stream/processed_local_audio_source.cc
index b61f233ef..e8f2cf1 100644
--- a/content/renderer/media/stream/processed_local_audio_source.cc
+++ b/content/renderer/media/stream/processed_local_audio_source.cc
@@ -49,8 +49,6 @@
         switch (type) {
           case AEC::kEchoCancellationDisabled:
             return "disabled";
-          case AEC::kEchoCancellationAec2:
-            return "aec2";
           case AEC::kEchoCancellationAec3:
             return "aec3";
           case AEC::kEchoCancellationSystem:
diff --git a/content/renderer/media/stream/user_media_processor.cc b/content/renderer/media/stream/user_media_processor.cc
index 9127e8f..0fab41fd 100644
--- a/content/renderer/media/stream/user_media_processor.cc
+++ b/content/renderer/media/stream/user_media_processor.cc
@@ -168,13 +168,9 @@
         echo_cancellation_mode =
             WebMediaStreamSource::EchoCancellationMode::kDisabled;
         break;
-      case EchoCancellationType::kEchoCancellationAec2:
-        echo_cancellation_mode =
-            WebMediaStreamSource::EchoCancellationMode::kBrowser;
-        break;
       case EchoCancellationType::kEchoCancellationAec3:
         echo_cancellation_mode =
-            WebMediaStreamSource::EchoCancellationMode::kAec3;
+            WebMediaStreamSource::EchoCancellationMode::kBrowser;
         break;
       case EchoCancellationType::kEchoCancellationSystem:
         echo_cancellation_mode =
diff --git a/content/renderer/media/webrtc/rtc_peer_connection_handler.cc b/content/renderer/media/webrtc/rtc_peer_connection_handler.cc
index 83a3bd7..947d5c6 100644
--- a/content/renderer/media/webrtc/rtc_peer_connection_handler.cc
+++ b/content/renderer/media/webrtc/rtc_peer_connection_handler.cc
@@ -8,6 +8,7 @@
 #include <string.h>
 
 #include <algorithm>
+#include <functional>
 #include <memory>
 #include <set>
 #include <string>
@@ -1572,7 +1573,7 @@
       base::BindRepeating(
           &RTCPeerConnectionHandler::AddTransceiverWithTrackOnSignalingThread,
           base::Unretained(this), base::RetainedRef(track_ref->webrtc_track()),
-          base::ConstRef(init), base::Unretained(&transceiver_state_surfacer),
+          std::cref(init), base::Unretained(&transceiver_state_surfacer),
           base::Unretained(&error_or_transceiver)),
       "AddTransceiverWithTrackOnSignalingThread");
   if (!error_or_transceiver.ok()) {
@@ -1623,8 +1624,8 @@
   RunSynchronousRepeatingClosureOnSignalingThread(
       base::BindRepeating(&RTCPeerConnectionHandler::
                               AddTransceiverWithMediaTypeOnSignalingThread,
-                          base::Unretained(this), base::ConstRef(media_type),
-                          base::ConstRef(init),
+                          base::Unretained(this), std::cref(media_type),
+                          std::cref(init),
                           base::Unretained(&transceiver_state_surfacer),
                           base::Unretained(&error_or_transceiver)),
       "AddTransceiverWithMediaTypeOnSignalingThread");
diff --git a/content/renderer/media/webrtc/rtc_video_decoder_adapter.cc b/content/renderer/media/webrtc/rtc_video_decoder_adapter.cc
index b69258d7..6b51dd2 100644
--- a/content/renderer/media/webrtc/rtc_video_decoder_adapter.cc
+++ b/content/renderer/media/webrtc/rtc_video_decoder_adapter.cc
@@ -5,6 +5,7 @@
 #include "content/renderer/media/webrtc/rtc_video_decoder_adapter.h"
 
 #include <algorithm>
+#include <functional>
 
 #include "base/bind.h"
 #include "base/bind_helpers.h"
@@ -198,8 +199,8 @@
   if (media_task_runner_->PostTask(
           FROM_HERE,
           base::BindOnce(&RTCVideoDecoderAdapter::InitializeOnMediaThread,
-                         base::Unretained(this), base::ConstRef(config),
-                         base::ConstRef(init_cb)))) {
+                         base::Unretained(this), std::cref(config),
+                         std::cref(init_cb)))) {
     waiter.Wait();
   }
   return result;
diff --git a/content/renderer/media/webrtc/rtc_video_encoder.cc b/content/renderer/media/webrtc/rtc_video_encoder.cc
index 7249dcf..d24ff0c 100644
--- a/content/renderer/media/webrtc/rtc_video_encoder.cc
+++ b/content/renderer/media/webrtc/rtc_video_encoder.cc
@@ -797,7 +797,6 @@
   }
 
   webrtc::CodecSpecificInfo info;
-  memset(&info, 0, sizeof(info));
   info.codecType = video_codec_type_;
   if (video_codec_type_ == webrtc::kVideoCodecVP8) {
     info.codecSpecific.VP8.keyIdx = -1;
diff --git a/content/renderer/render_frame_impl_browsertest.cc b/content/renderer/render_frame_impl_browsertest.cc
index eac47c00..4a6e1959 100644
--- a/content/renderer/render_frame_impl_browsertest.cc
+++ b/content/renderer/render_frame_impl_browsertest.cc
@@ -865,6 +865,8 @@
       blink::mojom::FrameHostTestInterfaceRequest request) override {
     binder_callback_.Run(std::move(request));
   }
+  void GetAudioContextManager(
+      blink::mojom::AudioContextManagerRequest) override {}
 
   mojo::Binding<blink::mojom::DocumentInterfaceBroker> binding_;
   BinderCallback binder_callback_;
diff --git a/content/renderer/render_view_impl.cc b/content/renderer/render_view_impl.cc
index 91c52f9d..a6bbc19f 100644
--- a/content/renderer/render_view_impl.cc
+++ b/content/renderer/render_view_impl.cc
@@ -1714,6 +1714,13 @@
   return true;
 }
 
+// TODO(https://crbug.com/937569): Remove this in Chrome 82.
+bool RenderViewImpl::AllowPopupsDuringPageUnload() {
+  const base::CommandLine& command_line =
+      *base::CommandLine::ForCurrentProcess();
+  return command_line.HasSwitch(switches::kAllowPopupsDuringPageUnload);
+}
+
 bool RenderViewImpl::CanUpdateLayout() {
   return true;
 }
diff --git a/content/renderer/render_view_impl.h b/content/renderer/render_view_impl.h
index bd4dfd1..cb703eb 100644
--- a/content/renderer/render_view_impl.h
+++ b/content/renderer/render_view_impl.h
@@ -260,6 +260,7 @@
   void DidFocus(blink::WebLocalFrame* calling_frame) override;
   blink::WebScreenInfo GetScreenInfo() override;
   bool CanHandleGestureEvent() override;
+  bool AllowPopupsDuringPageUnload() override;
 
 #if defined(OS_ANDROID)
   // Only used on Android since all other platforms implement
diff --git a/content/renderer/render_widget_fullscreen_pepper.cc b/content/renderer/render_widget_fullscreen_pepper.cc
index 7ab3b4d5..d24067e 100644
--- a/content/renderer/render_widget_fullscreen_pepper.cc
+++ b/content/renderer/render_widget_fullscreen_pepper.cc
@@ -392,12 +392,12 @@
   // viewport.
   gfx::Size layer_size = gfx::Rect(ViewRect()).size();
   // When IsUseZoomForDSFEnabled() is true, layout and compositor layer sizes
-  // given by blink are all in physical pixels. When IsUseZoomForDSFEnabled() is
-  // false, layout and compositor layer sizes given by blink are all in DIP, and
-  // the compositor scales them internally by the device scale factor.
-  if (!compositor_deps()->IsUseZoomForDSFEnabled()) {
-    layer_size = gfx::ConvertSizeToDIP(
-        GetOriginalScreenInfo().device_scale_factor, layer_size);
+  // given by blink are all in physical pixels, and the compositor does not do
+  // any scaling. But the ViewRect() is always in DIP so we must scale the layer
+  // here as the compositor won't.
+  if (compositor_deps()->IsUseZoomForDSFEnabled()) {
+    layer_size = gfx::ScaleToCeiledSize(
+        layer_size, GetOriginalScreenInfo().device_scale_factor);
   }
   layer_->SetBounds(layer_size);
 }
diff --git a/content/renderer/service_worker/service_worker_context_client.cc b/content/renderer/service_worker/service_worker_context_client.cc
index 8aaf40e..0c503a61 100644
--- a/content/renderer/service_worker/service_worker_context_client.cc
+++ b/content/renderer/service_worker/service_worker_context_client.cc
@@ -563,7 +563,7 @@
 
 void ServiceWorkerContextClient::ReportConsoleMessage(
     int source,
-    int level,
+    blink::mojom::ConsoleMessageLevel level,
     const blink::WebString& message,
     int line_number,
     const blink::WebString& source_url) {
diff --git a/content/renderer/service_worker/service_worker_context_client.h b/content/renderer/service_worker/service_worker_context_client.h
index 333c3e5..be31fe2 100644
--- a/content/renderer/service_worker/service_worker_context_client.h
+++ b/content/renderer/service_worker/service_worker_context_client.h
@@ -117,7 +117,7 @@
                        int column_number,
                        const blink::WebString& source_url) override;
   void ReportConsoleMessage(int source,
-                            int level,
+                            blink::mojom::ConsoleMessageLevel level,
                             const blink::WebString& message,
                             int line_number,
                             const blink::WebString& source_url) override;
diff --git a/content/shell/android/BUILD.gn b/content/shell/android/BUILD.gn
index 6be386b4..d1e5690 100644
--- a/content/shell/android/BUILD.gn
+++ b/content/shell/android/BUILD.gn
@@ -71,6 +71,7 @@
     ":content_shell_java_resources",
     ":content_shell_manifest",
     "//base:base_java",
+    "//components/download/internal/common:internal_java",
     "//components/embedder_support/android:content_view_java",
     "//components/embedder_support/android:view_java",
     "//components/viz/service:service_java",
diff --git a/content/test/BUILD.gn b/content/test/BUILD.gn
index 62dbdc50..418dbabc 100644
--- a/content/test/BUILD.gn
+++ b/content/test/BUILD.gn
@@ -935,6 +935,7 @@
     "../browser/webrtc/webrtc_video_capture_browsertest.cc",
     "../browser/webrtc/webrtc_video_capture_service_browsertest.cc",
     "../browser/webrtc/webrtc_video_capture_service_enumeration_browsertest.cc",
+    "../browser/webrtc/webrtc_video_capture_shared_device_browsertest.cc",
     "../browser/webrtc/webrtc_webcam_browsertest.cc",
     "../browser/webrtc/webrtc_webcam_browsertest.h",
     "../browser/webui/web_ui_mojo_browsertest.cc",
@@ -1030,6 +1031,7 @@
     "//services/service_manager/public/cpp",
     "//services/test/echo/public/mojom",
     "//services/video_capture/public/cpp",
+    "//services/video_capture/public/cpp:mocks",
     "//services/video_capture/public/mojom:constants",
     "//services/viz/privileged/interfaces",
     "//services/ws/public/cpp/gpu",
diff --git a/content/test/data/accessibility/event/listbox-next-expected-win.txt b/content/test/data/accessibility/event/listbox-next-expected-win.txt
index 9335db2..4e76ac7 100644
--- a/content/test/data/accessibility/event/listbox-next-expected-win.txt
+++ b/content/test/data/accessibility/event/listbox-next-expected-win.txt
@@ -1,6 +1,6 @@
 EVENT_OBJECT_FOCUS on <option> role=ROLE_SYSTEM_LISTITEM name="Orange" SELECTED,FOCUSED,FOCUSABLE,SELECTABLE PosInSet=2 SetSize=3
-EVENT_OBJECT_SELECTION on <option> role=ROLE_SYSTEM_LISTITEM name="Orange" SELECTED,FOCUSABLE,SELECTABLE PosInSet=2 SetSize=3
+EVENT_OBJECT_SELECTION on <option> role=ROLE_SYSTEM_LISTITEM name="Orange" SELECTED,FOCUSED,FOCUSABLE,SELECTABLE PosInSet=2 SetSize=3
 EVENT_OBJECT_SELECTIONWITHIN on <select> role=ROLE_SYSTEM_LIST FOCUSABLE IA2_STATE_VERTICAL SetSize=3
 EVENT_OBJECT_STATECHANGE on <option> role=ROLE_SYSTEM_LISTITEM name="Apple" FOCUSABLE,SELECTABLE PosInSet=1 SetSize=3
-EVENT_OBJECT_STATECHANGE on <option> role=ROLE_SYSTEM_LISTITEM name="Orange" SELECTED,FOCUSABLE,SELECTABLE PosInSet=2 SetSize=3
-IA2_EVENT_ACTIVE_DESCENDANT_CHANGED on <select> role=ROLE_SYSTEM_LIST FOCUSABLE IA2_STATE_VERTICAL SetSize=3
+EVENT_OBJECT_STATECHANGE on <option> role=ROLE_SYSTEM_LISTITEM name="Orange" SELECTED,FOCUSED,FOCUSABLE,SELECTABLE PosInSet=2 SetSize=3
+IA2_EVENT_ACTIVE_DESCENDANT_CHANGED on <select> role=ROLE_SYSTEM_LIST FOCUSABLE IA2_STATE_VERTICAL SetSize=3
\ No newline at end of file
diff --git a/content/test/data/feature-policy-main.html.mock-http-headers b/content/test/data/feature-policy-main.html.mock-http-headers
index c925f5cb..5c7b52e5 100644
--- a/content/test/data/feature-policy-main.html.mock-http-headers
+++ b/content/test/data/feature-policy-main.html.mock-http-headers
@@ -1,2 +1,2 @@
 HTTP/1.1 200 OK
-Feature-Policy: geolocation 'self' http://example.com/
+Feature-Policy: geolocation 'self' http://example.com; oversized-images 'self' http://example.com;
diff --git a/content/test/data/feature-policy1.html.mock-http-headers b/content/test/data/feature-policy1.html.mock-http-headers
index ec76592f..585b13b5 100644
--- a/content/test/data/feature-policy1.html.mock-http-headers
+++ b/content/test/data/feature-policy1.html.mock-http-headers
@@ -1,2 +1,2 @@
 HTTP/1.1 200 OK
-Feature-Policy: geolocation 'self'
+Feature-Policy: geolocation 'self'; oversized-images 'self';
diff --git a/content/test/data/feature-policy2.html.mock-http-headers b/content/test/data/feature-policy2.html.mock-http-headers
index 1eaf3d9..4dd410c9 100644
--- a/content/test/data/feature-policy2.html.mock-http-headers
+++ b/content/test/data/feature-policy2.html.mock-http-headers
@@ -1,2 +1,2 @@
 HTTP/1.1 200 OK
-Feature-Policy: geolocation *
+Feature-Policy: geolocation *; oversized-images *;
diff --git a/content/test/data/feature-policy3.html.mock-http-headers b/content/test/data/feature-policy3.html.mock-http-headers
index 1eaf3d9..4dd410c9 100644
--- a/content/test/data/feature-policy3.html.mock-http-headers
+++ b/content/test/data/feature-policy3.html.mock-http-headers
@@ -1,2 +1,2 @@
 HTTP/1.1 200 OK
-Feature-Policy: geolocation *
+Feature-Policy: geolocation *; oversized-images *;
diff --git a/device/bluetooth/BUILD.gn b/device/bluetooth/BUILD.gn
index ce3674b5..8291648b 100644
--- a/device/bluetooth/BUILD.gn
+++ b/device/bluetooth/BUILD.gn
@@ -235,7 +235,7 @@
 
   if (is_chromeos) {
     deps += [
-      "//chromeos:chromeos_constants",
+      "//chromeos/constants",
       "//chromeos/dbus",
     ]
   }
diff --git a/device/bluetooth/bluetooth_remote_gatt_characteristic_unittest.cc b/device/bluetooth/bluetooth_remote_gatt_characteristic_unittest.cc
index 96fbe64..e8c9d13 100644
--- a/device/bluetooth/bluetooth_remote_gatt_characteristic_unittest.cc
+++ b/device/bluetooth/bluetooth_remote_gatt_characteristic_unittest.cc
@@ -3,6 +3,8 @@
 // found in the LICENSE file.
 
 #include <stdint.h>
+
+#include <functional>
 #include <utility>
 
 #include "base/bind.h"
@@ -766,7 +768,7 @@
 
   characteristic1_->ReadRemoteCharacteristic(
       base::Bind(test_callback, GetReadValueCallback(Call::EXPECTED),
-                 base::ConstRef(observer)),
+                 std::cref(observer)),
       GetGattErrorCallback(Call::NOT_EXPECTED));
 
   std::vector<uint8_t> test_vector = {0, 1, 2, 3, 4, 0xf, 0xf0, 0xff};
diff --git a/device/fido/mac/touch_id_context.mm b/device/fido/mac/touch_id_context.mm
index d96ddbc..25cef5f 100644
--- a/device/fido/mac/touch_id_context.mm
+++ b/device/fido/mac/touch_id_context.mm
@@ -9,7 +9,6 @@
 #include "base/bind.h"
 #include "base/logging.h"
 #include "base/mac/foundation_util.h"
-#include "base/mac/sdk_forward_declarations.h"
 #include "base/memory/ptr_util.h"
 #include "base/sequenced_task_runner.h"
 #include "base/strings/sys_string_conversions.h"
@@ -50,12 +49,9 @@
 // static
 bool TouchIdContext::TouchIdAvailableImpl() {
   base::scoped_nsobject<LAContext> context([[LAContext alloc] init]);
-  bool available =
+  return
       [context canEvaluatePolicy:LAPolicyDeviceOwnerAuthenticationWithBiometrics
                            error:nil];
-  if (@available(macOS 10.13.2, *))
-    return available && [context biometryType] == LABiometryTypeTouchID;
-  return available;
 }
 
 // static
diff --git a/docs/callback.md b/docs/callback.md
index 475954c8..3f441b9 100644
--- a/docs/callback.md
+++ b/docs/callback.md
@@ -425,13 +425,13 @@
 
 ### Passing Parameters By Reference
 
-Const references are *copied* unless `base::ConstRef` is used. Example:
+References are *copied* unless `std::ref` or `std::cref` is used. Example:
 
 ```cpp
 void foo(const int& arg) { printf("%d %p\n", arg, &arg); }
 int n = 1;
 base::Closure has_copy = base::Bind(&foo, n);
-base::Closure has_ref = base::Bind(&foo, base::ConstRef(n));
+base::Closure has_ref = base::Bind(&foo, std::cref(n));
 n = 2;
 foo(n);                        // Prints "2 0xaaaaaaaaaaaa"
 has_copy.Run();                // Prints "1 0xbbbbbbbbbbbb"
@@ -439,9 +439,9 @@
 ```
 
 Normally parameters are copied in the closure.
-**DANGER**: `base::ConstRef` stores a const reference instead, referencing the
-original parameter. This means that you must ensure the object outlives the
-callback!
+**DANGER**: `std::ref` and `std::cref` store a (const) reference instead,
+referencing the original parameter. This means that you must ensure the object
+outlives the callback!
 
 ## Implementation notes
 
@@ -481,16 +481,16 @@
 an external smart pointer as a weak pointer.
 
 `base::UnwrapTraits<BoundObject>::Unwrap()` is called for each bound arguments
-right before `base::Callback` calls the target function. You can specialize
-this to define an argument wrapper such as `base::Unretained`,
-`base::ConstRef`, `base::Owned`, `base::RetainedRef` and `base::Passed`.
+right before `base::Callback` calls the target function. You can specialize this
+to define an argument wrapper such as `base::Unretained`, `base::Owned`,
+`base::RetainedRef` and `base::Passed`.
 
 ### How The Implementation Works:
 
 There are three main components to the system:
   1) The `base::Callback<>` classes.
   2) The `base::Bind()` functions.
-  3) The arguments wrappers (e.g., `base::Unretained()` and `base::ConstRef()`).
+  3) The arguments wrappers (e.g., `base::Unretained()` and `base::Owned()`).
 
 The Callback classes represent a generic function pointer. Internally, it
 stores a refcounted piece of state that represents the target function and all
@@ -527,16 +527,15 @@
 references. (Binding to non-const references is forbidden, see bind.h.)
 
 To change this behavior, we introduce a set of argument wrappers (e.g.,
-`base::Unretained()`, and `base::ConstRef()`).  These are simple container
-templates that are passed by value, and wrap a pointer to argument.  See the
-file-level comment in base/bind_helpers.h for more info.
+`base::Unretained()`).  These are simple container templates that are passed by
+value, and wrap a pointer to argument.  See the file-level comment in
+base/bind_helpers.h for more info.
 
 These types are passed to the `Unwrap()` functions to modify the behavior of
 `base::Bind()`.  The `Unwrap()` functions change behavior by doing partial
 specialization based on whether or not a parameter is a wrapper type.
 
-`base::ConstRef()` is similar to `tr1::cref`.  `base::Unretained()` is specific
-to Chromium.
+`base::Unretained()` is specific to Chromium.
 
 ### Missing Functionality
  - Binding arrays to functions that take a non-const pointer.
diff --git a/docs/clang_sheriffing.md b/docs/clang_sheriffing.md
new file mode 100644
index 0000000..e594178
--- /dev/null
+++ b/docs/clang_sheriffing.md
@@ -0,0 +1,143 @@
+# Clang Sheriffing
+
+Chromium bundles its own pre-built version of [Clang](clang.md). This is done so
+that Chromium developers have access to the latest and greatest developer tools
+provided by Clang and LLVM (ASan, CFI, coverage, etc). In order to [update the
+compiler](updating_clang.md) (roll clang), it has to be tested so that we can be
+confident that it works in the configurations that Chromium cares about.
+
+Fortunately, Chromium happens to be a pretty decent stress test for a C++
+compiler, so we maintain a [waterfall of
+builders](https://ci.chromium.org/p/chromium/g/chromium.clang/console) that
+continuously build fresh versions of Clang and use them to build and test
+Chromium. "Clang sheriffing" is the process of monitoring that waterfall,
+determining if any compile or test failures are due to an upstream compiler
+change, filing bugs upstream, and often reverting bad changes in LLVM. This
+document describes some of the processes and techniques for doing that.
+
+[TOC]
+
+## Is it the compiler?
+
+Chromium does not always build and pass tests in all configurations that
+everyone cares about. Some configurations simply take too long to build
+(ThinLTO) or be tested (dbg) on the CQ before committing. And, some tests are
+flaky. So, our console is often filled with red boxes, and the boxes don't
+always need to be green to roll clang.
+
+Oftentimes, if a bot is red with a test failure, it's not a bug in the compiler.
+To check this, the easiest and best thing to do is to try to find a
+corresponding builder that doesn't use ToT clang. For standard configurations,
+start on the waterfall that corresponds to the OS of the red bot, and search
+from there. If the failing bot is Google Chrome branded, go to the (Google
+internal) [official builder
+list](https://uberchromegw.corp.google.com/i/official.desktop.continuous/builders/)
+and start searching from there.
+
+If you are feeling charitable, you can try to see when the test failure was
+introduced by looking at the history in the bot. One way to do this is to add
+`?numbuilds=200` to the builder URL to see more history. If that isn't enough
+history, you can manually binary search build numbers by editing the URL until
+you find where the regression was introduced. If it's immediately clear what CL
+introduced the regression (i.e.  caused tests to fail reliably in the official
+build configuration), you can simply load the change in gerrit and revert it,
+linking to the first failing build that implicates the change being reverted.
+
+If the failure looks like a compiler bug, these are the common failures we see
+and what to do about them:
+
+1. compiler crash
+1. compiler warning change
+1. compiler error
+1. miscompile
+1. linker errors
+
+## Compiler crash
+
+This is probably the most common bug. The standard procedure is to do these
+things:
+
+1. Use `got_clang_revision` property from first red and last green build to find
+   upstream regression range
+1. File a crbug documenting the crash. Include the range, and any other bots
+   displaying the same symptoms.
+1. Collect the crash reproduction files and reproduce the crash locally. When
+   clang crashes, it prints something like this:
+   ```
+   PLEASE ATTACH THE FOLLOWING FILES TO THE BUG REPORT:
+   Preprocessed source(s) and associated run script(s) are located at:
+   clang: note: diagnostic msg: C:\src\tmp\t-8f292b.cpp
+   clang: note: diagnostic msg: C:\src\tmp\t-8f292b.sh
+   ```
+   If you re-run the shell script, it should reproduce the crash.
+1. Identify the revision that introduced the crash. First, look at the commit
+   messages in the LLVM revision range to see if one modifies the code near the
+   point of the crash. If so, try reverting it locally, rebuild, and run the
+   reproducer to see if the crash goes away.
+
+   If that doesn't work, use `git bisect`. Use this as a template for the bisect
+   run script:
+   ```shell
+   #!/bin/bash
+   cd $(dirname $0)  # get into llvm build dir
+   ninja -j900 clang || exit 125 # skip revisions that don't compile
+   ./t-8f292b.sh   # exit 0 if good, 0 if bad
+   ```
+1. Reply to the commit on `llvm-commits` or on the code review on
+   [Phabricator](https://reviews.llvm.org/) letting the author know that you
+   believe they introduced a regression, and that you will revert the patch and
+   provide a reduced reproducer.
+1. Revert the upstream LLVM change once you are confident that you have the
+   right culprit. Tell the patch author that you will provide a reproduction.
+1. Start a reduction using [CReduce](https://embed.cs.utah.edu/creduce/using/).
+   Follow the docs there for writing an interestingness test and use it to
+   reduce the input to something that can be provided upstream. Send the reduced
+   reproducer to the patch author.
+
+## Compiler warning change
+
+New Clang versions often find new bad code patterns to warn on. Chromium builds
+with `-Werror`, so improvements to warnings often turn into build failures in
+Chromium. Once you understand the code pattern Clang is complaining about, file
+a bug to do either fix or silence the new warning.
+
+If this is a completely new warning, disable it by adding `-Wno-NEW-WARNING` to
+[this list of disabled
+warnings](https://cs.chromium.org/chromium/src/build/config/compiler/BUILD.gn?l=1479)
+if `llvm_force_head_revision` is true. Here is [an
+example](https://chromium-review.googlesource.com/1251622). This will keep the
+ToT bots green while you decide what to do.
+
+Sometimes, behavior changes and a pre-existing warning changes to warn on new
+code. In this case, fixing Chromium may be the easiest and quickest fix. If
+there are many sites, you may consider changing clang to put the new diagnostic
+into a new warning group so you can handle it as a new warning as described
+above.
+
+If the warning is high value, then eventually our team or other contributors
+will end up fixing the crbug and there is nothing more to do.  If the warning
+seems low value, pass that feedback along to the author of the new warning
+upstream. It's unlikely that it should be on by default or enabled by `-Wall` if
+users don't find it valuable. If the warning is particularly noisy and can't be
+easily disabled without disabling other high value warnings, you should consider
+reverting the change upstream and asking for more discussion.
+
+## Compiler error
+
+This rarely happens, but sometimes clang becomes more strict and no longer
+accepts code that it previously did. The standard procedure for a new warning
+may apply, but it's more likely that the upstream Clang change should be
+reverted, if the C++ code in question in Chromium looks valid.
+
+## Miscompile
+
+Miscompiles tend to result in crashes, so if you see a test with the CRASHED
+status, this is probably what you want to do.
+
+1. Bisect object files to find the object with the code that changed.
+1. Debug it with a traditional debugger
+
+## Linker error
+
+TODO: Describe object file bisection, identify obj with symbol that no longer
+has the section.
diff --git a/extensions/browser/api/hid/hid_device_manager.cc b/extensions/browser/api/hid/hid_device_manager.cc
index 2ee53ba..da6f97f 100644
--- a/extensions/browser/api/hid/hid_device_manager.cc
+++ b/extensions/browser/api/hid/hid_device_manager.cc
@@ -6,6 +6,7 @@
 
 #include <stdint.h>
 
+#include <functional>
 #include <limits>
 #include <string>
 #include <utility>
@@ -364,7 +365,7 @@
   // safe to pass |device_info| by reference.
   event->will_dispatch_callback =
       base::BindRepeating(&WillDispatchDeviceEvent, weak_factory_.GetWeakPtr(),
-                          base::ConstRef(device_info));
+                          std::cref(device_info));
   event_router_->BroadcastEvent(std::move(event));
 }
 
diff --git a/extensions/browser/api/system_cpu/OWNERS b/extensions/browser/api/system_cpu/OWNERS
index 20685fc..c13ffb7 100644
--- a/extensions/browser/api/system_cpu/OWNERS
+++ b/extensions/browser/api/system_cpu/OWNERS
@@ -1 +1 @@
-hongbo.min@intel.com
+leon.han@intel.com
diff --git a/extensions/browser/api/system_display/OWNERS b/extensions/browser/api/system_display/OWNERS
index a2d9bbc..de646c5 100644
--- a/extensions/browser/api/system_display/OWNERS
+++ b/extensions/browser/api/system_display/OWNERS
@@ -1,3 +1,2 @@
-hongbo.min@intel.com
 stevenjb@chromium.org
 oshima@chromium.org
diff --git a/extensions/browser/api/system_info/OWNERS b/extensions/browser/api/system_info/OWNERS
index 20685fc..c13ffb7 100644
--- a/extensions/browser/api/system_info/OWNERS
+++ b/extensions/browser/api/system_info/OWNERS
@@ -1 +1 @@
-hongbo.min@intel.com
+leon.han@intel.com
diff --git a/extensions/browser/api/system_memory/OWNERS b/extensions/browser/api/system_memory/OWNERS
index 20685fc..c13ffb7 100644
--- a/extensions/browser/api/system_memory/OWNERS
+++ b/extensions/browser/api/system_memory/OWNERS
@@ -1 +1 @@
-hongbo.min@intel.com
+leon.han@intel.com
diff --git a/extensions/browser/api/system_storage/OWNERS b/extensions/browser/api/system_storage/OWNERS
index 71174fe..78bec85 100644
--- a/extensions/browser/api/system_storage/OWNERS
+++ b/extensions/browser/api/system_storage/OWNERS
@@ -1,3 +1,2 @@
-hongbo.min@intel.com
+leon.han@intel.com
 ningxin.hu@intel.com
-joshua.lock@intel.com
diff --git a/extensions/browser/api/usb/usb_device_manager.cc b/extensions/browser/api/usb/usb_device_manager.cc
index aef80a9..4f670f1 100644
--- a/extensions/browser/api/usb/usb_device_manager.cc
+++ b/extensions/browser/api/usb/usb_device_manager.cc
@@ -4,6 +4,7 @@
 
 #include "extensions/browser/api/usb/usb_device_manager.h"
 
+#include <functional>
 #include <memory>
 #include <utility>
 
@@ -268,8 +269,8 @@
                             usb::OnDeviceRemoved::Create(device_obj)));
     }
 
-    event->will_dispatch_callback = base::BindRepeating(
-        &WillDispatchDeviceEvent, base::ConstRef(device_info));
+    event->will_dispatch_callback =
+        base::BindRepeating(&WillDispatchDeviceEvent, std::cref(device_info));
     event_router->BroadcastEvent(std::move(event));
   }
 }
diff --git a/extensions/browser/app_window/app_window.cc b/extensions/browser/app_window/app_window.cc
index f94378214..f39f0e9 100644
--- a/extensions/browser/app_window/app_window.cc
+++ b/extensions/browser/app_window/app_window.cc
@@ -881,6 +881,10 @@
   }
 }
 
+void AppWindow::ActivateContents(WebContents* contents) {
+  native_app_window_->Activate();
+}
+
 void AppWindow::CloseContents(WebContents* contents) {
   native_app_window_->Close();
 }
diff --git a/extensions/browser/app_window/app_window.h b/extensions/browser/app_window/app_window.h
index 17948da..bd6c41e 100644
--- a/extensions/browser/app_window/app_window.h
+++ b/extensions/browser/app_window/app_window.h
@@ -391,6 +391,7 @@
   friend class PlatformAppBrowserTest;
 
   // content::WebContentsDelegate implementation.
+  void ActivateContents(content::WebContents* contents) override;
   void CloseContents(content::WebContents* contents) override;
   bool ShouldSuppressDialogs(content::WebContents* source) override;
   content::ColorChooser* OpenColorChooser(
diff --git a/extensions/browser/extension_function_histogram_value.h b/extensions/browser/extension_function_histogram_value.h
index ea305de..21c3096 100644
--- a/extensions/browser/extension_function_histogram_value.h
+++ b/extensions/browser/extension_function_histogram_value.h
@@ -355,7 +355,7 @@
   SOCKET_READ = 294,
   DELETED_EXPERIMENTAL_PROCESSES_TERMINATE = 295,
   METRICSPRIVATE_RECORDTIME = 296,
-  BOOKMARKMANAGERPRIVATE_GETSTRINGS = 297,
+  DELETED_BOOKMARKMANAGERPRIVATE_GETSTRINGS = 297,
   USB_ISOCHRONOUSTRANSFER = 298,
   PERMISSIONS_REMOVE = 299,
   MANAGEMENT_UNINSTALL = 300,
diff --git a/extensions/shell/BUILD.gn b/extensions/shell/BUILD.gn
index 691b82a..de406cea 100644
--- a/extensions/shell/BUILD.gn
+++ b/extensions/shell/BUILD.gn
@@ -227,8 +227,8 @@
       "browser/shell_screen.h",
     ]
     deps += [
-      "//chromeos:chromeos_constants",
       "//chromeos/audio",
+      "//chromeos/constants",
       "//chromeos/dbus",
       "//chromeos/disks",
       "//chromeos/login/login_state",
@@ -356,8 +356,8 @@
       "browser/shell_screen_unittest.cc",
     ]
     deps += [
-      "//chromeos:chromeos_constants",
       "//chromeos/audio",
+      "//chromeos/constants",
       "//chromeos/dbus:test_support",
     ]
   }
@@ -428,7 +428,7 @@
 
   if (is_chromeos) {
     deps += [
-      "//chromeos:chromeos_constants",
+      "//chromeos/constants",
       "//components/keyed_service/content",
     ]
   }
diff --git a/fuchsia/base/agent_impl.cc b/fuchsia/base/agent_impl.cc
index 77b4f57..4c9623a 100644
--- a/fuchsia/base/agent_impl.cc
+++ b/fuchsia/base/agent_impl.cc
@@ -20,11 +20,22 @@
 
   // Tear down this instance when the client disconnects from the directory.
   service_provider_->SetOnLastClientDisconnectedClosure(base::BindOnce(
-      &ComponentStateBase::OnComponentDisconnected, base::Unretained(this)));
+      &ComponentStateBase::TeardownIfUnused, base::Unretained(this)));
 }
 
-void AgentImpl::ComponentStateBase::OnComponentDisconnected() {
+void AgentImpl::ComponentStateBase::TeardownIfUnused() {
   DCHECK(agent_impl_);
+
+  // Don't teardown if the ServiceProvider has client(s).
+  if (service_provider_->has_clients())
+    return;
+
+  // Don't teardown if caller-specified bindings still have clients.
+  for (auto& keepalive_callback : keepalive_callbacks_) {
+    if (keepalive_callback.Run())
+      return;
+  }
+
   agent_impl_->DeleteComponentState(component_id_);
   // Do not touch |this|, since it is already gone.
 }
diff --git a/fuchsia/base/agent_impl.h b/fuchsia/base/agent_impl.h
index 9b578ff..4bea609 100644
--- a/fuchsia/base/agent_impl.h
+++ b/fuchsia/base/agent_impl.h
@@ -63,15 +63,31 @@
       return &service_directory_;
     }
 
+    // Registers |service_binding| to prevent teardown of this
+    // ComponentStateBase while it has one or more clients. |service_binding|
+    // will typically be the ScopedServiceBinding<> of a critical service used
+    // by the component.
+    template <typename T>
+    void AddKeepAliveBinding(T* service_binding) {
+      keepalive_callbacks_.push_back(base::BindRepeating(
+          &T::has_clients, base::Unretained(service_binding)));
+      service_binding->SetOnLastClientCallback(base::BindRepeating(
+          &ComponentStateBase::TeardownIfUnused, base::Unretained(this)));
+    }
+
    private:
     friend class AgentImpl;
 
-    void OnComponentDisconnected();
+    // Tears down this instance if there are no ServiceProvider clients, and
+    // no |keepalive_callbacks_| indicate that there are no clients of
+    // bindings that were configured to keep us alive.
+    void TeardownIfUnused();
 
     const std::string component_id_;
     AgentImpl* agent_impl_ = nullptr;
     base::fuchsia::ServiceDirectory service_directory_;
     std::unique_ptr<base::fuchsia::ServiceProviderImpl> service_provider_;
+    std::vector<base::RepeatingCallback<bool()>> keepalive_callbacks_;
 
     DISALLOW_COPY_AND_ASSIGN(ComponentStateBase);
   };
diff --git a/fuchsia/base/agent_impl_unittests.cc b/fuchsia/base/agent_impl_unittests.cc
index acc12892..6e97672 100644
--- a/fuchsia/base/agent_impl_unittests.cc
+++ b/fuchsia/base/agent_impl_unittests.cc
@@ -21,6 +21,7 @@
 const char kNoServicesComponentId[] = "no-services";
 const char kAccumulatorComponentId1[] = "accumulator1";
 const char kAccumulatorComponentId2[] = "accumulator2";
+const char kKeepAliveComponentId[] = "keep-alive";
 
 class EmptyComponentState : public AgentImpl::ComponentStateBase {
  public:
@@ -51,12 +52,20 @@
       : ComponentStateBase(component),
         service_binding_(service_directory(), &service_) {}
 
- private:
+ protected:
   AccumulatingTestInterfaceImpl service_;
   base::fuchsia::ScopedServiceBinding<base::fuchsia::testfidl::TestInterface>
       service_binding_;
 };
 
+class KeepAliveComponentState : public AccumulatorComponentState {
+ public:
+  KeepAliveComponentState(base::StringPiece component)
+      : AccumulatorComponentState(component) {
+    AddKeepAliveBinding(&service_binding_);
+  }
+};
+
 class AgentImplTest : public ::testing::Test {
  public:
   AgentImplTest() {
@@ -85,6 +94,8 @@
     } else if (component_id == kAccumulatorComponentId1 ||
                component_id == kAccumulatorComponentId2) {
       return std::make_unique<AccumulatorComponentState>(component_id);
+    } else if (component_id == kKeepAliveComponentId) {
+      return std::make_unique<KeepAliveComponentState>(component_id);
     }
     return nullptr;
   }
@@ -203,10 +214,14 @@
       test_interface1.NewRequest().TakeChannel());
 
   // Both TestInterface pointers should remain valid until we are done.
-  test_interface1.set_error_handler(
-      [](zx_status_t status) { ZX_LOG(FATAL, status); });
-  test_interface2.set_error_handler(
-      [](zx_status_t status) { ZX_LOG(FATAL, status); });
+  test_interface1.set_error_handler([](zx_status_t status) {
+    ZX_LOG(ERROR, status);
+    ADD_FAILURE();
+  });
+  test_interface2.set_error_handler([](zx_status_t status) {
+    ZX_LOG(ERROR, status);
+    ADD_FAILURE();
+  });
 
   // Call Add() via one TestInterface and verify accumulator had been at zero.
   {
@@ -255,10 +270,14 @@
       test_interface1.NewRequest().TakeChannel());
 
   // Both TestInterface pointers should remain valid until we are done.
-  test_interface1.set_error_handler(
-      [](zx_status_t status) { ZX_LOG(FATAL, status); });
-  test_interface2.set_error_handler(
-      [](zx_status_t status) { ZX_LOG(FATAL, status); });
+  test_interface1.set_error_handler([](zx_status_t status) {
+    ZX_LOG(ERROR, status);
+    ADD_FAILURE();
+  });
+  test_interface2.set_error_handler([](zx_status_t status) {
+    ZX_LOG(ERROR, status);
+    ADD_FAILURE();
+  });
 
   // Call Add() via one TestInterface and verify accumulator had been at zero.
   {
@@ -285,4 +304,54 @@
   test_interface2.set_error_handler(nullptr);
 }
 
+// Verify that connections to a service registered to kee-alive the
+// ComponentStateBase keeps it alive after the ServiceProvider is dropped.
+TEST_F(AgentImplTest, KeepAliveBinding) {
+  fuchsia::modular::AgentPtr agent = CreateAgentAndConnect();
+
+  {
+    // Connect to the Agent and request the TestInterface.
+    fuchsia::sys::ServiceProviderPtr component_services;
+    agent->Connect(kAccumulatorComponentId1, component_services.NewRequest());
+    base::fuchsia::testfidl::TestInterfacePtr test_interface;
+    component_services->ConnectToService(
+        base::fuchsia::testfidl::TestInterface::Name_,
+        test_interface.NewRequest().TakeChannel());
+
+    // The TestInterface pointer should be closed as soon as we Unbind() the
+    // ServiceProvider.
+    test_interface.set_error_handler(
+        [](zx_status_t status) { EXPECT_EQ(status, ZX_ERR_PEER_CLOSED); });
+
+    // After disconnecting ServiceProvider, TestInterface should remain valid.
+    component_services.Unbind();
+    base::RunLoop().RunUntilIdle();
+    EXPECT_FALSE(test_interface);
+  }
+
+  {
+    // Connect to the Agent and request the TestInterface.
+    fuchsia::sys::ServiceProviderPtr component_services;
+    agent->Connect(kKeepAliveComponentId, component_services.NewRequest());
+    base::fuchsia::testfidl::TestInterfacePtr test_interface;
+    component_services->ConnectToService(
+        base::fuchsia::testfidl::TestInterface::Name_,
+        test_interface.NewRequest().TakeChannel());
+
+    // The TestInterface pointer should remain valid until we are done.
+    test_interface.set_error_handler([](zx_status_t status) {
+      ZX_LOG(ERROR, status);
+      ADD_FAILURE();
+    });
+
+    // After disconnecting ServiceProvider, TestInterface should remain valid.
+    component_services.Unbind();
+    base::RunLoop().RunUntilIdle();
+    EXPECT_TRUE(test_interface);
+  }
+
+  // Spin the MessageLoop to let the AgentImpl see that TestInterface is gone.
+  base::RunLoop().RunUntilIdle();
+}
+
 }  // namespace cr_fuchsia
diff --git a/fuchsia/fidl/cast/queryable_data.fidl b/fuchsia/fidl/cast/queryable_data.fidl
index 8d27c0b..c7148a139 100644
--- a/fuchsia/fidl/cast/queryable_data.fidl
+++ b/fuchsia/fidl/cast/queryable_data.fidl
@@ -13,7 +13,10 @@
 /// to CastRunner instances.
 [Discoverable]
 protocol QueryableData {
-  /// Gets all entries managed by the QueryableData service.
-  GetAllEntries() -> (vector<QueryableDataEntry> values);
+  /// Gets a list of changed entries since the last call to GetChangedEntries,
+  /// delaying execution of the the pending callback until a change becomes
+  /// available. The initial call will return all entries managed by the
+  /// QueryableData service.
+  GetChangedEntries() -> (vector<QueryableDataEntry> values);
 };
 
diff --git a/fuchsia/runners/cast/fake_queryable_data.cc b/fuchsia/runners/cast/fake_queryable_data.cc
index b83fe23..7b36dae 100644
--- a/fuchsia/runners/cast/fake_queryable_data.cc
+++ b/fuchsia/runners/cast/fake_queryable_data.cc
@@ -21,7 +21,7 @@
   entries_[key.as_string()] = cur_entry_converted;
 }
 
-void FakeQueryableData::GetAllEntries(GetAllEntriesCallback callback) {
+void FakeQueryableData::GetChangedEntries(GetChangedEntriesCallback callback) {
   std::vector<chromium::cast::QueryableDataEntry> output;
   for (const auto& e : entries_) {
     output.push_back(e.second);
diff --git a/fuchsia/runners/cast/fake_queryable_data.h b/fuchsia/runners/cast/fake_queryable_data.h
index a7a02ce..32f5522e 100644
--- a/fuchsia/runners/cast/fake_queryable_data.h
+++ b/fuchsia/runners/cast/fake_queryable_data.h
@@ -23,7 +23,7 @@
   void Add(base::StringPiece key, const base::Value& value);
 
   // chromium::cast::QueryableData implementation.
-  void GetAllEntries(GetAllEntriesCallback callback) override;
+  void GetChangedEntries(GetChangedEntriesCallback callback) override;
 
  private:
   std::map<std::string, chromium::cast::QueryableDataEntry> entries_;
diff --git a/fuchsia/runners/cast/queryable_data_bindings.cc b/fuchsia/runners/cast/queryable_data_bindings.cc
index e9201d9..0b296c1 100644
--- a/fuchsia/runners/cast/queryable_data_bindings.cc
+++ b/fuchsia/runners/cast/queryable_data_bindings.cc
@@ -32,7 +32,7 @@
   CHECK(base::PathService::Get(base::DIR_ASSETS, &assets_path));
   InjectJavaScriptFileIntoFrame(assets_path.AppendASCII(kBindingsPath), frame_);
 
-  service_->GetAllEntries(
+  service_->GetChangedEntries(
       fit::bind_member(this, &QueryableDataBindings::OnEntriesReceived));
 }
 
@@ -75,4 +75,7 @@
 
   InjectJavaScriptBufferIntoFrame(std::move(initialize_values_js_buffer),
                                   frame_);
+
+  // TODO(crbug.com/929291): Handle change events by calling and waiting on
+  // GetChangedEntries() here.
 }
diff --git a/google_apis/gcm/tools/mcs_probe.cc b/google_apis/gcm/tools/mcs_probe.cc
index 0b36f38..3f57939 100644
--- a/google_apis/gcm/tools/mcs_probe.cc
+++ b/google_apis/gcm/tools/mcs_probe.cc
@@ -338,8 +338,7 @@
 
   host_resolver_ = net::HostResolver::CreateDefaultResolver(&net_log_);
   http_auth_handler_factory_ = net::HttpAuthHandlerRegistryFactory::Create(
-      host_resolver_.get(), &http_auth_preferences_,
-      std::vector<std::string>{net::kBasicAuthScheme});
+      &http_auth_preferences_, std::vector<std::string>{net::kBasicAuthScheme});
 
   net::URLRequestContextBuilder builder;
   builder.set_net_log(&net_log_);
diff --git a/gpu/command_buffer/service/shared_image_factory.cc b/gpu/command_buffer/service/shared_image_factory.cc
index e1f2b67..8605158 100644
--- a/gpu/command_buffer/service/shared_image_factory.cc
+++ b/gpu/command_buffer/service/shared_image_factory.cc
@@ -105,9 +105,17 @@
   std::unique_ptr<SharedImageBacking> backing;
   bool using_wrapped_sk_image = wrapped_sk_image_factory_ &&
                                 (usage & SHARED_IMAGE_USAGE_OOP_RASTERIZATION);
-  bool using_interop_factory = using_vulkan_ &&
-                               (usage & SHARED_IMAGE_USAGE_GLES2) &&
+  // If |shared_image_manager_| is thread safe, it means the display is running
+  // on a separate thread (which uses a separate GL context or VkDeviceQueue).
+  bool share_between_threads = shared_image_manager_->is_thread_safe() &&
                                (usage & SHARED_IMAGE_USAGE_DISPLAY);
+  bool share_between_gl_vulkan = using_vulkan_ &&
+                                 (usage & SHARED_IMAGE_USAGE_GLES2) &&
+                                 (usage & SHARED_IMAGE_USAGE_DISPLAY);
+  bool using_interop_factory = share_between_threads || share_between_gl_vulkan;
+  // TODO(penghuang): make sure all shared image are created with correct usage.
+  // https://crbug.com/937480
+  // using_interop_factory = shared_image_manager_->is_thread_safe();
   if (using_wrapped_sk_image) {
     backing = wrapped_sk_image_factory_->CreateSharedImage(
         mailbox, format, size, color_space, usage);
diff --git a/gpu/config/gpu_finch_features.cc b/gpu/config/gpu_finch_features.cc
index 087c594..168f361 100644
--- a/gpu/config/gpu_finch_features.cc
+++ b/gpu/config/gpu_finch_features.cc
@@ -3,6 +3,10 @@
 // found in the LICENSE file.
 #include "gpu/config/gpu_finch_features.h"
 
+#if defined(OS_ANDROID)
+#include "ui/gl/android/android_surface_control_compat.h"
+#endif
+
 namespace features {
 #if defined(OS_ANDROID)
 // Use android AImageReader when playing videos with MediaPlayer.
@@ -74,4 +78,11 @@
 const base::Feature kVaapiJpegImageDecodeAcceleration{
     "VaapiJpegImageDecodeAcceleration", base::FEATURE_DISABLED_BY_DEFAULT};
 
+#if defined(OS_ANDROID)
+bool IsAndroidSurfaceControlEnabled() {
+  return base::FeatureList::IsEnabled(kAndroidSurfaceControl) &&
+         gl::SurfaceControl::IsSupported();
+}
+#endif
+
 }  // namespace features
diff --git a/gpu/config/gpu_finch_features.h b/gpu/config/gpu_finch_features.h
index 68efa19ae..1e66891 100644
--- a/gpu/config/gpu_finch_features.h
+++ b/gpu/config/gpu_finch_features.h
@@ -39,6 +39,10 @@
 
 GPU_EXPORT extern const base::Feature kVaapiJpegImageDecodeAcceleration;
 
+#if defined(OS_ANDROID)
+GPU_EXPORT bool IsAndroidSurfaceControlEnabled();
+#endif
+
 }  // namespace features
 
 #endif  // GPU_CONFIG_GPU_FEATURES_H_
diff --git a/gpu/config/gpu_util.cc b/gpu/config/gpu_util.cc
index 25683692..7c5e2d9 100644
--- a/gpu/config/gpu_util.cc
+++ b/gpu/config/gpu_util.cc
@@ -51,9 +51,7 @@
   if (!gpu_preferences.enable_android_surface_control)
     return kGpuFeatureStatusDisabled;
 
-  if (!gl::SurfaceControl::IsSupported())
-    return kGpuFeatureStatusDisabled;
-
+  DCHECK(gl::SurfaceControl::IsSupported());
   return kGpuFeatureStatusEnabled;
 #endif
 }
diff --git a/gpu/ipc/service/gpu_channel_manager.cc b/gpu/ipc/service/gpu_channel_manager.cc
index c87ddfb8..c185b57 100644
--- a/gpu/ipc/service/gpu_channel_manager.cc
+++ b/gpu/ipc/service/gpu_channel_manager.cc
@@ -60,6 +60,7 @@
     scoped_refptr<base::SingleThreadTaskRunner> io_task_runner,
     Scheduler* scheduler,
     SyncPointManager* sync_point_manager,
+    SharedImageManager* shared_image_manager,
     GpuMemoryBufferFactory* gpu_memory_buffer_factory,
     const GpuFeatureInfo& gpu_feature_info,
     GpuProcessActivityFlags activity_flags,
@@ -77,6 +78,7 @@
       mailbox_manager_(gles2::CreateMailboxManager(gpu_preferences)),
       scheduler_(scheduler),
       sync_point_manager_(sync_point_manager),
+      shared_image_manager_(shared_image_manager),
       shader_translator_cache_(gpu_preferences_),
       default_offscreen_surface_(std::move(default_offscreen_surface)),
       gpu_memory_buffer_factory_(gpu_memory_buffer_factory),
diff --git a/gpu/ipc/service/gpu_channel_manager.h b/gpu/ipc/service/gpu_channel_manager.h
index cae718b..5b00e29d 100644
--- a/gpu/ipc/service/gpu_channel_manager.h
+++ b/gpu/ipc/service/gpu_channel_manager.h
@@ -27,7 +27,6 @@
 #include "gpu/command_buffer/service/service_discardable_manager.h"
 #include "gpu/command_buffer/service/shader_translator_cache.h"
 #include "gpu/command_buffer/service/shared_context_state.h"
-#include "gpu/command_buffer/service/shared_image_manager.h"
 #include "gpu/config/gpu_driver_bug_workarounds.h"
 #include "gpu/config/gpu_feature_info.h"
 #include "gpu/config/gpu_preferences.h"
@@ -75,6 +74,7 @@
       scoped_refptr<base::SingleThreadTaskRunner> io_task_runner,
       Scheduler* scheduler,
       SyncPointManager* sync_point_manager,
+      SharedImageManager* shared_image_manager,
       GpuMemoryBufferFactory* gpu_memory_buffer_factory,
       const GpuFeatureInfo& gpu_feature_info,
       GpuProcessActivityFlags activity_flags,
@@ -150,7 +150,7 @@
 
   SyncPointManager* sync_point_manager() const { return sync_point_manager_; }
 
-  SharedImageManager* shared_image_manager() { return &shared_image_manager_; }
+  SharedImageManager* shared_image_manager() { return shared_image_manager_; }
 
   // Retrieve GPU Resource consumption statistics for the task manager
   void GetVideoMemoryUsageStats(
@@ -203,7 +203,8 @@
   std::unique_ptr<gles2::Outputter> outputter_;
   Scheduler* scheduler_;
   // SyncPointManager guaranteed to outlive running MessageLoop.
-  SyncPointManager* sync_point_manager_;
+  SyncPointManager* const sync_point_manager_;
+  SharedImageManager* const shared_image_manager_;
   std::unique_ptr<gles2::ProgramCache> program_cache_;
   gles2::ShaderTranslatorCache shader_translator_cache_;
   gles2::FramebufferCompletenessCache framebuffer_completeness_cache_;
@@ -212,7 +213,6 @@
   GpuFeatureInfo gpu_feature_info_;
   ServiceDiscardableManager discardable_manager_;
   PassthroughDiscardableManager passthrough_discardable_manager_;
-  SharedImageManager shared_image_manager_;
 #if defined(OS_ANDROID)
   // Last time we know the GPU was powered on. Global for tracking across all
   // transport surfaces.
diff --git a/gpu/ipc/service/gpu_channel_test_common.cc b/gpu/ipc/service/gpu_channel_test_common.cc
index 2d970a8..c9e8273 100644
--- a/gpu/ipc/service/gpu_channel_test_common.cc
+++ b/gpu/ipc/service/gpu_channel_test_common.cc
@@ -9,6 +9,7 @@
 #include "base/threading/thread_task_runner_handle.h"
 #include "gpu/command_buffer/common/activity_flags.h"
 #include "gpu/command_buffer/service/scheduler.h"
+#include "gpu/command_buffer/service/shared_image_manager.h"
 #include "gpu/command_buffer/service/sync_point_manager.h"
 #include "gpu/ipc/service/gpu_channel.h"
 #include "gpu/ipc/service/gpu_channel_manager.h"
@@ -58,6 +59,7 @@
     : task_runner_(new base::TestSimpleTaskRunner),
       io_task_runner_(new base::TestSimpleTaskRunner),
       sync_point_manager_(new SyncPointManager()),
+      shared_image_manager_(new SharedImageManager(false /* thread_safe */)),
       scheduler_(new Scheduler(task_runner_, sync_point_manager_.get())),
       channel_manager_delegate_(new TestGpuChannelManagerDelegate()) {
   // We need GL bindings to actually initialize command buffers.
@@ -73,7 +75,8 @@
   channel_manager_.reset(new GpuChannelManager(
       GpuPreferences(), channel_manager_delegate_.get(), nullptr, /* watchdog */
       task_runner_.get(), io_task_runner_.get(), scheduler_.get(),
-      sync_point_manager_.get(), nullptr, /* gpu_memory_buffer_factory */
+      sync_point_manager_.get(), shared_image_manager_.get(),
+      nullptr, /* gpu_memory_buffer_factory */
       std::move(feature_info), GpuProcessActivityFlags(),
       gl::init::CreateOffscreenGLSurface(gfx::Size()),
       nullptr /* image_decode_accelerator_worker */));
diff --git a/gpu/ipc/service/gpu_channel_test_common.h b/gpu/ipc/service/gpu_channel_test_common.h
index fc54ec0..9ab01faf 100644
--- a/gpu/ipc/service/gpu_channel_test_common.h
+++ b/gpu/ipc/service/gpu_channel_test_common.h
@@ -26,6 +26,7 @@
 class GpuChannelManager;
 class Scheduler;
 class SyncPointManager;
+class SharedImageManager;
 class TestGpuChannelManagerDelegate;
 
 class GpuChannelTestCommon : public testing::Test {
@@ -55,6 +56,7 @@
   scoped_refptr<base::TestSimpleTaskRunner> task_runner_;
   scoped_refptr<base::TestSimpleTaskRunner> io_task_runner_;
   std::unique_ptr<SyncPointManager> sync_point_manager_;
+  std::unique_ptr<SharedImageManager> shared_image_manager_;
   std::unique_ptr<Scheduler> scheduler_;
   std::unique_ptr<TestGpuChannelManagerDelegate> channel_manager_delegate_;
   std::unique_ptr<GpuChannelManager> channel_manager_;
diff --git a/infra/config/cr-buildbucket.cfg b/infra/config/cr-buildbucket.cfg
index aef1acc0..8029899 100644
--- a/infra/config/cr-buildbucket.cfg
+++ b/infra/config/cr-buildbucket.cfg
@@ -1647,11 +1647,6 @@
       mixins: "win"
     }
     builders {
-      name: "CrWinAsanCov"
-      mixins: "clang-ci"
-      mixins: "win"
-    }
-    builders {
       name: "ToTAndroid"
       mixins: "clang-ci"
       mixins: "linux"
diff --git a/infra/config/luci-milo.cfg b/infra/config/luci-milo.cfg
index e8b2ea0..44cbfe1e 100644
--- a/infra/config/luci-milo.cfg
+++ b/infra/config/luci-milo.cfg
@@ -1975,11 +1975,6 @@
     short_name: "dll"
   }
   builders {
-    name: "buildbucket/luci.chromium.ci/ToTWinASanLibfuzzer"
-    category: "ToT Windows|x64"
-    short_name: "fuz"
-  }
-  builders {
     name: "buildbucket/luci.chrome.ci/ToTWinThinLTO64"
     category: "ToT Windows|x64"
     short_name: "lto"
@@ -2001,9 +1996,9 @@
     short_name: "dll"
   }
   builders {
-    name: "buildbucket/luci.chromium.ci/CrWinAsanCov"
+    name: "buildbucket/luci.chromium.ci/ToTWinASanLibfuzzer"
     category: "ToT Windows|Asan"
-    short_name: "cov"
+    short_name: "fuz"
   }
   builders {
     name: "buildbot/chromium.clang/linux-win_cross-rel"
@@ -2212,9 +2207,9 @@
     short_name: "dll"
   }
   builders {
-    name: "buildbucket/luci.chromium.ci/CrWinAsanCov"
+    name: "buildbucket/luci.chromium.ci/ToTWinASanLibfuzzer"
     category: "ToT Windows|Asan"
-    short_name: "cov"
+    short_name: "fuz"
   }
   builders {
     name: "buildbucket/luci.chromium.ci/linux-win_cross-rel"
diff --git a/infra/config/luci-scheduler.cfg b/infra/config/luci-scheduler.cfg
index a49cdb7..d614afb 100644
--- a/infra/config/luci-scheduler.cfg
+++ b/infra/config/luci-scheduler.cfg
@@ -134,7 +134,6 @@
   triggers: "Closure Compilation Linux"
   triggers: "CrWinAsan"
   triggers: "CrWinAsan(dll)"
-  triggers: "CrWinAsanCov"
   triggers: "CrWinGomaStaging"
   triggers: "Deterministic Android (dbg)"
   triggers: "Deterministic Android"
@@ -4681,16 +4680,6 @@
 }
 
 job {
-  id: "CrWinAsanCov"
-  acl_sets: "default"
-  buildbucket {
-    server: "cr-buildbucket.appspot.com"
-    bucket: "luci.chromium.ci"
-    builder: "CrWinAsanCov"
-  }
-}
-
-job {
   id: "ToTAndroid"
   acl_sets: "default"
   buildbucket {
diff --git a/ios/chrome/app/strings/ios_strings.grd b/ios/chrome/app/strings/ios_strings.grd
index 141f854..01b7ae3 100644
--- a/ios/chrome/app/strings/ios_strings.grd
+++ b/ios/chrome/app/strings/ios_strings.grd
@@ -857,7 +857,7 @@
         No Username
       </message>
       <message name="IDS_IOS_MANUAL_FALLBACK_NO_PASSWORDS_FOR_SITE" desc="The title for disabled buttons when a list of credentials for manual fallback is empty. [30em]">
-        No passwords for this site found
+        No passwords found for this site
       </message>
       <message name="IDS_IOS_MANUAL_FALLBACK_NOT_SECURE_OK_BUTTON" desc="The title of the OK button of the 'Not Secure' alert in manual fallback." meaning="The user will dismiss the alert [20em]">
         OK
diff --git a/ios/chrome/browser/about_flags.mm b/ios/chrome/browser/about_flags.mm
index 62ae43f..f86c009 100644
--- a/ios/chrome/browser/about_flags.mm
+++ b/ios/chrome/browser/about_flags.mm
@@ -573,6 +573,10 @@
      flag_descriptions::kOfflineVersionWithoutNativeContentDescription,
      flags_ui::kOsIos,
      FEATURE_VALUE_TYPE(reading_list::kOfflineVersionWithoutNativeContent)},
+    {"store-pending-item-in-context",
+     flag_descriptions::kStorePendingItemInContextName,
+     flag_descriptions::kStorePendingItemInContextDescription, flags_ui::kOsIos,
+     FEATURE_VALUE_TYPE(web::features::kStorePendingItemInContext)},
 };
 
 // Add all switches from experimental flags to |command_line|.
diff --git a/ios/chrome/browser/ios_chrome_flag_descriptions.cc b/ios/chrome/browser/ios_chrome_flag_descriptions.cc
index 6841335..434cc98 100644
--- a/ios/chrome/browser/ios_chrome_flag_descriptions.cc
+++ b/ios/chrome/browser/ios_chrome_flag_descriptions.cc
@@ -432,6 +432,14 @@
     "When enabled, API will be injected into webpages to allow sending messages"
     " directly to any frame of a webpage.";
 
+const char kStorePendingItemInContextName[] =
+    "Store pending item in NavigationContext";
+const char kStorePendingItemInContextDescription[] =
+    "When enabled pending item will be stored in NavigationContext after "
+    "context is created. The item is still stored in NavigationManager if the "
+    "navigated was requested, but context does not yet exist or when "
+    "navigation was aborted.";
+
 const char kWebPageTextAccessibilityName[] =
     "Enable text accessibility in web pages";
 const char kWebPageTextAccessibilityDescription[] =
diff --git a/ios/chrome/browser/ios_chrome_flag_descriptions.h b/ios/chrome/browser/ios_chrome_flag_descriptions.h
index 1d777cd..64319c5 100644
--- a/ios/chrome/browser/ios_chrome_flag_descriptions.h
+++ b/ios/chrome/browser/ios_chrome_flag_descriptions.h
@@ -362,6 +362,11 @@
 extern const char kWebFrameMessagingName[];
 extern const char kWebFrameMessagingDescription[];
 
+// Title and description for the flag to store pending item in NavigationContext
+// after context is created.
+extern const char kStorePendingItemInContextName[];
+extern const char kStorePendingItemInContextDescription[];
+
 // Title and description for the flag to enable text accessibility in webpages.
 extern const char kWebPageTextAccessibilityName[];
 extern const char kWebPageTextAccessibilityDescription[];
diff --git a/ios/chrome/browser/passwords/password_controller.mm b/ios/chrome/browser/passwords/password_controller.mm
index 662c178..5cfbbe7d2 100644
--- a/ios/chrome/browser/passwords/password_controller.mm
+++ b/ios/chrome/browser/passwords/password_controller.mm
@@ -141,6 +141,9 @@
 // Tracks if current password is generated.
 @property(nonatomic, assign) BOOL isPasswordGenerated;
 
+// Tracks field when current password was generated.
+@property(nonatomic, copy) NSString* passwordGeneratedIdentifier;
+
 @end
 
 @interface PasswordController ()<FormSuggestionProvider, PasswordFormFiller>
@@ -360,20 +363,31 @@
                        }];
 
   if (self.isPasswordGenerated &&
-      [self canGeneratePasswordForForm:formName
-                       fieldIdentifier:fieldIdentifier
-                             fieldType:fieldType]) {
+      [fieldIdentifier isEqualToString:self.passwordGeneratedIdentifier]) {
+    // TODO(crbug.com/886583):  On other platforms, when the user clicks on
+    // generation field, we show password in clear text. And the user has
+    // the possibility to edit it. On iOS, it's harder to do (it's probably
+    // bad idea to change field type from password to text).
+    // We probably need to show some additionaly UI.  Waiting on UX.
     if (typedValue.length < kMinimumLengthForEditedPassword) {
       self.isPasswordGenerated = NO;
-      // TODO(crbug.com/886583): call
-      // passwordManager->OnPasswordNoLongerGenerated, but how to get
-      // PasswordForm?
+      self.passwordGeneratedIdentifier = nil;
+      self.passwordManager->OnPasswordNoLongerGenerated(
+          self.passwordManagerDriver);
     } else {
+      // Inject updated value to possibly update confirmation field.
       [self injectGeneratedPasswordForFormName:formName
                              generatedPassword:typedValue
                              completionHandler:nil];
     }
   }
+
+  if (self.isPasswordGenerated) {
+    // Always update, in case, for example, that username has been edited.
+    self.passwordManager->UpdateGeneratedPasswordOnUserInput(
+        self.passwordManagerDriver, SysNSStringToUTF16(formName),
+        SysNSStringToUTF16(fieldIdentifier), SysNSStringToUTF16(typedValue));
+  }
 }
 
 - (void)retrieveSuggestionsForForm:(NSString*)formName
@@ -794,10 +808,24 @@
                          generatedPassword:(NSString*)generatedPassword
                          completionHandler:(void (^)(BOOL))completionHandler {
   auto generatedPasswordInjected = ^(BOOL success) {
+    auto passwordPresaved = ^(BOOL found, const autofill::FormData& form) {
+      if (found) {
+        // TODO(crbug.com/886583): set is_manually_triggered when we show a
+        // prompt.
+        self.passwordManager->PresaveGeneratedPassword(
+            self.passwordManagerDriver, form,
+            SysNSStringToUTF16(generatedPassword),
+            SysNSStringToUTF16(newPasswordIdentifier),
+            /* is_manually_triggered */ false);
+      }
+      // If the form isn't found, it disappeared between fillPasswordForm below
+      // and here. There isn't much that can be done.
+    };
     if (success) {
-      // TODO(crbug.com/886583) call _pM::OnPresaveGeneratedPassword once it has
-      // been refactored not to need a full form.
+      [self.formHelper extractPasswordFormData:formName
+                             completionHandler:passwordPresaved];
       self.isPasswordGenerated = YES;
+      self.passwordGeneratedIdentifier = newPasswordIdentifier;
     }
     if (completionHandler)
       completionHandler(YES);
diff --git a/ios/chrome/browser/payments/ios_payment_instrument_launcher.h b/ios/chrome/browser/payments/ios_payment_instrument_launcher.h
index b2afbdf..62c9a84b 100644
--- a/ios/chrome/browser/payments/ios_payment_instrument_launcher.h
+++ b/ios/chrome/browser/payments/ios_payment_instrument_launcher.h
@@ -5,14 +5,11 @@
 #ifndef IOS_CHROME_BROWSER_PAYMENTS_IOS_PAYMENT_INSTRUMENT_LAUNCHER_H_
 #define IOS_CHROME_BROWSER_PAYMENTS_IOS_PAYMENT_INSTRUMENT_LAUNCHER_H_
 
+#include "base/values.h"
 #include "components/keyed_service/core/keyed_service.h"
 #include "components/payments/core/payment_currency_amount.h"
 #include "ios/chrome/browser/payments/payment_request.h"
 
-namespace base {
-class DictionaryValue;
-}  // namespace base
-
 namespace web {
 class NavigationItem;
 }  // namespace web
@@ -80,21 +77,20 @@
   // a mapping of the payment method names to the corresponding JSON-stringified
   // payment method specific data. This function converts that map into a JSON
   // readable object.
-  std::unique_ptr<base::DictionaryValue> SerializeMethodData(
+  base::Value SerializeMethodData(
       const std::map<std::string, std::set<std::string>>&
           stringified_method_data);
 
   // Returns the JSON-serialized top-level certificate chain of the browsing
   // context. |item| has information on the browsing state, including the
   // SSL certificate needed to build the certificate chain.
-  std::unique_ptr<base::ListValue> SerializeCertificateChain(
-      web::NavigationItem* item);
+  base::Value SerializeCertificateChain(web::NavigationItem* item);
 
   // Returns the JSON-serialized array of PaymentDetailsModifier objects.
   // |details| is the object that represents the details of a PaymentRequest
   // object and contains the vector of PaymentDetailsModifier objects to
   // serialize.
-  std::unique_ptr<base::ListValue> SerializeModifiers(PaymentDetails details);
+  base::Value SerializeModifiers(PaymentDetails details);
 
   // Invokes the payment instrument delegate with the appropriate function.
   // If |method_name| or |details| are empty then |delegate_| calls
diff --git a/ios/chrome/browser/payments/ios_payment_instrument_launcher.mm b/ios/chrome/browser/payments/ios_payment_instrument_launcher.mm
index 7f5e81ae..499f27b7 100644
--- a/ios/chrome/browser/payments/ios_payment_instrument_launcher.mm
+++ b/ios/chrome/browser/payments/ios_payment_instrument_launcher.mm
@@ -69,50 +69,49 @@
 
   delegate_ = delegate;
 
-  std::unique_ptr<base::DictionaryValue> params_to_payment_app =
-      std::make_unique<base::DictionaryValue>();
-
   // TODO(crbug.com/748556): Filter the following list to only show method names
   // that we know the payment app supports. For now, sending all the requested
   // method names i.e., 'basic-card' and 'https://alice-pay.com" to the payment
   // app works as the payment app provider can then decide what to do with that
   // information, but this is not ideal nor is this consistent with Android
   // implementation.
-  std::unique_ptr<base::ListValue> method_names =
-      std::make_unique<base::ListValue>();
-  for (auto const& it : payment_request->stringified_method_data())
-    method_names->GetList().emplace_back(it.first);
-  params_to_payment_app->SetList(kMethodNames, std::move(method_names));
+  base::Value method_names(base::Value::Type::LIST);
+  for (auto const& pair : payment_request->stringified_method_data())
+    method_names.GetList().emplace_back(pair.first);
 
-  params_to_payment_app->SetDictionary(
+  base::Value params_to_payment_app(base::Value::Type::DICTIONARY);
+  params_to_payment_app.SetKey(kMethodNames, std::move(method_names));
+
+  params_to_payment_app.SetKey(
       kMethodData,
       SerializeMethodData(payment_request->stringified_method_data()));
 
-  params_to_payment_app->SetString(
-      kMerchantName, base::UTF16ToASCII(active_web_state->GetTitle()));
+  params_to_payment_app.SetKey(kMerchantName,
+                               base::Value(active_web_state->GetTitle()));
 
-  params_to_payment_app->SetKey(
+  params_to_payment_app.SetKey(
       kTopLevelOrigin,
       base::Value(active_web_state->GetLastCommittedURL().host()));
 
-  params_to_payment_app->SetList(
+  params_to_payment_app.SetKey(
       kTopLevelCertificateChain,
       SerializeCertificateChain(
           active_web_state->GetNavigationManager()->GetVisibleItem()));
 
   DCHECK(payment_request->web_payment_request().details.total);
-  params_to_payment_app->SetDictionary(
+  params_to_payment_app.SetKey(
       kTotal,
-      PaymentCurrencyAmountToDictionaryValue(
-          *(payment_request->web_payment_request().details.total->amount)));
+      base::Value::FromUniquePtrValue(PaymentCurrencyAmountToDictionaryValue(
+          *(payment_request->web_payment_request().details.total->amount))));
 
-  params_to_payment_app->SetList(
+  params_to_payment_app.SetKey(
       kModifiers,
       SerializeModifiers(payment_request->web_payment_request().details));
 
   // JSON stringify the object so that it can be encoded in base-64.
   std::string stringified_parameters;
-  base::JSONWriter::Write(*params_to_payment_app, &stringified_parameters);
+  base::JSONWriter::Write(params_to_payment_app, &stringified_parameters);
+
   std::string base_64_params;
   base::Base64Encode(stringified_parameters, &base_64_params);
 
@@ -131,7 +130,7 @@
       }
       completionHandler:^(BOOL success) {
         if (!success) {
-          CompleteLaunchRequest("", "");
+          CompleteLaunchRequest(std::string(), std::string());
         }
       }];
 
@@ -145,70 +144,59 @@
   std::string stringified_parameters;
   base::Base64Decode(base_64_response, &stringified_parameters);
 
-  std::unique_ptr<base::Value> value =
-      base::JSONReader::ReadDeprecated(stringified_parameters);
+  base::Optional<base::Value> value =
+      base::JSONReader::Read(stringified_parameters);
   if (!value) {
-    CompleteLaunchRequest("", "");
+    CompleteLaunchRequest(std::string(), std::string());
     return;
   }
 
-  std::unique_ptr<base::DictionaryValue> dict =
-      base::DictionaryValue::From(std::move(value));
-  if (!dict) {
-    CompleteLaunchRequest("", "");
+  if (!value->is_dict()) {
+    CompleteLaunchRequest(std::string(), std::string());
     return;
   }
 
-  int success;
-  if (!dict->GetInteger(kSuccess, &success) || success == 0) {
-    CompleteLaunchRequest("", "");
+  base::Optional<int> success = value->FindIntKey(kSuccess);
+  if (!success || *success == 0) {
+    CompleteLaunchRequest(std::string(), std::string());
     return;
   }
 
-  std::string method_name;
-  if (!dict->GetString(kMethodName, &method_name) || method_name.empty()) {
-    CompleteLaunchRequest("", "");
+  const std::string* method_name = value->FindStringKey(kMethodName);
+  if (!method_name || method_name->empty()) {
+    CompleteLaunchRequest(std::string(), std::string());
     return;
   }
 
-  std::string stringified_details;
-  if (!dict->GetString(kDetails, &stringified_details) ||
-      stringified_details.empty()) {
-    CompleteLaunchRequest("", "");
+  const std::string* stringified_details = value->FindStringKey(kDetails);
+  if (!stringified_details || stringified_details->empty()) {
+    CompleteLaunchRequest(std::string(), std::string());
     return;
   }
 
-  CompleteLaunchRequest(method_name, stringified_details);
+  CompleteLaunchRequest(*method_name, *stringified_details);
 }
 
-std::unique_ptr<base::DictionaryValue>
-IOSPaymentInstrumentLauncher::SerializeMethodData(
+base::Value IOSPaymentInstrumentLauncher::SerializeMethodData(
     const std::map<std::string, std::set<std::string>>&
         stringified_method_data) {
-  std::unique_ptr<base::DictionaryValue> method_data =
-      std::make_unique<base::DictionaryValue>();
-
+  base::Value method_data(base::Value::Type::DICTIONARY);
   for (auto const& map_it : stringified_method_data) {
-    base::ListValue data_list;
+    base::Value data_list(base::Value::Type::LIST);
     for (auto const& data_it : map_it.second) {
       // We insert the stringified data, not the JSON object and only if the
       // corresponding JSON object is valid.
-      if (base::JSONReader::ReadDeprecated(data_it))
+      if (base::JSONReader::Read(data_it))
         data_list.GetList().emplace_back(data_it);
     }
-
-    method_data->SetKey(map_it.first, std::move(data_list));
+    method_data.SetKey(map_it.first, std::move(data_list));
   }
-
   return method_data;
 }
 
-std::unique_ptr<base::ListValue>
-IOSPaymentInstrumentLauncher::SerializeCertificateChain(
+base::Value IOSPaymentInstrumentLauncher::SerializeCertificateChain(
     web::NavigationItem* item) {
-  std::unique_ptr<base::ListValue> cert_chain_list =
-      std::make_unique<base::ListValue>();
-
+  base::Value cert_chain_list(base::Value::Type::LIST);
   if (!item)
     return cert_chain_list;
 
@@ -223,32 +211,28 @@
         net::x509_util::CryptoBufferAsStringPiece(handle.get()));
   }
 
-  std::unique_ptr<base::ListValue> byte_array;
   for (const auto& cert_string : cert_chain) {
-    base::ListValue byte_array;
+    base::Value byte_array(base::Value::Type::LIST);
     byte_array.GetList().reserve(cert_string.size());
     for (const char byte : cert_string)
       byte_array.GetList().emplace_back(byte);
 
-    base::DictionaryValue cert_chain_dict;
+    base::Value cert_chain_dict(base::Value::Type::DICTIONARY);
     cert_chain_dict.SetKey(kCertificate, std::move(byte_array));
-    cert_chain_list->GetList().push_back(std::move(cert_chain_dict));
+    cert_chain_list.GetList().push_back(std::move(cert_chain_dict));
   }
 
   return cert_chain_list;
 }
 
-std::unique_ptr<base::ListValue>
-IOSPaymentInstrumentLauncher::SerializeModifiers(PaymentDetails details) {
-  std::unique_ptr<base::ListValue> modifiers =
-      std::make_unique<base::ListValue>();
+base::Value IOSPaymentInstrumentLauncher::SerializeModifiers(
+    PaymentDetails details) {
+  base::Value modifiers(base::Value::Type::LIST);
   size_t numModifiers = details.modifiers.size();
   for (size_t i = 0; i < numModifiers; ++i) {
-    std::unique_ptr<base::DictionaryValue> modifier =
-        details.modifiers[i].ToDictionaryValue();
-    modifiers->GetList().push_back(std::move(*modifier));
+    modifiers.GetList().push_back(base::Value::FromUniquePtrValue(
+        details.modifiers[i].ToDictionaryValue()));
   }
-
   return modifiers;
 }
 
diff --git a/ios/chrome/browser/payments/ios_payment_instrument_launcher_unittest.mm b/ios/chrome/browser/payments/ios_payment_instrument_launcher_unittest.mm
index a8107ffc..82b22a18 100644
--- a/ios/chrome/browser/payments/ios_payment_instrument_launcher_unittest.mm
+++ b/ios/chrome/browser/payments/ios_payment_instrument_launcher_unittest.mm
@@ -80,7 +80,7 @@
     test_personal_data_manager_.SetAutofillWalletImportEnabled(true);
   }
 
-  std::unique_ptr<base::DictionaryValue> SerializeMethodDataWrapper(
+  base::Value SerializeMethodDataWrapper(
       const std::map<std::string, std::set<std::string>>&
           stringified_method_data) {
     IOSPaymentInstrumentLauncher launcher;
@@ -107,11 +107,9 @@
                                      chrome_browser_state_.get(), &web_state_,
                                      &test_personal_data_manager_);
 
-  base::DictionaryValue expected_dict;
-
-  EXPECT_TRUE(expected_dict.Equals(
-      SerializeMethodDataWrapper(payment_request.stringified_method_data())
-          .get()));
+  base::Value expected_dict(base::Value::Type::DICTIONARY);
+  EXPECT_EQ(expected_dict, SerializeMethodDataWrapper(
+                               payment_request.stringified_method_data()));
 }
 
 // Tests that serializing populated stringified method data yields the expected
@@ -149,27 +147,33 @@
                                      chrome_browser_state_.get(), &web_state_,
                                      &test_personal_data_manager_);
 
-  base::DictionaryValue expected_dict;
-  base::ListValue jef_data_list;
-  base::DictionaryValue jef_data;
-  jef_data.SetString("Some data", "Some stringified data");
+  // Jef data...
+  base::Value jef_data(base::Value::Type::DICTIONARY);
+  jef_data.SetKey("Some data", base::Value("Some stringified data"));
   std::string jef_stringified_data;
   base::JSONWriter::Write(jef_data, &jef_stringified_data);
+  base::Value jef_data_list(base::Value::Type::LIST);
   jef_data_list.GetList().emplace_back(jef_stringified_data);
-  expected_dict.SetKey("https://jefpay.com", std::move(jef_data_list));
-  base::ListValue bob_data_list;
-  base::DictionaryValue bob_data;
-  bob_data.SetString("Some data", "Some stringified data");
+
+  // Bob data...
+  base::Value bob_data(base::Value::Type::DICTIONARY);
+  bob_data.SetKey("Some data", base::Value("Some stringified data"));
   std::string bob_stringified_data;
   base::JSONWriter::Write(bob_data, &bob_stringified_data);
+  base::Value bob_data_list(base::Value::Type::LIST);
   bob_data_list.GetList().emplace_back(bob_stringified_data);
+
+  // Alice data...
+  base::Value alice_data_list(base::Value::Type::LIST);
+
+  // Expected output.
+  base::Value expected_dict(base::Value::Type::DICTIONARY);
+  expected_dict.SetKey("https://jefpay.com", std::move(jef_data_list));
   expected_dict.SetKey("https://bobpay.com", std::move(bob_data_list));
-  base::ListValue alice_data_list;
   expected_dict.SetKey("https://alicepay.com", std::move(alice_data_list));
 
-  EXPECT_TRUE(expected_dict.Equals(
-      SerializeMethodDataWrapper(payment_request.stringified_method_data())
-          .get()));
+  EXPECT_EQ(expected_dict, SerializeMethodDataWrapper(
+                               payment_request.stringified_method_data()));
 }
 
 // Tests that attempting to open an invalid universal link calls the
diff --git a/ios/chrome/browser/signin/gaia_auth_fetcher_ios_ns_url_session_bridge.mm b/ios/chrome/browser/signin/gaia_auth_fetcher_ios_ns_url_session_bridge.mm
index c816de0..ce923608 100644
--- a/ios/chrome/browser/signin/gaia_auth_fetcher_ios_ns_url_session_bridge.mm
+++ b/ios/chrome/browser/signin/gaia_auth_fetcher_ios_ns_url_session_bridge.mm
@@ -94,8 +94,8 @@
       GetBrowserState()->GetCookieManager();
   net::CookieOptions options;
   options.set_include_httponly();
-  options.set_same_site_cookie_mode(
-      net::CookieOptions::SameSiteCookieMode::INCLUDE_STRICT_AND_LAX);
+  options.set_same_site_cookie_context(
+      net::CookieOptions::SameSiteCookieContext::SAME_SITE_STRICT);
   cookie_manager->GetCookieList(
       GetRequest().url, options,
       base::BindOnce(
diff --git a/ios/chrome/browser/ui/autofill/manual_fill/BUILD.gn b/ios/chrome/browser/ui/autofill/manual_fill/BUILD.gn
index 0ca6cdd..3cbd60d 100644
--- a/ios/chrome/browser/ui/autofill/manual_fill/BUILD.gn
+++ b/ios/chrome/browser/ui/autofill/manual_fill/BUILD.gn
@@ -12,6 +12,8 @@
     "address_form.mm",
     "address_mediator.h",
     "address_mediator.mm",
+    "all_password_coordinator.h",
+    "all_password_coordinator.mm",
     "card_coordinator.h",
     "card_coordinator.mm",
     "card_mediator.h",
@@ -96,7 +98,7 @@
     "manual_fill_password_cell.h",
     "manual_fill_password_cell.mm",
     "password_consumer.h",
-    "password_list_delegate.h",
+    "password_list_navigator.h",
     "password_view_controller.h",
     "password_view_controller.mm",
     "uicolor_manualfill.h",
diff --git a/ios/chrome/browser/ui/autofill/manual_fill/all_password_coordinator.h b/ios/chrome/browser/ui/autofill/manual_fill/all_password_coordinator.h
new file mode 100644
index 0000000..d41d868
--- /dev/null
+++ b/ios/chrome/browser/ui/autofill/manual_fill/all_password_coordinator.h
@@ -0,0 +1,42 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef IOS_CHROME_BROWSER_UI_AUTOFILL_MANUAL_FILL_ALL_PASSWORD_COORDINATOR_H_
+#define IOS_CHROME_BROWSER_UI_AUTOFILL_MANUAL_FILL_ALL_PASSWORD_COORDINATOR_H_
+
+#import "ios/chrome/browser/ui/autofill/manual_fill/fallback_coordinator.h"
+
+@protocol PasswordListNavigator;
+
+namespace manual_fill {
+
+extern NSString* const kPasswordDoneButtonAccessibilityIdentifier;
+
+}  // namespace manual_fill
+
+// Creates and manages a view controller to present all the passwords to the
+// user. The view controller contains a search bar. The presentation is done
+// with a table view presentation controller. Any selected password will be sent
+// to the current field in the active web state.
+@interface ManualFillAllPasswordCoordinator : FallbackCoordinator
+
+// Creates a coordinator with the passed args.
+- (instancetype)
+    initWithBaseViewController:(UIViewController*)viewController
+                  browserState:(ios::ChromeBrowserState*)browserState
+              injectionHandler:(ManualFillInjectionHandler*)injectionHandler
+                     navigator:(id<PasswordListNavigator>)navigator
+    NS_DESIGNATED_INITIALIZER;
+
+// Unavailable, use
+// -initWithBaseViewController:browserState:webStateList:injectionHandler:.
+- (instancetype)
+    initWithBaseViewController:(UIViewController*)viewController
+                  browserState:(ios::ChromeBrowserState*)browserState
+              injectionHandler:(ManualFillInjectionHandler*)injectionHandler
+    NS_UNAVAILABLE;
+
+@end
+
+#endif  // IOS_CHROME_BROWSER_UI_AUTOFILL_MANUAL_FILL_ALL_PASSWORD_COORDINATOR_H_
diff --git a/ios/chrome/browser/ui/autofill/manual_fill/all_password_coordinator.mm b/ios/chrome/browser/ui/autofill/manual_fill/all_password_coordinator.mm
new file mode 100644
index 0000000..6f2308a
--- /dev/null
+++ b/ios/chrome/browser/ui/autofill/manual_fill/all_password_coordinator.mm
@@ -0,0 +1,158 @@
+// 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/ui/autofill/manual_fill/all_password_coordinator.h"
+
+#include "components/keyed_service/core/service_access_type.h"
+#include "components/password_manager/core/browser/password_store.h"
+#include "ios/chrome/browser/passwords/ios_chrome_password_store_factory.h"
+#import "ios/chrome/browser/ui/autofill/manual_fill/manual_fill_injection_handler.h"
+#import "ios/chrome/browser/ui/autofill/manual_fill/password_list_navigator.h"
+#import "ios/chrome/browser/ui/autofill/manual_fill/password_mediator.h"
+#import "ios/chrome/browser/ui/autofill/manual_fill/password_view_controller.h"
+#import "ios/chrome/browser/ui/table_view/table_view_animator.h"
+#import "ios/chrome/browser/ui/table_view/table_view_navigation_controller.h"
+#import "ios/chrome/browser/ui/table_view/table_view_presentation_controller.h"
+#include "ios/chrome/browser/ui/util/ui_util.h"
+
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
+namespace manual_fill {
+
+NSString* const kPasswordDoneButtonAccessibilityIdentifier =
+    @"kManualFillPasswordDoneButtonAccessibilityIdentifier";
+
+}  // namespace manual_fill
+
+@interface ManualFillAllPasswordCoordinator () <
+    UIViewControllerTransitioningDelegate>
+
+// Fetches and filters the passwords for the view controller.
+@property(nonatomic, strong) ManualFillPasswordMediator* passwordMediator;
+
+// The view controller presented above the keyboard where the user can select
+// one of their passwords.
+@property(nonatomic, strong) PasswordViewController* passwordViewController;
+
+@end
+
+@implementation ManualFillAllPasswordCoordinator
+
+// Property tagged dynamic because it overrides super class delegate with and
+// extension of the super delegate type (i.e. PasswordCoordinatorDelegate
+// extends FallbackCoordinatorDelegate).
+@dynamic delegate;
+
+- (instancetype)
+    initWithBaseViewController:(UIViewController*)viewController
+                  browserState:(ios::ChromeBrowserState*)browserState
+              injectionHandler:(ManualFillInjectionHandler*)injectionHandler
+                     navigator:(id<PasswordListNavigator>)navigator {
+  self = [super initWithBaseViewController:viewController
+                              browserState:browserState
+                          injectionHandler:injectionHandler];
+  if (self) {
+    UISearchController* searchController =
+        [[UISearchController alloc] initWithSearchResultsController:nil];
+
+    _passwordViewController = [[PasswordViewController alloc]
+        initWithSearchController:searchController];
+    _passwordViewController.contentInsetsAlwaysEqualToSafeArea = YES;
+
+    auto passwordStore = IOSChromePasswordStoreFactory::GetForBrowserState(
+        browserState, ServiceAccessType::EXPLICIT_ACCESS);
+    _passwordMediator = [[ManualFillPasswordMediator alloc]
+        initWithPasswordStore:passwordStore];
+
+    [_passwordMediator fetchPasswordsForURL:GURL::EmptyGURL()];
+    _passwordMediator.actionSectionEnabled = NO;
+    _passwordMediator.consumer = _passwordViewController;
+    _passwordMediator.contentDelegate = injectionHandler;
+    _passwordMediator.navigator = navigator;
+
+    searchController.searchResultsUpdater = _passwordMediator;
+
+    UIBarButtonItem* doneButton = [[UIBarButtonItem alloc]
+        initWithBarButtonSystemItem:UIBarButtonSystemItemDone
+                             target:navigator
+                             action:@selector(dismissPresentedViewController)];
+    doneButton.accessibilityIdentifier =
+        manual_fill::kPasswordDoneButtonAccessibilityIdentifier;
+    _passwordViewController.navigationItem.rightBarButtonItem = doneButton;
+  }
+  return self;
+}
+
+- (void)start {
+  [super start];
+  TableViewNavigationController* navigationController =
+      [[TableViewNavigationController alloc]
+          initWithTable:self.passwordViewController];
+  navigationController.transitioningDelegate = self;
+  [navigationController setModalPresentationStyle:UIModalPresentationCustom];
+
+  [self.baseViewController presentViewController:navigationController
+                                        animated:YES
+                                      completion:nil];
+}
+
+- (void)stop {
+  [self.passwordViewController dismissViewControllerAnimated:NO completion:nil];
+  [super stop];
+}
+
+#pragma mark - UIViewControllerTransitioningDelegate
+
+- (UIPresentationController*)
+    presentationControllerForPresentedViewController:
+        (UIViewController*)presented
+                            presentingViewController:
+                                (UIViewController*)presenting
+                                sourceViewController:(UIViewController*)source {
+  TableViewPresentationController* presentationController =
+      [[TableViewPresentationController alloc]
+          initWithPresentedViewController:presented
+                 presentingViewController:presenting];
+  return presentationController;
+}
+
+- (id<UIViewControllerAnimatedTransitioning>)
+    animationControllerForPresentedController:(UIViewController*)presented
+                         presentingController:(UIViewController*)presenting
+                             sourceController:(UIViewController*)source {
+  UITraitCollection* traitCollection = presenting.traitCollection;
+  if (traitCollection.horizontalSizeClass == UIUserInterfaceSizeClassCompact &&
+      traitCollection.verticalSizeClass != UIUserInterfaceSizeClassCompact) {
+    // Use the default animator for fullscreen presentations.
+    return nil;
+  }
+
+  TableViewAnimator* animator = [[TableViewAnimator alloc] init];
+  animator.presenting = YES;
+  return animator;
+}
+
+- (id<UIViewControllerAnimatedTransitioning>)
+    animationControllerForDismissedController:(UIViewController*)dismissed {
+  UITraitCollection* traitCollection = dismissed.traitCollection;
+  if (traitCollection.horizontalSizeClass == UIUserInterfaceSizeClassCompact &&
+      traitCollection.verticalSizeClass != UIUserInterfaceSizeClassCompact) {
+    // Use the default animator for fullscreen presentations.
+    return nil;
+  }
+
+  TableViewAnimator* animator = [[TableViewAnimator alloc] init];
+  animator.presenting = NO;
+  return animator;
+}
+
+#pragma mark - FallbackCoordinator
+
+- (UIViewController*)viewController {
+  return self.passwordViewController;
+}
+
+@end
diff --git a/ios/chrome/browser/ui/autofill/manual_fill/manual_fill_address_cell.mm b/ios/chrome/browser/ui/autofill/manual_fill/manual_fill_address_cell.mm
index 8a14b65..3d53ed8e 100644
--- a/ios/chrome/browser/ui/autofill/manual_fill/manual_fill_address_cell.mm
+++ b/ios/chrome/browser/ui/autofill/manual_fill/manual_fill_address_cell.mm
@@ -143,30 +143,49 @@
   NSMutableArray<UIView*>* verticalLeadViews = [[NSMutableArray alloc] init];
   UIView* guide = self.contentView;
 
-  // Top label, summary of line 1 and 2.
-  NSMutableAttributedString* attributedString =
-      [[NSMutableAttributedString alloc]
-          initWithString:address.line1 ? address.line1 : @""
-              attributes:@{
-                NSForegroundColorAttributeName : UIColor.blackColor,
-                NSFontAttributeName :
-                    [UIFont preferredFontForTextStyle:UIFontTextStyleHeadline]
-              }];
-  if (address.line2.length) {
-    NSString* line2String =
-        [NSString stringWithFormat:@" –– %@", address.line2];
-    NSDictionary* attributes = @{
-      NSForegroundColorAttributeName : UIColor.lightGrayColor,
-      NSFontAttributeName :
-          [UIFont preferredFontForTextStyle:UIFontTextStyleBody]
-    };
-    NSAttributedString* line2StringAttributedString =
-        [[NSAttributedString alloc] initWithString:line2String
-                                        attributes:attributes];
-    [attributedString appendAttributedString:line2StringAttributedString];
+  NSString* blackText = nil;
+  NSString* grayText = nil;
+
+  // Top label is the address summary and fallbacks on city and email.
+  if (address.line1.length) {
+    blackText = address.line1;
+    grayText = address.line2.length ? address.line2 : nil;
+  } else if (address.line2.length) {
+    blackText = address.line2;
+  } else if (address.city.length) {
+    blackText = address.city;
+  } else if (address.emailAddress.length) {
+    blackText = address.emailAddress;
   }
-  self.addressLabel.attributedText = attributedString;
-  [verticalLeadViews addObject:self.addressLabel];
+
+  NSMutableAttributedString* attributedString = nil;
+  if (blackText.length) {
+    attributedString = [[NSMutableAttributedString alloc]
+        initWithString:blackText
+            attributes:@{
+              NSForegroundColorAttributeName : UIColor.blackColor,
+              NSFontAttributeName :
+                  [UIFont preferredFontForTextStyle:UIFontTextStyleHeadline]
+            }];
+    if (grayText.length) {
+      NSString* formattedGrayText =
+          [NSString stringWithFormat:@" –– %@", grayText];
+      NSDictionary* attributes = @{
+        NSForegroundColorAttributeName : UIColor.lightGrayColor,
+        NSFontAttributeName :
+            [UIFont preferredFontForTextStyle:UIFontTextStyleBody]
+      };
+      NSAttributedString* grayAttributedString =
+          [[NSAttributedString alloc] initWithString:formattedGrayText
+                                          attributes:attributes];
+      [attributedString appendAttributedString:grayAttributedString];
+    }
+  }
+
+  if (attributedString) {
+    self.addressLabel.attributedText = attributedString;
+    [verticalLeadViews addObject:self.addressLabel];
+  }
 
   self.dynamicConstraints = [[NSMutableArray alloc] init];
 
diff --git a/ios/chrome/browser/ui/autofill/manual_fill/password_coordinator.h b/ios/chrome/browser/ui/autofill/manual_fill/password_coordinator.h
index 03cc1d8f..64ccc57f 100644
--- a/ios/chrome/browser/ui/autofill/manual_fill/password_coordinator.h
+++ b/ios/chrome/browser/ui/autofill/manual_fill/password_coordinator.h
@@ -9,12 +9,6 @@
 
 class GURL;
 
-namespace manual_fill {
-
-extern NSString* const PasswordDoneButtonAccessibilityIdentifier;
-
-}  // namespace manual_fill
-
 // Delegate for the coordinator actions.
 @protocol PasswordCoordinatorDelegate<FallbackCoordinatorDelegate>
 
@@ -23,9 +17,9 @@
 
 @end
 
-// Creates and manages a view controller to present passwords to the user.
-// Any selected password will be sent to the current field in the active web
-// state.
+// Creates and manages a view controller to present passwords to the user. It
+// will filter the passwords based on the passed URL when instantiating it. Any
+// selected password will be sent to the current field in the active web state.
 @interface ManualFillPasswordCoordinator : FallbackCoordinator
 
 // The delegate for this coordinator. Delegate class extends
@@ -33,7 +27,7 @@
 @property(nonatomic, weak) id<PasswordCoordinatorDelegate> delegate;
 
 // Creates a coordinator that uses a |viewController|, |browserState|,
-// |webStateList| and an |injectionHandler|.
+// |URL| and an |injectionHandler|.
 - (instancetype)
     initWithBaseViewController:(UIViewController*)viewController
                   browserState:(ios::ChromeBrowserState*)browserState
diff --git a/ios/chrome/browser/ui/autofill/manual_fill/password_coordinator.mm b/ios/chrome/browser/ui/autofill/manual_fill/password_coordinator.mm
index 5856eb6e..5a8006de 100644
--- a/ios/chrome/browser/ui/autofill/manual_fill/password_coordinator.mm
+++ b/ios/chrome/browser/ui/autofill/manual_fill/password_coordinator.mm
@@ -4,32 +4,22 @@
 
 #import "ios/chrome/browser/ui/autofill/manual_fill/password_coordinator.h"
 
+#include "base/mac/foundation_util.h"
 #include "components/keyed_service/core/service_access_type.h"
 #include "components/password_manager/core/browser/password_store.h"
 #include "ios/chrome/browser/passwords/ios_chrome_password_store_factory.h"
+#import "ios/chrome/browser/ui/autofill/manual_fill/all_password_coordinator.h"
 #import "ios/chrome/browser/ui/autofill/manual_fill/manual_fill_injection_handler.h"
-#import "ios/chrome/browser/ui/autofill/manual_fill/password_list_delegate.h"
+#import "ios/chrome/browser/ui/autofill/manual_fill/password_list_navigator.h"
 #import "ios/chrome/browser/ui/autofill/manual_fill/password_mediator.h"
 #import "ios/chrome/browser/ui/autofill/manual_fill/password_view_controller.h"
-#import "ios/chrome/browser/ui/table_view/table_view_animator.h"
-#import "ios/chrome/browser/ui/table_view/table_view_navigation_controller.h"
-#import "ios/chrome/browser/ui/table_view/table_view_presentation_controller.h"
 #include "ios/chrome/browser/ui/util/ui_util.h"
 
 #if !defined(__has_feature) || !__has_feature(objc_arc)
 #error "This file requires ARC support."
 #endif
 
-namespace manual_fill {
-
-NSString* const PasswordDoneButtonAccessibilityIdentifier =
-    @"kManualFillPasswordDoneButtonAccessibilityIdentifier";
-
-}  // namespace manual_fill
-
-@interface ManualFillPasswordCoordinator ()<
-    PasswordListDelegate,
-    UIViewControllerTransitioningDelegate>
+@interface ManualFillPasswordCoordinator () <PasswordListNavigator>
 
 // Fetches and filters the passwords for the view controller.
 @property(nonatomic, strong) ManualFillPasswordMediator* passwordMediator;
@@ -38,17 +28,10 @@
 // one of their passwords.
 @property(nonatomic, strong) PasswordViewController* passwordViewController;
 
-// The view controller modally presented where the user can select one of their
-// passwords. Owned by the view controllers hierarchy.
-@property(nonatomic, weak) PasswordViewController* allPasswordsViewController;
-
 // Button presenting this coordinator in a popover. Used for continuation after
 // dismissing any presented view controller. iPad only.
 @property(nonatomic, weak) UIButton* presentingButton;
 
-// The domain for this mediator.
-@property(nonatomic) GURL URL;
-
 @end
 
 @implementation ManualFillPasswordCoordinator
@@ -75,11 +58,10 @@
         browserState, ServiceAccessType::EXPLICIT_ACCESS);
     _passwordMediator = [[ManualFillPasswordMediator alloc]
         initWithPasswordStore:passwordStore];
-    _URL = URL;
-    [_passwordMediator fetchPasswordsForURL:_URL];
+    [_passwordMediator fetchPasswordsForURL:URL];
     _passwordMediator.actionSectionEnabled = YES;
     _passwordMediator.consumer = _passwordViewController;
-    _passwordMediator.navigationDelegate = self;
+    _passwordMediator.navigator = self;
     _passwordMediator.contentDelegate = injectionHandler;
   }
   return self;
@@ -87,8 +69,8 @@
 
 - (void)stop {
   [super stop];
-  [self.allPasswordsViewController dismissViewControllerAnimated:YES
-                                                      completion:nil];
+  [self.activeChildCoordinator stop];
+  [self.childCoordinators removeAllObjects];
 }
 
 - (void)presentFromButton:(UIButton*)button {
@@ -96,56 +78,13 @@
   self.presentingButton = button;
 }
 
-#pragma mark - UIViewControllerTransitioningDelegate
-
-- (UIPresentationController*)
-presentationControllerForPresentedViewController:(UIViewController*)presented
-                        presentingViewController:(UIViewController*)presenting
-                            sourceViewController:(UIViewController*)source {
-  TableViewPresentationController* presentationController =
-      [[TableViewPresentationController alloc]
-          initWithPresentedViewController:presented
-                 presentingViewController:presenting];
-  return presentationController;
-}
-
-- (id<UIViewControllerAnimatedTransitioning>)
-animationControllerForPresentedController:(UIViewController*)presented
-                     presentingController:(UIViewController*)presenting
-                         sourceController:(UIViewController*)source {
-  UITraitCollection* traitCollection = presenting.traitCollection;
-  if (traitCollection.horizontalSizeClass == UIUserInterfaceSizeClassCompact &&
-      traitCollection.verticalSizeClass != UIUserInterfaceSizeClassCompact) {
-    // Use the default animator for fullscreen presentations.
-    return nil;
-  }
-
-  TableViewAnimator* animator = [[TableViewAnimator alloc] init];
-  animator.presenting = YES;
-  return animator;
-}
-
-- (id<UIViewControllerAnimatedTransitioning>)
-animationControllerForDismissedController:(UIViewController*)dismissed {
-  UITraitCollection* traitCollection = dismissed.traitCollection;
-  if (traitCollection.horizontalSizeClass == UIUserInterfaceSizeClassCompact &&
-      traitCollection.verticalSizeClass != UIUserInterfaceSizeClassCompact) {
-    // Use the default animator for fullscreen presentations.
-    return nil;
-  }
-
-  TableViewAnimator* animator = [[TableViewAnimator alloc] init];
-  animator.presenting = NO;
-  return animator;
-}
-
 #pragma mark - FallbackCoordinator
 
 - (UIViewController*)viewController {
   return self.passwordViewController;
 }
 
-#pragma mark - PasswordListDelegate
+#pragma mark - PasswordListNavigator
 
 - (void)openAllPasswordsList {
   // On iPad, first dismiss the popover before the new view is presented.
@@ -158,49 +97,32 @@
                            }];
     return;
   }
-  UISearchController* searchController =
-      [[UISearchController alloc] initWithSearchResultsController:nil];
-  searchController.searchResultsUpdater = self.passwordMediator;
 
-  PasswordViewController* allPasswordsViewController = [
-      [PasswordViewController alloc] initWithSearchController:searchController];
-  self.passwordMediator.actionSectionEnabled = NO;
-  [self.passwordMediator fetchPasswordsForURL:GURL::EmptyGURL()];
-  self.passwordMediator.consumer = allPasswordsViewController;
-  UIBarButtonItem* doneButton = [[UIBarButtonItem alloc]
-      initWithBarButtonSystemItem:UIBarButtonSystemItemDone
-                           target:self
-                           action:@selector(dismissPresentedViewController)];
-  doneButton.accessibilityIdentifier =
-      manual_fill::PasswordDoneButtonAccessibilityIdentifier;
-  allPasswordsViewController.navigationItem.rightBarButtonItem = doneButton;
-  self.allPasswordsViewController = allPasswordsViewController;
-
-  TableViewNavigationController* navigationController =
-      [[TableViewNavigationController alloc]
-          initWithTable:allPasswordsViewController];
-  navigationController.transitioningDelegate = self;
-  [navigationController setModalPresentationStyle:UIModalPresentationCustom];
-
-  [self.baseViewController presentViewController:navigationController
-                                        animated:YES
-                                      completion:nil];
+  ManualFillAllPasswordCoordinator* allPasswordCoordinator =
+      [[ManualFillAllPasswordCoordinator alloc]
+          initWithBaseViewController:self.baseViewController
+                        browserState:self.browserState
+                    injectionHandler:self.manualFillInjectionHandler
+                           navigator:self];
+  [allPasswordCoordinator start];
+  [self.childCoordinators addObject:allPasswordCoordinator];
 }
 
 - (void)dismissPresentedViewController {
+  if (self.viewController.presentingViewController) {
+    return;
+  }
+
   // Dismiss the full screen view controller and present the pop over.
   __weak __typeof(self) weakSelf = self;
-  [self.allPasswordsViewController.presentingViewController
+  FallbackCoordinator* activeCoordinator =
+      base::mac::ObjCCast<FallbackCoordinator>(self.activeChildCoordinator);
+  [activeCoordinator.viewController.presentingViewController
       dismissViewControllerAnimated:YES
                          completion:^{
-                           weakSelf.passwordMediator.actionSectionEnabled = YES;
-                           [weakSelf.passwordMediator
-                               fetchPasswordsForURL:self.URL];
-                           weakSelf.passwordMediator.consumer =
-                               weakSelf.passwordViewController;
+                           [weakSelf.childCoordinators removeAllObjects];
                            if (weakSelf.presentingButton) {
-                             [weakSelf
-                                 presentFromButton:weakSelf.presentingButton];
+                             [weakSelf presentFromButton:self.presentingButton];
                            }
                          }];
 }
diff --git a/ios/chrome/browser/ui/autofill/manual_fill/password_list_delegate.h b/ios/chrome/browser/ui/autofill/manual_fill/password_list_navigator.h
similarity index 80%
rename from ios/chrome/browser/ui/autofill/manual_fill/password_list_delegate.h
rename to ios/chrome/browser/ui/autofill/manual_fill/password_list_navigator.h
index 0193adc..01b5d8a 100644
--- a/ios/chrome/browser/ui/autofill/manual_fill/password_list_delegate.h
+++ b/ios/chrome/browser/ui/autofill/manual_fill/password_list_navigator.h
@@ -2,11 +2,11 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef IOS_CHROME_BROWSER_UI_AUTOFILL_MANUAL_FILL_PASSWORD_LIST_DELEGATE_H_
-#define IOS_CHROME_BROWSER_UI_AUTOFILL_MANUAL_FILL_PASSWORD_LIST_DELEGATE_H_
+#ifndef IOS_CHROME_BROWSER_UI_AUTOFILL_MANUAL_FILL_PASSWORD_LIST_NAVIGATOR_H_
+#define IOS_CHROME_BROWSER_UI_AUTOFILL_MANUAL_FILL_PASSWORD_LIST_NAVIGATOR_H_
 
-// Delegate for actions in manual fallback's passwords list.
-@protocol PasswordListDelegate
+// Object to navigate different views in manual fallback's passwords list.
+@protocol PasswordListNavigator
 
 // Dismisses the presented view controller and continues as pop over on iPads
 // or above the keyboard elsewhere.
@@ -20,4 +20,4 @@
 
 @end
 
-#endif  // IOS_CHROME_BROWSER_UI_AUTOFILL_MANUAL_FILL_PASSWORD_LIST_DELEGATE_H_
+#endif  // IOS_CHROME_BROWSER_UI_AUTOFILL_MANUAL_FILL_PASSWORD_LIST_NAVIGATOR_H_
diff --git a/ios/chrome/browser/ui/autofill/manual_fill/password_mediator.h b/ios/chrome/browser/ui/autofill/manual_fill/password_mediator.h
index 26d134b..24f99b9 100644
--- a/ios/chrome/browser/ui/autofill/manual_fill/password_mediator.h
+++ b/ios/chrome/browser/ui/autofill/manual_fill/password_mediator.h
@@ -11,7 +11,7 @@
 
 @protocol ManualFillContentDelegate;
 @protocol ManualFillPasswordConsumer;
-@protocol PasswordListDelegate;
+@protocol PasswordListNavigator;
 
 namespace password_manager {
 class PasswordStore;
@@ -38,8 +38,8 @@
 @property(nonatomic, weak) id<ManualFillPasswordConsumer> consumer;
 // The delegate in charge of using the content selected by the user.
 @property(nonatomic, weak) id<ManualFillContentDelegate> contentDelegate;
-// The delegate in charge of navigation.
-@property(nonatomic, weak) id<PasswordListDelegate> navigationDelegate;
+// The object in charge of navigation.
+@property(nonatomic, weak) id<PasswordListNavigator> navigator;
 // If YES  actions will be post to the consumer. Set this value before
 // setting the consumer, since just setting this won't trigger the consumer
 // callbacks. Defaults to NO.
diff --git a/ios/chrome/browser/ui/autofill/manual_fill/password_mediator.mm b/ios/chrome/browser/ui/autofill/manual_fill/password_mediator.mm
index 700e3f7..c5bc399 100644
--- a/ios/chrome/browser/ui/autofill/manual_fill/password_mediator.mm
+++ b/ios/chrome/browser/ui/autofill/manual_fill/password_mediator.mm
@@ -17,7 +17,7 @@
 #import "ios/chrome/browser/ui/autofill/manual_fill/manual_fill_content_delegate.h"
 #import "ios/chrome/browser/ui/autofill/manual_fill/manual_fill_password_cell.h"
 #import "ios/chrome/browser/ui/autofill/manual_fill/password_consumer.h"
-#import "ios/chrome/browser/ui/autofill/manual_fill/password_list_delegate.h"
+#import "ios/chrome/browser/ui/autofill/manual_fill/password_list_navigator.h"
 #import "ios/chrome/browser/ui/list_model/list_model.h"
 #import "ios/chrome/browser/ui/table_view/table_view_model.h"
 #include "ios/chrome/grit/ios_strings.h"
@@ -211,7 +211,7 @@
                  action:^{
                    base::RecordAction(base::UserMetricsAction(
                        "ManualFallback_Password_OpenOtherPassword"));
-                   [weakSelf.navigationDelegate openAllPasswordsList];
+                   [weakSelf.navigator openAllPasswordsList];
                  }];
       otherPasswordsItem.accessibilityIdentifier =
           manual_fill::OtherPasswordsAccessibilityIdentifier;
@@ -225,15 +225,13 @@
                action:^{
                  base::RecordAction(base::UserMetricsAction(
                      "ManualFallback_Password_OpenManagePassword"));
-                 [weakSelf.navigationDelegate openPasswordSettings];
+                 [weakSelf.navigator openPasswordSettings];
                }];
     managePasswordsItem.accessibilityIdentifier =
         manual_fill::ManagePasswordsAccessibilityIdentifier;
     [actions addObject:managePasswordsItem];
 
     [self.consumer presentActions:actions];
-  } else {
-    [self.consumer presentActions:@[]];
   }
 }
 
@@ -258,7 +256,7 @@
 - (void)userDidPickContent:(NSString*)content
              passwordField:(BOOL)passwordField
              requiresHTTPS:(BOOL)requiresHTTPS {
-  [self.navigationDelegate dismissPresentedViewController];
+  [self.navigator dismissPresentedViewController];
   [self.contentDelegate userDidPickContent:content
                              passwordField:passwordField
                              requiresHTTPS:requiresHTTPS];
diff --git a/ios/chrome/browser/ui/autofill/manual_fill/password_view_controller_egtest.mm b/ios/chrome/browser/ui/autofill/manual_fill/password_view_controller_egtest.mm
index 2f84a9d0..6689665c 100644
--- a/ios/chrome/browser/ui/autofill/manual_fill/password_view_controller_egtest.mm
+++ b/ios/chrome/browser/ui/autofill/manual_fill/password_view_controller_egtest.mm
@@ -16,8 +16,8 @@
 #include "components/password_manager/core/browser/password_store.h"
 #include "components/password_manager/core/browser/password_store_consumer.h"
 #include "ios/chrome/browser/passwords/ios_chrome_password_store_factory.h"
+#import "ios/chrome/browser/ui/autofill/manual_fill/all_password_coordinator.h"
 #import "ios/chrome/browser/ui/autofill/manual_fill/manual_fill_accessory_view_controller.h"
-#import "ios/chrome/browser/ui/autofill/manual_fill/password_coordinator.h"
 #import "ios/chrome/browser/ui/autofill/manual_fill/password_mediator.h"
 #import "ios/chrome/browser/ui/autofill/manual_fill/password_view_controller.h"
 #import "ios/chrome/browser/ui/settings/password/passwords_table_view_controller.h"
@@ -87,7 +87,7 @@
 
 id<GREYMatcher> OtherPasswordsDismissMatcher() {
   return grey_accessibilityID(
-      manual_fill::PasswordDoneButtonAccessibilityIdentifier);
+      manual_fill::kPasswordDoneButtonAccessibilityIdentifier);
 }
 
 // Returns a matcher for the example username in the list.
diff --git a/ios/chrome/browser/ui/reading_list/reading_list_egtest.mm b/ios/chrome/browser/ui/reading_list/reading_list_egtest.mm
index 1102131..ee084d1 100644
--- a/ios/chrome/browser/ui/reading_list/reading_list_egtest.mm
+++ b/ios/chrome/browser/ui/reading_list/reading_list_egtest.mm
@@ -6,6 +6,7 @@
 #import <UIKit/UIKit.h>
 #import <XCTest/XCTest.h>
 
+#include <functional>
 #include <memory>
 
 #include "base/bind.h"
@@ -500,13 +501,13 @@
   self.testServer->RegisterRequestHandler(base::BindRepeating(
       &net::test_server::HandlePrefixedRequest, kDistillableURL,
       base::BindRepeating(&HandleQueryOrCloseSocket,
-                          base::ConstRef(_serverRespondsWithContent),
-                          base::ConstRef(_serverResponseDelay), true)));
+                          std::cref(_serverRespondsWithContent),
+                          std::cref(_serverResponseDelay), true)));
   self.testServer->RegisterRequestHandler(base::BindRepeating(
       &net::test_server::HandlePrefixedRequest, kNonDistillableURL,
       base::BindRepeating(&HandleQueryOrCloseSocket,
-                          base::ConstRef(_serverRespondsWithContent),
-                          base::ConstRef(_serverResponseDelay), false)));
+                          std::cref(_serverRespondsWithContent),
+                          std::cref(_serverResponseDelay), false)));
   self.serverRespondsWithContent = true;
   GREYAssertTrue(self.testServer->Start(), @"Test server failed to start.");
 }
diff --git a/ios/chrome/browser/ui/settings/cells/table_view_clear_browsing_data_item.mm b/ios/chrome/browser/ui/settings/cells/table_view_clear_browsing_data_item.mm
index edbe2390..2af8f06 100644
--- a/ios/chrome/browser/ui/settings/cells/table_view_clear_browsing_data_item.mm
+++ b/ios/chrome/browser/ui/settings/cells/table_view_clear_browsing_data_item.mm
@@ -230,8 +230,12 @@
 }
 
 - (NSString*)accessibilityValue {
-  return [@[ self.detailTextLabel.text, self.optionalTextLabel.text ]
-      componentsJoinedByString:@". "];
+  if (self.optionalTextLabel && self.detailTextLabel) {
+    return [@[ self.detailTextLabel.text, self.optionalTextLabel.text ]
+        componentsJoinedByString:@". "];
+  } else {
+    return self.detailTextLabel.text;
+  }
 }
 
 @end
diff --git a/ios/chrome/browser/ui/settings/settings_table_view_controller.mm b/ios/chrome/browser/ui/settings/settings_table_view_controller.mm
index 58c63d3..8200a3b 100644
--- a/ios/chrome/browser/ui/settings/settings_table_view_controller.mm
+++ b/ios/chrome/browser/ui/settings/settings_table_view_controller.mm
@@ -1039,7 +1039,12 @@
       [UIImage imageNamed:kSyncAndGoogleServicesImageName];
   SyncSetupService* syncSetupService =
       SyncSetupServiceFactory::GetForBrowserState(_browserState);
-  if (!syncSetupService->HasFinishedInitialSetup()) {
+  AuthenticationService* authService =
+      AuthenticationServiceFactory::GetForBrowserState(_browserState);
+  if (!authService->IsAuthenticated()) {
+    // No sync status when the user is not signed-in.
+    googleServicesItem.detailText = nil;
+  } else if (!syncSetupService->HasFinishedInitialSetup()) {
     googleServicesItem.detailText =
         l10n_util::GetNSString(IDS_IOS_SYNC_SETUP_IN_PROGRESS);
   } else if (!IsTransientSyncError(syncSetupService->GetSyncServiceState())) {
diff --git a/ios/chrome/browser/web/error_page_egtest.mm b/ios/chrome/browser/web/error_page_egtest.mm
index 6fad78c..d4e3c45 100644
--- a/ios/chrome/browser/web/error_page_egtest.mm
+++ b/ios/chrome/browser/web/error_page_egtest.mm
@@ -4,6 +4,7 @@
 
 #import <EarlGrey/EarlGrey.h>
 
+#include <functional>
 #include <string>
 
 #include "base/bind.h"
@@ -43,7 +44,7 @@
   self.testServer->RegisterRequestHandler(base::BindRepeating(
       &net::test_server::HandlePrefixedRequest, "/echo-query",
       base::BindRepeating(&testing::HandleEchoQueryOrCloseSocket,
-                          base::ConstRef(_serverRespondsWithContent))));
+                          std::cref(_serverRespondsWithContent))));
 
   GREYAssertTrue(self.testServer->Start(), @"Test server failed to start.");
 }
diff --git a/ios/components/io_thread/ios_io_thread.mm b/ios/components/io_thread/ios_io_thread.mm
index d1d5f57..a795a6a 100644
--- a/ios/components/io_thread/ios_io_thread.mm
+++ b/ios/components/io_thread/ios_io_thread.mm
@@ -322,8 +322,7 @@
       std::make_unique<net::HttpAuthPreferences>();
   globals_->http_auth_handler_factory =
       net::HttpAuthHandlerRegistryFactory::Create(
-          globals_->host_resolver.get(), globals_->http_auth_preferences.get(),
-          supported_schemes);
+          globals_->http_auth_preferences.get(), supported_schemes);
 }
 
 void IOSIOThread::ClearHostCache() {
diff --git a/ios/web/features.mm b/ios/web/features.mm
index 9db3fa9b..9f5b855 100644
--- a/ios/web/features.mm
+++ b/ios/web/features.mm
@@ -26,7 +26,7 @@
                                            base::FEATURE_DISABLED_BY_DEFAULT};
 
 const base::Feature kStorePendingItemInContext{
-    "StorePendingItemInContext", base::FEATURE_DISABLED_BY_DEFAULT};
+    "StorePendingItemInContext", base::FEATURE_ENABLED_BY_DEFAULT};
 
 const base::Feature kWKHTTPSystemCookieStore{"WKHTTPSystemCookieStore",
                                              base::FEATURE_ENABLED_BY_DEFAULT};
diff --git a/ios/web/shell/shell_url_request_context_getter.mm b/ios/web/shell/shell_url_request_context_getter.mm
index 11e29eb..2e4038ac 100644
--- a/ios/web/shell/shell_url_request_context_getter.mm
+++ b/ios/web/shell/shell_url_request_context_getter.mm
@@ -121,7 +121,7 @@
         net::HostResolver::CreateDefaultResolver(
             url_request_context_->net_log()));
     storage_->set_http_auth_handler_factory(
-        net::HttpAuthHandlerFactory::CreateDefault(host_resolver.get()));
+        net::HttpAuthHandlerFactory::CreateDefault());
     storage_->set_host_resolver(std::move(host_resolver));
 
     net::HttpNetworkSession::Context network_session_context;
diff --git a/ios/web/web_state/error_page_inttest.mm b/ios/web/web_state/error_page_inttest.mm
index 747a8e6a..874a2a8 100644
--- a/ios/web/web_state/error_page_inttest.mm
+++ b/ios/web/web_state/error_page_inttest.mm
@@ -2,6 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+#include <functional>
+
 #include "base/bind.h"
 #include "base/strings/stringprintf.h"
 #include "base/strings/sys_string_conversions.h"
@@ -83,7 +85,7 @@
     server_.RegisterRequestHandler(base::BindRepeating(
         &net::test_server::HandlePrefixedRequest, "/echo-query",
         base::BindRepeating(&testing::HandleEchoQueryOrCloseSocket,
-                            base::ConstRef(server_responds_with_content_))));
+                            std::cref(server_responds_with_content_))));
     server_.RegisterRequestHandler(
         base::BindRepeating(&net::test_server::HandlePrefixedRequest, "/iframe",
                             base::BindRepeating(&testing::HandleIFrame)));
diff --git a/ios/web_view/internal/web_view_url_request_context_getter.mm b/ios/web_view/internal/web_view_url_request_context_getter.mm
index c1ff8c66..3be9309 100644
--- a/ios/web_view/internal/web_view_url_request_context_getter.mm
+++ b/ios/web_view/internal/web_view_url_request_context_getter.mm
@@ -120,7 +120,7 @@
         net::HostResolver::CreateDefaultResolver(
             url_request_context_->net_log()));
     storage_->set_http_auth_handler_factory(
-        net::HttpAuthHandlerFactory::CreateDefault(host_resolver.get()));
+        net::HttpAuthHandlerFactory::CreateDefault());
     storage_->set_host_resolver(std::move(host_resolver));
 
     net::HttpNetworkSession::Context network_session_context;
diff --git a/media/BUILD.gn b/media/BUILD.gn
index b8af37f..68bd042 100644
--- a/media/BUILD.gn
+++ b/media/BUILD.gn
@@ -35,7 +35,6 @@
     "ENABLE_MEDIA_REMOTING_RPC=$enable_media_remoting_rpc",
     "ENABLE_MPEG_H_AUDIO_DEMUXING=$enable_mpeg_h_audio_demuxing",
     "ENABLE_MSE_MPEG2TS_STREAM_PARSER=$enable_mse_mpeg2ts_stream_parser",
-    "ENABLE_RUNTIME_MEDIA_RENDERER_SELECTION=$enable_runtime_media_renderer_selection",
     "USE_PROPRIETARY_CODECS=$proprietary_codecs",
   ]
 }
diff --git a/media/base/BUILD.gn b/media/base/BUILD.gn
index 213ad99d..fc69db3 100644
--- a/media/base/BUILD.gn
+++ b/media/base/BUILD.gn
@@ -183,6 +183,7 @@
     "media_track.h",
     "media_tracks.cc",
     "media_tracks.h",
+    "media_types.cc",
     "media_types.h",
     "media_url_demuxer.cc",
     "media_url_demuxer.h",
diff --git a/media/base/android/java/src/org/chromium/media/MediaCodecUtil.java b/media/base/android/java/src/org/chromium/media/MediaCodecUtil.java
index 72cf45b..0d9e444 100644
--- a/media/base/android/java/src/org/chromium/media/MediaCodecUtil.java
+++ b/media/base/android/java/src/org/chromium/media/MediaCodecUtil.java
@@ -197,7 +197,7 @@
     private static boolean canDecode(String mime, boolean isSecure) {
         // TODO(liberato): Should we insist on software here?
         CodecCreationInfo info = createDecoder(mime, isSecure ? CodecType.SECURE : CodecType.ANY);
-        if (info.mediaCodec == null) return false;
+        if (info == null || info.mediaCodec == null) return false;
 
         try {
             info.mediaCodec.release();
diff --git a/media/base/media_switches.cc b/media/base/media_switches.cc
index 4ba8ac8..4880efb 100644
--- a/media/base/media_switches.cc
+++ b/media/base/media_switches.cc
@@ -81,13 +81,6 @@
 const char kUnsafelyAllowProtectedMediaIdentifierForDomain[] =
     "unsafely-allow-protected-media-identifier-for-domain";
 
-#if BUILDFLAG(ENABLE_RUNTIME_MEDIA_RENDERER_SELECTION)
-// Rather than use the renderer hosted remotely in the media service, fall back
-// to the default renderer within content_renderer. Does not change the behavior
-// of the media service.
-const char kDisableMojoRenderer[] = "disable-mojo-renderer";
-#endif  // BUILDFLAG(ENABLE_RUNTIME_MEDIA_RENDERER_SELECTION)
-
 // Use fake device for Media Stream to replace actual camera and microphone.
 const char kUseFakeDeviceForMediaStream[] = "use-fake-device-for-media-stream";
 
@@ -389,10 +382,17 @@
 const base::Feature kMediaDrmPersistentLicense{
     "MediaDrmPersistentLicense", base::FEATURE_ENABLED_BY_DEFAULT};
 
+// Determines if MediaDrmOriginIdManager should attempt to pre-provision origin
+// IDs at startup (whenever a profile is loaded). Also used by tests that
+// disable it so that the tests can setup before pre-provisioning is done.
+const base::Feature kMediaDrmPreprovisioningAtStartup{
+    "MediaDrmPreprovisioningAtStartup", base::FEATURE_DISABLED_BY_DEFAULT};
+
 // Enables the Android Image Reader path for Video decoding(for AVDA and MCVD)
 const base::Feature kAImageReaderVideoOutput{"AImageReaderVideoOutput",
                                              base::FEATURE_DISABLED_BY_DEFAULT};
-#endif
+
+#endif  // defined(OS_ANDROID)
 
 #if defined(OS_WIN)
 // Does NV12->NV12 video copy on the main thread right before the texture's
diff --git a/media/base/media_switches.h b/media/base/media_switches.h
index b2ee1479..6b37b9c 100644
--- a/media/base/media_switches.h
+++ b/media/base/media_switches.h
@@ -56,10 +56,6 @@
 MEDIA_EXPORT extern const char
     kUnsafelyAllowProtectedMediaIdentifierForDomain[];
 
-#if BUILDFLAG(ENABLE_RUNTIME_MEDIA_RENDERER_SELECTION)
-MEDIA_EXPORT extern const char kDisableMojoRenderer[];
-#endif  // BUILDFLAG(ENABLE_RUNTIME_MEDIA_RENDERER_SELECTION)
-
 MEDIA_EXPORT extern const char kUseFakeDeviceForMediaStream[];
 MEDIA_EXPORT extern const char kUseFileForFakeVideoCapture[];
 MEDIA_EXPORT extern const char kUseFileForFakeAudioCapture[];
@@ -147,6 +143,7 @@
 MEDIA_EXPORT extern const base::Feature kVideoFullscreenOrientationLock;
 MEDIA_EXPORT extern const base::Feature kVideoRotateToFullscreen;
 MEDIA_EXPORT extern const base::Feature kMediaDrmPersistentLicense;
+MEDIA_EXPORT extern const base::Feature kMediaDrmPreprovisioningAtStartup;
 MEDIA_EXPORT extern const base::Feature kAImageReaderVideoOutput;
 #endif  // defined(OS_ANDROID)
 
diff --git a/media/base/media_types.cc b/media/base/media_types.cc
new file mode 100644
index 0000000..bc980c1
--- /dev/null
+++ b/media/base/media_types.cc
@@ -0,0 +1,76 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "media/base/media_types.h"
+
+#include <tuple>
+
+namespace media {
+
+// static
+AudioType AudioType::FromDecoderConfig(const AudioDecoderConfig& config) {
+  return {config.codec()};
+}
+
+// static
+VideoType VideoType::FromDecoderConfig(const VideoDecoderConfig& config) {
+  // Level is not part of |config|. Its also not always known in the container
+  // metadata (e.g. WebM puts it in CodecPrivate which is often not included).
+  // Level is not used by /media to make/break support decisions, but
+  // embedders with strict hardware limitations could theoretically check it.
+  // The following attempts to make a safe guess by choosing the lowest level
+  // for the given codec.
+
+  // Zero is not a valid level for any of the following codecs. It means
+  // "unknown" or "no level" (e.g. VP8).
+  int level = 0;
+
+  switch (config.codec()) {
+    // These have no notion of level.
+    case kUnknownVideoCodec:
+    case kCodecTheora:
+    case kCodecVP8:
+    // These use non-numeric levels, aren't part of our mime code, and
+    // are ancient with very limited support.
+    case kCodecVC1:
+    case kCodecMPEG2:
+    case kCodecMPEG4:
+      break;
+    case kCodecH264:
+    case kCodecVP9:
+    case kCodecHEVC:
+      // 10 is the level_idc for level 1.0.
+      level = 10;
+      break;
+    case kCodecDolbyVision:
+      // Dolby doesn't do decimals, so 1 is just 1.
+      level = 1;
+      break;
+    case kCodecAV1:
+      // Strangely, AV1 starts at 2.0.
+      level = 20;
+      break;
+  }
+
+  return {config.codec(), config.profile(), level, config.color_space_info()};
+}
+
+bool operator==(const AudioType& x, const AudioType& y) {
+  return x.codec == y.codec;
+}
+
+bool operator!=(const AudioType& x, const AudioType& y) {
+  return !(x == y);
+}
+
+bool operator==(const VideoType& x, const VideoType& y) {
+  return std::tie(x.codec, x.profile, x.level, x.color_space) ==
+         std::tie(y.codec, y.profile, y.level, y.color_space);
+}
+
+bool operator!=(const VideoType& x, const VideoType& y) {
+  return !(x == y);
+}
+
+}  // namespace media
\ No newline at end of file
diff --git a/media/base/media_types.h b/media/base/media_types.h
index b1b9498e..3b40976 100644
--- a/media/base/media_types.h
+++ b/media/base/media_types.h
@@ -6,27 +6,38 @@
 #define MEDIA_BASE_MEDIA_TYPES_H_
 
 #include "media/base/audio_codecs.h"
+#include "media/base/audio_decoder_config.h"
 #include "media/base/media_export.h"
 #include "media/base/video_codecs.h"
 #include "media/base/video_color_space.h"
+#include "media/base/video_decoder_config.h"
 
 namespace media {
 
 // These structures represent parsed audio/video content types (mime strings).
-// These are a subset of {Audio|Video}DecoderConfig classes, which can only be
-// created after demuxing.
+// These are generally a subset of {Audio|Video}DecoderConfig classes, which can
+// only be created after demuxing.
 
 struct MEDIA_EXPORT AudioType {
+  static AudioType FromDecoderConfig(const AudioDecoderConfig& config);
+
   AudioCodec codec;
 };
 
 struct MEDIA_EXPORT VideoType {
+  static VideoType FromDecoderConfig(const VideoDecoderConfig& config);
+
   VideoCodec codec;
   VideoCodecProfile profile;
   int level;
   VideoColorSpace color_space;
 };
 
+MEDIA_EXPORT bool operator==(const AudioType& x, const AudioType& y);
+MEDIA_EXPORT bool operator!=(const AudioType& x, const AudioType& y);
+MEDIA_EXPORT bool operator==(const VideoType& x, const VideoType& y);
+MEDIA_EXPORT bool operator!=(const VideoType& x, const VideoType& y);
+
 }  // namespace media
 
 #endif  // MEDIA_BASE_MEDIA_TYPES_H_
diff --git a/media/base/mock_filters.cc b/media/base/mock_filters.cc
index 249413d..105bc5a 100644
--- a/media/base/mock_filters.cc
+++ b/media/base/mock_filters.cc
@@ -266,4 +266,8 @@
 
 MockStreamParser::~MockStreamParser() = default;
 
+MockMediaClient::MockMediaClient() = default;
+
+MockMediaClient::~MockMediaClient() = default;
+
 }  // namespace media
diff --git a/media/base/mock_filters.h b/media/base/mock_filters.h
index 1c4414fee..8505f32 100644
--- a/media/base/mock_filters.h
+++ b/media/base/mock_filters.h
@@ -9,6 +9,7 @@
 
 #include <memory>
 #include <string>
+#include <vector>
 
 #include "base/callback.h"
 #include "base/macros.h"
@@ -25,6 +26,7 @@
 #include "media/base/decoder_buffer.h"
 #include "media/base/decryptor.h"
 #include "media/base/demuxer.h"
+#include "media/base/media_client.h"
 #include "media/base/media_track.h"
 #include "media/base/pipeline.h"
 #include "media/base/pipeline_status.h"
@@ -607,6 +609,24 @@
   DISALLOW_COPY_AND_ASSIGN(MockStreamParser);
 };
 
+class MockMediaClient : public media::MediaClient {
+ public:
+  MockMediaClient();
+  ~MockMediaClient() override;
+
+  // MediaClient implementation.
+  MOCK_METHOD1(AddSupportedKeySystems,
+               void(std::vector<std::unique_ptr<media::KeySystemProperties>>*
+                        key_systems));
+  MOCK_METHOD0(IsKeySystemsUpdateNeeded, bool());
+  MOCK_METHOD1(IsSupportedAudioType, bool(const media::AudioType& type));
+  MOCK_METHOD1(IsSupportedVideoType, bool(const media::VideoType& type));
+  MOCK_METHOD1(IsSupportedBitstreamAudioCodec, bool(media::AudioCodec codec));
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(MockMediaClient);
+};
+
 }  // namespace media
 
 #endif  // MEDIA_BASE_MOCK_FILTERS_H_
diff --git a/media/base/supported_types.cc b/media/base/supported_types.cc
index 1795a783..d9a6b5b 100644
--- a/media/base/supported_types.cc
+++ b/media/base/supported_types.cc
@@ -251,10 +251,16 @@
     case media::kUnknownVideoCodec:
     case media::kCodecVC1:
     case media::kCodecMPEG2:
-    case media::kCodecMPEG4:
     case media::kCodecHEVC:
     case media::kCodecDolbyVision:
       return false;
+
+    case media::kCodecMPEG4:
+#if defined(OS_CHROMEOS)
+      return true;
+#else
+      return false;
+#endif
   }
 
   NOTREACHED();
diff --git a/media/base/supported_types_unittest.cc b/media/base/supported_types_unittest.cc
index b8b2c7a9..a70c6fd 100644
--- a/media/base/supported_types_unittest.cc
+++ b/media/base/supported_types_unittest.cc
@@ -14,6 +14,12 @@
 const bool kPropCodecsEnabled = false;
 #endif
 
+#if defined(OS_CHROMEOS) && BUILDFLAG(USE_PROPRIETARY_CODECS)
+const bool kMpeg4Supported = true;
+#else
+const bool kMpeg4Supported = false;
+#endif
+
 TEST(SupportedTypesTest, IsSupportedVideoTypeBasics) {
   // Default to common 709.
   const media::VideoColorSpace kColorSpace = media::VideoColorSpace::REC709();
@@ -41,9 +47,6 @@
   EXPECT_FALSE(IsSupportedVideoType({media::kCodecMPEG2,
                                      media::VIDEO_CODEC_PROFILE_UNKNOWN,
                                      kUnspecifiedLevel, kColorSpace}));
-  EXPECT_FALSE(IsSupportedVideoType({media::kCodecMPEG4,
-                                     media::VIDEO_CODEC_PROFILE_UNKNOWN,
-                                     kUnspecifiedLevel, kColorSpace}));
   EXPECT_FALSE(IsSupportedVideoType({media::kCodecHEVC,
                                      media::VIDEO_CODEC_PROFILE_UNKNOWN,
                                      kUnspecifiedLevel, kColorSpace}));
@@ -53,6 +56,10 @@
       kPropCodecsEnabled,
       IsSupportedVideoType(
           {media::kCodecH264, media::H264PROFILE_BASELINE, 1, kColorSpace}));
+  EXPECT_EQ(kMpeg4Supported,
+            IsSupportedVideoType({media::kCodecMPEG4,
+                                  media::VIDEO_CODEC_PROFILE_UNKNOWN,
+                                  kUnspecifiedLevel, kColorSpace}));
 }
 
 TEST(SupportedTypesTest, IsSupportedVideoType_VP9TransferFunctions) {
diff --git a/media/blink/cache_util_unittest.cc b/media/blink/cache_util_unittest.cc
index e7ef430d..ef5953c 100644
--- a/media/blink/cache_util_unittest.cc
+++ b/media/blink/cache_util_unittest.cc
@@ -36,7 +36,7 @@
 static WebURLResponse CreateResponse(const GRFUTestCase& test) {
   WebURLResponse response;
   response.SetHTTPVersion(test.version);
-  response.SetHTTPStatusCode(test.status_code);
+  response.SetHttpStatusCode(test.status_code);
   for (const std::string& line :
        base::SplitString(test.headers, "\n", base::KEEP_WHITESPACE,
                          base::SPLIT_WANT_NONEMPTY)) {
diff --git a/media/blink/multibuffer_data_source_unittest.cc b/media/blink/multibuffer_data_source_unittest.cc
index d9f5c36..419d16d 100644
--- a/media/blink/multibuffer_data_source_unittest.cc
+++ b/media/blink/multibuffer_data_source_unittest.cc
@@ -1385,7 +1385,7 @@
   // Server responds with a redirect.
   blink::WebURL url{GURL(kHttpDifferentPathUrl)};
   blink::WebURLResponse response((GURL(kHttpUrl)));
-  response.SetHTTPStatusCode(307);
+  response.SetHttpStatusCode(307);
   data_provider()->WillFollowRedirect(url, response);
   Respond(response_generator_->Generate206(kDataSize));
   ReceiveData(kDataSize);
@@ -1401,7 +1401,7 @@
   // Server responds with a redirect.
   blink::WebURL url{GURL(kHttpDifferentPathUrl)};
   blink::WebURLResponse response((GURL(kHttpUrl)));
-  response.SetHTTPStatusCode(307);
+  response.SetHttpStatusCode(307);
   data_provider()->WillFollowRedirect(url, response);
 
   EXPECT_CALL(host_, SetTotalBytes(response_generator_->content_length()));
@@ -1423,7 +1423,7 @@
   // Server responds with a redirect.
   blink::WebURL url{GURL(kHttpDifferentPathUrl)};
   blink::WebURLResponse response((GURL(kHttpUrl)));
-  response.SetHTTPStatusCode(307);
+  response.SetHttpStatusCode(307);
   data_provider()->WillFollowRedirect(url, response);
 
   EXPECT_CALL(host_, AddBufferedByteRange(0, kDataSize));
@@ -1437,7 +1437,7 @@
   // Server responds with a redirect.
   blink::WebURL url{GURL(kHttpDifferentPathUrl)};
   blink::WebURLResponse response((GURL(kHttpUrl)));
-  response.SetHTTPStatusCode(307);
+  response.SetHttpStatusCode(307);
   data_provider()->WillFollowRedirect(url, response);
 
   Respond(response_generator_->Generate404());
@@ -1475,7 +1475,7 @@
   Initialize(kHttpUrl, true);
   GURL gurl(kHttpUrl);
   blink::WebURLResponse response(gurl);
-  response.SetHTTPStatusCode(200);
+  response.SetHttpStatusCode(200);
   response.SetHTTPHeaderField(
       WebString::FromUTF8("Content-Length"),
       WebString::FromUTF8(base::NumberToString(kDataSize / 2)));
diff --git a/media/blink/resource_multibuffer_data_provider_unittest.cc b/media/blink/resource_multibuffer_data_provider_unittest.cc
index ac2a64f0..f1d95a78 100644
--- a/media/blink/resource_multibuffer_data_provider_unittest.cc
+++ b/media/blink/resource_multibuffer_data_provider_unittest.cc
@@ -111,7 +111,7 @@
         WebString::FromUTF8("Content-Length"),
         WebString::FromUTF8(base::StringPrintf("%" PRId64, instance_size)));
     response.SetExpectedContentLength(instance_size);
-    response.SetHTTPStatusCode(kHttpOK);
+    response.SetHttpStatusCode(kHttpOK);
     loader_->DidReceiveResponse(response);
 
     if (ok) {
@@ -156,7 +156,7 @@
                                   WebString::FromUTF8("bytes"));
     }
 
-    response.SetHTTPStatusCode(kHttpPartialContent);
+    response.SetHttpStatusCode(kHttpPartialContent);
     loader_->DidReceiveResponse(response);
 
     EXPECT_EQ(instance_size, url_data_->length());
@@ -244,7 +244,7 @@
   EXPECT_CALL(*this, RedirectCallback(scoped_refptr<UrlData>(nullptr)));
 
   WebURLResponse response(gurl_);
-  response.SetHTTPStatusCode(404);
+  response.SetHttpStatusCode(404);
   response.SetHTTPStatusText("Not Found\n");
   loader_->DidReceiveResponse(response);
 }
@@ -308,7 +308,7 @@
                                              "%d-%d/%d",
                                              1, 10, 1024)));
   response.SetExpectedContentLength(10);
-  response.SetHTTPStatusCode(kHttpPartialContent);
+  response.SetHttpStatusCode(kHttpPartialContent);
   loader_->DidReceiveResponse(response);
 }
 
diff --git a/media/blink/test_response_generator.cc b/media/blink/test_response_generator.cc
index b9b3296c6..998bffe8 100644
--- a/media/blink/test_response_generator.cc
+++ b/media/blink/test_response_generator.cc
@@ -27,7 +27,7 @@
 
 WebURLResponse TestResponseGenerator::Generate200() {
   WebURLResponse response(gurl_);
-  response.SetHTTPStatusCode(200);
+  response.SetHttpStatusCode(200);
 
   response.SetHTTPHeaderField(
       WebString::FromUTF8("Content-Length"),
@@ -58,7 +58,7 @@
   int64_t range_content_length = content_length_ - first_byte_offset;
 
   WebURLResponse response(gurl_);
-  response.SetHTTPStatusCode(206);
+  response.SetHttpStatusCode(206);
 
   if ((flags & kNoAcceptRanges) == 0) {
     response.SetHTTPHeaderField(WebString::FromUTF8("Accept-Ranges"),
@@ -88,7 +88,7 @@
 
 WebURLResponse TestResponseGenerator::GenerateResponse(int code) {
   WebURLResponse response(gurl_);
-  response.SetHTTPStatusCode(code);
+  response.SetHttpStatusCode(code);
   return response;
 }
 
@@ -99,7 +99,7 @@
 WebURLResponse TestResponseGenerator::GenerateFileResponse(
     int64_t first_byte_offset) {
   WebURLResponse response(gurl_);
-  response.SetHTTPStatusCode(0);
+  response.SetHttpStatusCode(0);
 
   if (first_byte_offset >= 0) {
     response.SetExpectedContentLength(content_length_ - first_byte_offset);
diff --git a/media/blink/webmediaplayer_impl_unittest.cc b/media/blink/webmediaplayer_impl_unittest.cc
index a073863..d3a7300 100644
--- a/media/blink/webmediaplayer_impl_unittest.cc
+++ b/media/blink/webmediaplayer_impl_unittest.cc
@@ -594,7 +594,7 @@
         blink::WebString::FromUTF8("Content-Length"),
         blink::WebString::FromUTF8(base::NumberToString(data->data_size())));
     response.SetExpectedContentLength(data->data_size());
-    response.SetHTTPStatusCode(200);
+    response.SetHttpStatusCode(200);
     client->DidReceiveResponse(response);
 
     // Copy over the file data and indicate that's everything.
diff --git a/media/capture/video/chromeos/camera_3a_controller_unittest.cc b/media/capture/video/chromeos/camera_3a_controller_unittest.cc
index 8a39e65..97d24d03 100644
--- a/media/capture/video/chromeos/camera_3a_controller_unittest.cc
+++ b/media/capture/video/chromeos/camera_3a_controller_unittest.cc
@@ -4,6 +4,8 @@
 
 #include "media/capture/video/chromeos/camera_3a_controller.h"
 
+#include <functional>
+
 #include "base/bind.h"
 #include "base/synchronization/waitable_event.h"
 #include "base/threading/thread.h"
@@ -70,7 +72,7 @@
     thread_.task_runner()->PostTask(
         location,
         base::BindOnce(&Camera3AControllerTest::RunOnThread,
-                       base::Unretained(this), base::ConstRef(location),
+                       base::Unretained(this), std::cref(location),
                        base::Passed(&closure), base::Unretained(&done)));
     done.Wait();
   }
@@ -80,8 +82,7 @@
     RunOnThreadSync(
         FROM_HERE,
         base::BindOnce(&Camera3AControllerTest::Reset3AControllerOnThread,
-                       base::Unretained(this),
-                       base::ConstRef(static_metadata)));
+                       base::Unretained(this), std::cref(static_metadata)));
   }
 
   template <typename Value>
@@ -320,7 +321,7 @@
   RunOnThreadSync(FROM_HERE,
                   base::BindOnce(&Camera3AController::OnResultMetadataAvailable,
                                  base::Unretained(camera_3a_controller_.get()),
-                                 base::ConstRef(result_metadata)));
+                                 std::cref(result_metadata)));
 
   // |camera_3a_controller_| should call the registered callback once 3A are
   // stabilized.
@@ -339,7 +340,7 @@
   RunOnThreadSync(FROM_HERE,
                   base::BindOnce(&Camera3AController::OnResultMetadataAvailable,
                                  base::Unretained(camera_3a_controller_.get()),
-                                 base::ConstRef(result_metadata)));
+                                 std::cref(result_metadata)));
   done.Wait();
 }
 
diff --git a/media/capture/video/chromeos/reprocess_manager.cc b/media/capture/video/chromeos/reprocess_manager.cc
index 62dedde..d226b28 100644
--- a/media/capture/video/chromeos/reprocess_manager.cc
+++ b/media/capture/video/chromeos/reprocess_manager.cc
@@ -2,6 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+#include <functional>
 #include <utility>
 
 #include "media/capture/video/chromeos/camera_metadata_utils.h"
@@ -97,7 +98,7 @@
       FROM_HERE,
       base::BindOnce(
           &ReprocessManager::ReprocessManagerImpl::UpdateSupportedEffects,
-          base::Unretained(impl.get()), base::ConstRef(metadata)));
+          base::Unretained(impl.get()), std::cref(metadata)));
 }
 
 ReprocessManager::ReprocessManagerImpl::ReprocessManagerImpl() {}
diff --git a/media/cdm/cdm_adapter.cc b/media/cdm/cdm_adapter.cc
index 373cd9b..66cdaff3 100644
--- a/media/cdm/cdm_adapter.cc
+++ b/media/cdm/cdm_adapter.cc
@@ -677,10 +677,7 @@
 }
 
 void CdmAdapter::SetTimer(int64_t delay_ms, void* context) {
-  // TODO(crbug.com/887761): Use CHECKs for bug investigation. Change back to
-  // DCHECK after it's completed.
-  CHECK(task_runner_);
-  CHECK(task_runner_->BelongsToCurrentThread());
+  DCHECK(task_runner_->BelongsToCurrentThread());
 
   auto delay = base::TimeDelta::FromMilliseconds(delay_ms);
   DVLOG(3) << __func__ << ": delay = " << delay << ", context = " << context;
diff --git a/media/ffmpeg/BUILD.gn b/media/ffmpeg/BUILD.gn
index 501d7d86..e0d4b97 100644
--- a/media/ffmpeg/BUILD.gn
+++ b/media/ffmpeg/BUILD.gn
@@ -30,6 +30,7 @@
   deps = [
     "//base",
     "//media/base",
+    "//media/formats",
     "//third_party/ffmpeg",
     "//third_party/ffmpeg:ffmpeg_features",
   ]
diff --git a/media/ffmpeg/ffmpeg_common.cc b/media/ffmpeg/ffmpeg_common.cc
index a38d6a7..4ac39af 100644
--- a/media/ffmpeg/ffmpeg_common.cc
+++ b/media/ffmpeg/ffmpeg_common.cc
@@ -16,6 +16,7 @@
 #include "media/base/media_util.h"
 #include "media/base/video_decoder_config.h"
 #include "media/base/video_util.h"
+#include "media/formats/mp4/box_definitions.h"
 #include "media/media_buildflags.h"
 
 namespace media {
@@ -479,11 +480,26 @@
   // actually handle capabilities requests correctly. http://crbug.com/784610
   VideoCodecProfile profile = VIDEO_CODEC_PROFILE_UNKNOWN;
   switch (codec) {
-#if !BUILDFLAG(ENABLE_FFMPEG_VIDEO_DECODERS)
-    case kCodecH264:
+#if BUILDFLAG(USE_PROPRIETARY_CODECS)
+    case kCodecH264: {
+      profile = ProfileIDToVideoCodecProfile(codec_context->profile);
+      // if the profile is still unknown, try to extract it from
+      // the extradata using the internal parser
+      if (profile == VIDEO_CODEC_PROFILE_UNKNOWN && codec_context->extradata &&
+          codec_context->extradata_size) {
+        mp4::AVCDecoderConfigurationRecord avc_config;
+        if (avc_config.Parse(codec_context->extradata,
+                             codec_context->extradata_size)) {
+          profile = ProfileIDToVideoCodecProfile(avc_config.profile_indication);
+        }
+      }
+      // All the heuristics failed, let's assign a default profile
+      if (profile == VIDEO_CODEC_PROFILE_UNKNOWN)
+        profile = H264PROFILE_BASELINE;
+
       format = PIXEL_FORMAT_I420;
-      profile = H264PROFILE_BASELINE;
       break;
+    }
 #endif
     case kCodecVP8:
 #if !BUILDFLAG(ENABLE_FFMPEG_VIDEO_DECODERS)
diff --git a/media/ffmpeg/ffmpeg_common_unittest.cc b/media/ffmpeg/ffmpeg_common_unittest.cc
index d73fba3d..8e55b95 100644
--- a/media/ffmpeg/ffmpeg_common_unittest.cc
+++ b/media/ffmpeg/ffmpeg_common_unittest.cc
@@ -296,5 +296,31 @@
   printf("</enum>\n");
 #endif
 }
+#if BUILDFLAG(USE_PROPRIETARY_CODECS)
+TEST_F(FFmpegCommonTest, VerifyH264Profile) {
+  // Open a file to get a real AVStreams from FFmpeg.
+  base::MemoryMappedFile file;
+  ASSERT_TRUE(file.Initialize(GetTestDataFilePath("bear-1280x720.mp4")));
+  InMemoryUrlProtocol protocol(file.data(), file.length(), false);
+  FFmpegGlue glue(&protocol);
+  ASSERT_TRUE(glue.OpenContext());
+  AVFormatContext* format_context = glue.format_context();
+
+  for (size_t i = 0; i < format_context->nb_streams; ++i) {
+    AVStream* stream = format_context->streams[i];
+    AVCodecParameters* codec_parameters = stream->codecpar;
+    AVMediaType codec_type = codec_parameters->codec_type;
+
+    if (codec_type == AVMEDIA_TYPE_VIDEO) {
+      VideoDecoderConfig video_config;
+      EXPECT_TRUE(AVStreamToVideoDecoderConfig(stream, &video_config));
+      EXPECT_EQ(H264PROFILE_HIGH, video_config.profile());
+    } else {
+      // Only process video.
+      continue;
+    }
+  }
+}
+#endif
 
 }  // namespace media
diff --git a/media/filters/ffmpeg_audio_decoder.cc b/media/filters/ffmpeg_audio_decoder.cc
index 6669e43..caf8e1a 100644
--- a/media/filters/ffmpeg_audio_decoder.cc
+++ b/media/filters/ffmpeg_audio_decoder.cc
@@ -6,6 +6,8 @@
 
 #include <stdint.h>
 
+#include <functional>
+
 #include "base/bind.h"
 #include "base/callback_helpers.h"
 #include "base/single_thread_task_runner.h"
@@ -165,12 +167,12 @@
   }
 
   bool decoded_frame_this_loop = false;
-  // base::Unretained and base::ConstRef are safe to use with the callback given
+  // base::Unretained and std::cref are safe to use with the callback given
   // to DecodePacket() since that callback is only used the function call.
   switch (decoding_loop_->DecodePacket(
-      &packet, base::BindRepeating(
-                   &FFmpegAudioDecoder::OnNewFrame, base::Unretained(this),
-                   base::ConstRef(buffer), &decoded_frame_this_loop))) {
+      &packet, base::BindRepeating(&FFmpegAudioDecoder::OnNewFrame,
+                                   base::Unretained(this), std::cref(buffer),
+                                   &decoded_frame_this_loop))) {
     case FFmpegDecodingLoop::DecodeStatus::kSendPacketFailed:
       MEDIA_LOG(ERROR, media_log_)
           << "Failed to send audio packet for decoding: "
diff --git a/media/filters/ffmpeg_demuxer.cc b/media/filters/ffmpeg_demuxer.cc
index 410e5f43..b6ca49f 100644
--- a/media/filters/ffmpeg_demuxer.cc
+++ b/media/filters/ffmpeg_demuxer.cc
@@ -33,7 +33,9 @@
 #include "media/base/limits.h"
 #include "media/base/media_log.h"
 #include "media/base/media_tracks.h"
+#include "media/base/media_types.h"
 #include "media/base/sample_rates.h"
+#include "media/base/supported_types.h"
 #include "media/base/timestamp_constants.h"
 #include "media/base/video_codecs.h"
 #include "media/base/webvtt_util.h"
@@ -252,16 +254,15 @@
   if (stream->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) {
     audio_config.reset(new AudioDecoderConfig());
 
-    // IsValidConfig() checks that the codec is supported and that the channel
-    // layout and sample format are valid.
-    //
     // TODO(chcunningham): Change AVStreamToAudioDecoderConfig to check
     // IsValidConfig internally and return a null scoped_ptr if not valid.
     if (!AVStreamToAudioDecoderConfig(stream, audio_config.get()) ||
-        !audio_config->IsValidConfig()) {
+        !audio_config->IsValidConfig() ||
+        !IsSupportedAudioType(AudioType::FromDecoderConfig(*audio_config))) {
       MEDIA_LOG(DEBUG, media_log) << "Warning, FFmpegDemuxer failed to create "
-                                     "a valid audio decoder configuration from "
-                                     "muxed stream";
+                                     "a valid/supported audio decoder "
+                                     "configuration from muxed stream, config:"
+                                  << audio_config->AsHumanReadableString();
       return nullptr;
     }
 
@@ -270,16 +271,15 @@
   } else if (stream->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) {
     video_config.reset(new VideoDecoderConfig());
 
-    // IsValidConfig() checks that the codec is supported and that the channel
-    // layout and sample format are valid.
-    //
     // TODO(chcunningham): Change AVStreamToVideoDecoderConfig to check
     // IsValidConfig internally and return a null scoped_ptr if not valid.
     if (!AVStreamToVideoDecoderConfig(stream, video_config.get()) ||
-        !video_config->IsValidConfig()) {
+        !video_config->IsValidConfig() ||
+        !IsSupportedVideoType(VideoType::FromDecoderConfig(*video_config))) {
       MEDIA_LOG(DEBUG, media_log) << "Warning, FFmpegDemuxer failed to create "
-                                     "a valid video decoder configuration from "
-                                     "muxed stream";
+                                     "a valid/supported video decoder "
+                                     "configuration from muxed stream, config:"
+                                  << video_config->AsHumanReadableString();
       return nullptr;
     }
 
@@ -1419,6 +1419,9 @@
                               base::TimeDelta());
     }
 
+    // TODO(chcunningham): Remove the IsValidConfig() checks below. If the
+    // config isn't valid we shouldn't have created a demuxer stream nor
+    // an entry in |media_tracks|, so the check should always be true.
     if ((codec_type == AVMEDIA_TYPE_AUDIO &&
          media_tracks->getAudioConfig(track_id).IsValidConfig()) ||
         (codec_type == AVMEDIA_TYPE_VIDEO &&
diff --git a/media/filters/ffmpeg_demuxer_unittest.cc b/media/filters/ffmpeg_demuxer_unittest.cc
index ee4ee9f..cada939 100644
--- a/media/filters/ffmpeg_demuxer_unittest.cc
+++ b/media/filters/ffmpeg_demuxer_unittest.cc
@@ -22,9 +22,11 @@
 #include "base/threading/thread_task_runner_handle.h"
 #include "build/build_config.h"
 #include "media/base/decrypt_config.h"
+#include "media/base/media_client.h"
 #include "media/base/media_tracks.h"
 #include "media/base/media_util.h"
 #include "media/base/mock_demuxer_host.h"
+#include "media/base/mock_filters.h"
 #include "media/base/mock_media_log.h"
 #include "media/base/test_helpers.h"
 #include "media/base/timestamp_constants.h"
@@ -35,9 +37,12 @@
 #include "media/formats/mp4/bitstream_converter.h"
 #include "media/media_buildflags.h"
 #include "testing/gtest/include/gtest/gtest.h"
+#include "ui/gfx/color_space.h"
 
+using ::testing::_;
 using ::testing::AnyNumber;
 using ::testing::DoAll;
+using ::testing::Eq;
 using ::testing::Exactly;
 using ::testing::InSequence;
 using ::testing::Invoke;
@@ -47,7 +52,6 @@
 using ::testing::SetArgPointee;
 using ::testing::StrictMock;
 using ::testing::WithArgs;
-using ::testing::_;
 
 namespace media {
 
@@ -66,9 +70,11 @@
 
 MATCHER_P(FailedToCreateValidDecoderConfigFromStream, stream_type, "") {
   return CONTAINS_STRING(
-      arg, "\"debug\":\"Warning, FFmpegDemuxer failed to create a valid " +
-               std::string(stream_type) +
-               " decoder configuration from muxed stream");
+      arg,
+      "\"debug\":\"Warning, FFmpegDemuxer failed to create a "
+      "valid/supported " +
+          std::string(stream_type) +
+          " decoder configuration from muxed stream");
 }
 
 MATCHER_P(SkippingUnsupportedStream, stream_type, "") {
@@ -1343,6 +1349,20 @@
 TEST_F(FFmpegDemuxerTest, HEVC_in_MP4_container) {
   CreateDemuxer("bear-hevc-frag.mp4");
 #if BUILDFLAG(ENABLE_HEVC_DEMUXING)
+  // HEVC is not supported by default media platform. Embedders who add support
+  // must declare it via MediaClient.
+  MockMediaClient media_client;
+  SetMediaClient(&media_client);
+
+  VideoColorSpace color_space(VideoColorSpace::PrimaryID::SMPTE170M,
+                              VideoColorSpace::TransferID::SMPTE170M,
+                              VideoColorSpace::MatrixID::SMPTE170M,
+                              gfx::ColorSpace::RangeID::LIMITED);
+  VideoType hevc_type = {VideoCodec::kCodecHEVC,
+                         VideoCodecProfile::HEVCPROFILE_MAIN, 10, color_space};
+  EXPECT_CALL(media_client, IsSupportedVideoType(Eq(hevc_type)))
+      .WillRepeatedly(Return(true));
+
   InitializeDemuxer();
 
   DemuxerStream* video = GetStream(DemuxerStream::VIDEO);
@@ -1353,6 +1373,8 @@
 
   video->Read(NewReadCB(FROM_HERE, 1042, 200200, false));
   base::RunLoop().Run();
+
+  SetMediaClient(nullptr);
 #else
   InitializeDemuxerAndExpectPipelineStatus(DEMUXER_ERROR_NO_SUPPORTED_STREAMS);
 #endif
@@ -1361,6 +1383,15 @@
 TEST_F(FFmpegDemuxerTest, Read_AC3_Audio) {
   CreateDemuxer("bear-ac3-only-frag.mp4");
 #if BUILDFLAG(ENABLE_AC3_EAC3_AUDIO_DEMUXING)
+  // AC3 is not supported by default media platform. Embedders who add support
+  // must declare it via MediaClient.
+  MockMediaClient media_client;
+  SetMediaClient(&media_client);
+
+  AudioType ac3_type = {AudioCodec::kCodecAC3};
+  EXPECT_CALL(media_client, IsSupportedAudioType(Eq(ac3_type)))
+      .WillRepeatedly(Return(true));
+
   InitializeDemuxer();
 
   // Attempt a read from the audio stream and run the message loop until done.
@@ -1372,6 +1403,8 @@
 
   audio->Read(NewReadCB(FROM_HERE, 836, 34830, true));
   base::RunLoop().Run();
+
+  SetMediaClient(nullptr);
 #else
   InitializeDemuxerAndExpectPipelineStatus(DEMUXER_ERROR_NO_SUPPORTED_STREAMS);
 #endif
@@ -1380,6 +1413,15 @@
 TEST_F(FFmpegDemuxerTest, Read_EAC3_Audio) {
   CreateDemuxer("bear-eac3-only-frag.mp4");
 #if BUILDFLAG(ENABLE_AC3_EAC3_AUDIO_DEMUXING)
+  // EAC3 is not supported by default media platform. Embedders who add support
+  // must declare it via MediaClient.
+  MockMediaClient media_client;
+  SetMediaClient(&media_client);
+
+  AudioType eac3_type = {AudioCodec::kCodecEAC3};
+  EXPECT_CALL(media_client, IsSupportedAudioType(Eq(eac3_type)))
+      .WillRepeatedly(Return(true));
+
   InitializeDemuxer();
 
   // Attempt a read from the audio stream and run the message loop until done.
@@ -1391,6 +1433,8 @@
 
   audio->Read(NewReadCB(FROM_HERE, 872, 34830, true));
   base::RunLoop().Run();
+
+  SetMediaClient(nullptr);
 #else
   InitializeDemuxerAndExpectPipelineStatus(DEMUXER_ERROR_NO_SUPPORTED_STREAMS);
 #endif
diff --git a/media/filters/gpu_video_decoder.cc b/media/filters/gpu_video_decoder.cc
index 71789da..72088c75 100644
--- a/media/filters/gpu_video_decoder.cc
+++ b/media/filters/gpu_video_decoder.cc
@@ -687,6 +687,10 @@
   frame->set_color_space(picture.color_space());
   if (picture.allow_overlay())
     frame->metadata()->SetBoolean(VideoFrameMetadata::ALLOW_OVERLAY, true);
+  if (picture.read_lock_fences_enabled()) {
+    frame->metadata()->SetBoolean(VideoFrameMetadata::READ_LOCK_FENCES_ENABLED,
+                                  true);
+  }
   if (picture.texture_owner())
     frame->metadata()->SetBoolean(VideoFrameMetadata::TEXTURE_OWNER, true);
   if (picture.wants_promotion_hint()) {
diff --git a/media/formats/BUILD.gn b/media/formats/BUILD.gn
index be967c8..550746d 100644
--- a/media/formats/BUILD.gn
+++ b/media/formats/BUILD.gn
@@ -14,6 +14,7 @@
     "//media/cdm",
     "//media/filters",
     "//media/muxers",
+    "//media/ffmpeg",
   ]
 
   sources = [
diff --git a/media/formats/mp2t/descriptors.cc b/media/formats/mp2t/descriptors.cc
index b79a0871..a0a771e 100644
--- a/media/formats/mp2t/descriptors.cc
+++ b/media/formats/mp2t/descriptors.cc
@@ -9,7 +9,6 @@
 #include "base/logging.h"
 #include "media/base/bit_reader.h"
 #include "media/base/encryption_pattern.h"
-#include "media/base/encryption_scheme.h"
 #include "media/formats/mp2t/mp2t_common.h"
 
 namespace media {
@@ -115,7 +114,7 @@
 
 bool Descriptors::HasCADescriptorCenc(int* ca_pid,
                                       int* pssh_pid,
-                                      EncryptionScheme* scheme) const {
+                                      EncryptionMode* mode) const {
   DCHECK(ca_pid);
   DCHECK(pssh_pid);
   int system_id;
@@ -146,8 +145,8 @@
   RCHECK(reader.ReadBits(13, pssh_pid));
   // The pattern is actually set differently for audio and video, so OK not to
   // set it here. Important thing is to set the cipher mode.
-  *scheme = EncryptionScheme(EncryptionScheme::CIPHER_MODE_AES_CBC,
-                             EncryptionPattern());
+  *mode = EncryptionMode::kCbcs;
+
   return true;
 }
 
diff --git a/media/formats/mp2t/descriptors.h b/media/formats/mp2t/descriptors.h
index 6737d09..8e444f8 100644
--- a/media/formats/mp2t/descriptors.h
+++ b/media/formats/mp2t/descriptors.h
@@ -10,10 +10,11 @@
 #include <map>
 #include <string>
 
+#include "media/base/decrypt_config.h"
+
 namespace media {
 
 class BitReader;
-class EncryptionScheme;
 
 namespace mp2t {
 
@@ -48,10 +49,10 @@
   // Indicates whether a CA descriptor is present, and if so, whether it is
   // of the type defined by ISO/IEC 23001-9:2014 (i.e. with a specific
   // system_id value and layout of the private_data). If so, the |ca_pid|,
-  // |pssh_pid| and |scheme| are populated with the contents of the descriptor.
+  // |pssh_pid| and |mode| are populated with the contents of the descriptor.
   bool HasCADescriptorCenc(int* ca_pid,
                            int* pssh_pid,
-                           EncryptionScheme* scheme) const;
+                           EncryptionMode* mode) const;
 
   // Indicates whether a Private Data Indicator descriptor is present with a
   // particular |value|.
diff --git a/media/formats/mp2t/es_parser_adts.cc b/media/formats/mp2t/es_parser_adts.cc
index 05e63d21..1834049 100644
--- a/media/formats/mp2t/es_parser_adts.cc
+++ b/media/formats/mp2t/es_parser_adts.cc
@@ -15,7 +15,6 @@
 #include "media/base/bit_reader.h"
 #include "media/base/channel_layout.h"
 #include "media/base/encryption_pattern.h"
-#include "media/base/encryption_scheme.h"
 #include "media/base/media_util.h"
 #include "media/base/stream_parser_buffer.h"
 #include "media/base/timestamp_constants.h"
@@ -220,7 +219,7 @@
             std::make_unique<DecryptConfig>(
                 base_decrypt_config->encryption_mode(),
                 base_decrypt_config->key_id(), base_decrypt_config->iv(),
-                subsamples, base_decrypt_config->encryption_pattern()));
+                subsamples, EncryptionPattern()));
       }
     }
 #endif
diff --git a/media/formats/mp2t/es_parser_h264.cc b/media/formats/mp2t/es_parser_h264.cc
index b893adb3a..41d85555 100644
--- a/media/formats/mp2t/es_parser_h264.cc
+++ b/media/formats/mp2t/es_parser_h264.cc
@@ -11,7 +11,6 @@
 #include "base/optional.h"
 #include "media/base/decrypt_config.h"
 #include "media/base/encryption_pattern.h"
-#include "media/base/encryption_scheme.h"
 #include "media/base/media_util.h"
 #include "media/base/stream_parser_buffer.h"
 #include "media/base/timestamp_constants.h"
@@ -485,10 +484,8 @@
             DecryptConfig::CreateCbcsConfig(
                 base_decrypt_config->key_id(), base_decrypt_config->iv(),
                 subsamples,
-                base_decrypt_config->HasPattern()
-                    ? base_decrypt_config->encryption_pattern()
-                    : EncryptionPattern(kSampleAESEncryptBlocks,
-                                        kSampleAESSkipBlocks)));
+                EncryptionPattern(kSampleAESEncryptBlocks,
+                                  kSampleAESSkipBlocks)));
         break;
     }
   }
diff --git a/media/formats/mp2t/mp2t_stream_parser.cc b/media/formats/mp2t/mp2t_stream_parser.cc
index 92515ad..bc281ae 100644
--- a/media/formats/mp2t/mp2t_stream_parser.cc
+++ b/media/formats/mp2t/mp2t_stream_parser.cc
@@ -427,9 +427,9 @@
 bool Mp2tStreamParser::ShouldForceEncryptedParser() {
   // If we expect to handle encrypted data later in the stream, then force the
   // use of the encrypted parser variant so that the initial configuration
-  // reflects the intended encryption scheme (even if the initial segment itself
+  // reflects the intended encryption mode (even if the initial segment itself
   // is not encrypted).
-  return initial_scheme_.is_encrypted();
+  return initial_encryption_mode_ != EncryptionMode::kUnencrypted;
 }
 
 std::unique_ptr<EsParser> Mp2tStreamParser::CreateEncryptedH264Parser(
@@ -829,9 +829,10 @@
 #if BUILDFLAG(ENABLE_HLS_SAMPLE_AES)
 std::unique_ptr<PidState> Mp2tStreamParser::MakeCatPidState() {
   std::unique_ptr<TsSection> cat_section_parser(new TsSectionCat(
-      base::Bind(&Mp2tStreamParser::RegisterCencPids, base::Unretained(this)),
-      base::Bind(&Mp2tStreamParser::RegisterEncryptionScheme,
-                 base::Unretained(this))));
+      base::BindRepeating(&Mp2tStreamParser::RegisterCencPids,
+                          base::Unretained(this)),
+      base::BindRepeating(&Mp2tStreamParser::RegisterEncryptionMode,
+                          base::Unretained(this))));
   std::unique_ptr<PidState> cat_pid_state(new PidState(
       TsSection::kPidCat, PidState::kPidCat, std::move(cat_section_parser)));
   cat_pid_state->Enable();
@@ -880,11 +881,10 @@
   }
 }
 
-void Mp2tStreamParser::RegisterEncryptionScheme(
-    const EncryptionScheme& scheme) {
+void Mp2tStreamParser::RegisterEncryptionMode(EncryptionMode mode) {
   // We only need to record this for the initial decoder config.
   if (!is_initialized_) {
-    initial_scheme_ = scheme;
+    initial_encryption_mode_ = mode;
   }
   // Reset the DecryptConfig, so that unless and until a CENC-ECM (containing
   // key id and IV) is seen, media data will be considered unencrypted. This is
@@ -895,16 +895,16 @@
 void Mp2tStreamParser::RegisterNewKeyIdAndIv(const std::string& key_id,
                                              const std::string& iv) {
   if (!iv.empty()) {
-    switch (initial_scheme_.mode()) {
-      case EncryptionScheme::CIPHER_MODE_UNENCRYPTED:
+    switch (initial_encryption_mode_) {
+      case EncryptionMode::kUnencrypted:
         decrypt_config_.reset();
         break;
-      case EncryptionScheme::CIPHER_MODE_AES_CTR:
+      case EncryptionMode::kCenc:
         decrypt_config_ = DecryptConfig::CreateCencConfig(key_id, iv, {});
         break;
-      case EncryptionScheme::CIPHER_MODE_AES_CBC:
-        decrypt_config_ = DecryptConfig::CreateCbcsConfig(
-            key_id, iv, {}, initial_scheme_.pattern());
+      case EncryptionMode::kCbcs:
+        decrypt_config_ =
+            DecryptConfig::CreateCbcsConfig(key_id, iv, {}, base::nullopt);
         break;
     }
   }
diff --git a/media/formats/mp2t/mp2t_stream_parser.h b/media/formats/mp2t/mp2t_stream_parser.h
index 994150c..ffa5ab16a 100644
--- a/media/formats/mp2t/mp2t_stream_parser.h
+++ b/media/formats/mp2t/mp2t_stream_parser.h
@@ -16,7 +16,6 @@
 #include "media/base/audio_decoder_config.h"
 #include "media/base/byte_queue.h"
 #include "media/base/decrypt_config.h"
-#include "media/base/encryption_scheme.h"
 #include "media/base/media_export.h"
 #include "media/base/stream_parser.h"
 #include "media/base/video_decoder_config.h"
@@ -25,7 +24,6 @@
 
 namespace media {
 
-class DecryptConfig;
 class StreamParserBuffer;
 
 namespace mp2t {
@@ -121,10 +119,10 @@
   void RegisterCencPids(int ca_pid, int pssh_pid);
   void UnregisterCencPids();
 
-  // Register a default encryption scheme to be used for decoder configs. This
+  // Register a default encryption mode to be used for decoder configs. This
   // value is only used in the absence of explicit encryption metadata, as might
   // be the case during an unencrypted portion of a live stream.
-  void RegisterEncryptionScheme(const EncryptionScheme& scheme);
+  void RegisterEncryptionMode(EncryptionMode mode);
 
   // Register the new KeyID and IV (parsed from CENC-ECM).
   void RegisterNewKeyIdAndIv(const std::string& key_id, const std::string& iv);
@@ -173,7 +171,7 @@
   TimestampUnroller timestamp_unroller_;
 
 #if BUILDFLAG(ENABLE_HLS_SAMPLE_AES)
-  EncryptionScheme initial_scheme_;
+  EncryptionMode initial_encryption_mode_ = EncryptionMode::kUnencrypted;
 
   // TODO(jrummell): Rather than store the key_id and iv in a DecryptConfig,
   // provide a better way to access the last values seen in a ECM packet.
diff --git a/media/formats/mp2t/ts_section_cat.cc b/media/formats/mp2t/ts_section_cat.cc
index 3513ec6..a9239fbd 100644
--- a/media/formats/mp2t/ts_section_cat.cc
+++ b/media/formats/mp2t/ts_section_cat.cc
@@ -8,7 +8,6 @@
 
 #include "base/logging.h"
 #include "media/base/bit_reader.h"
-#include "media/base/encryption_scheme.h"
 #include "media/formats/mp2t/descriptors.h"
 #include "media/formats/mp2t/mp2t_common.h"
 
@@ -17,9 +16,9 @@
 
 TsSectionCat::TsSectionCat(
     const RegisterCencPidsCb& register_cenc_ids_cb,
-    const RegisterEncryptionSchemeCb& register_encryption_scheme_cb)
+    const RegisterEncryptionModeCb& register_encryption_mode_cb)
     : register_cenc_ids_cb_(register_cenc_ids_cb),
-      register_encryption_scheme_cb_(register_encryption_scheme_cb),
+      register_encryption_mode_cb_(register_encryption_mode_cb),
       version_number_(-1) {}
 
 TsSectionCat::~TsSectionCat() {}
@@ -59,9 +58,9 @@
 
   Descriptors descriptors;
   int ca_pid, pssh_pid;
-  EncryptionScheme scheme;
+  EncryptionMode mode;
   RCHECK(descriptors.Read(bit_reader, section_length - 4));
-  RCHECK(descriptors.HasCADescriptorCenc(&ca_pid, &pssh_pid, &scheme));
+  RCHECK(descriptors.HasCADescriptorCenc(&ca_pid, &pssh_pid, &mode));
   int crc32;
   RCHECK(bit_reader->ReadBits(32, &crc32));
 
@@ -77,7 +76,7 @@
 
   // Can now register the PIDs and scheme.
   register_cenc_ids_cb_.Run(ca_pid, pssh_pid);
-  register_encryption_scheme_cb_.Run(scheme);
+  register_encryption_mode_cb_.Run(mode);
 
   version_number_ = version_number;
 
diff --git a/media/formats/mp2t/ts_section_cat.h b/media/formats/mp2t/ts_section_cat.h
index 27922de..637aec8 100644
--- a/media/formats/mp2t/ts_section_cat.h
+++ b/media/formats/mp2t/ts_section_cat.h
@@ -7,23 +7,21 @@
 
 #include "base/callback.h"
 #include "base/macros.h"
+#include "media/base/decrypt_config.h"
 #include "media/formats/mp2t/ts_section_psi.h"
 
 namespace media {
-
-class EncryptionScheme;
-
 namespace mp2t {
 
 class TsSectionCat : public TsSectionPsi {
  public:
   // RegisterCencPidsCb::Run(int ca_pid, int pssh_pid);
-  using RegisterCencPidsCb = base::Callback<void(int, int)>;
-  // RegisterEncryptionScheme::Run(const EncryptionScheme& scheme);
-  using RegisterEncryptionSchemeCb =
-      base::Callback<void(const EncryptionScheme&)>;
+  using RegisterCencPidsCb = base::RepeatingCallback<void(int, int)>;
+  // RegisterEncryptionMode::Run(EncryptionMode mode);
+  using RegisterEncryptionModeCb =
+      base::RepeatingCallback<void(EncryptionMode)>;
   TsSectionCat(const RegisterCencPidsCb& register_cenc_ids_cb,
-               const RegisterEncryptionSchemeCb& register_encryption_scheme_cb);
+               const RegisterEncryptionModeCb& register_encryption_mode_cb);
   ~TsSectionCat() override;
 
   // TsSectionPsi implementation.
@@ -32,7 +30,7 @@
 
  private:
   RegisterCencPidsCb register_cenc_ids_cb_;
-  RegisterEncryptionSchemeCb register_encryption_scheme_cb_;
+  RegisterEncryptionModeCb register_encryption_mode_cb_;
 
   // Parameters from the CAT.
   int version_number_;
diff --git a/media/gpu/android/avda_picture_buffer_manager.cc b/media/gpu/android/avda_picture_buffer_manager.cc
index 692fbe5..80ff9c4a 100644
--- a/media/gpu/android/avda_picture_buffer_manager.cc
+++ b/media/gpu/android/avda_picture_buffer_manager.cc
@@ -63,8 +63,8 @@
                                                1,  // depth
                                                0,  // border
                                                GL_RGBA, GL_UNSIGNED_BYTE);
-    texture_owner_ = TextureOwner::Create(std::move(texture),
-                                          TextureOwner::SecureMode::kInsecure);
+    texture_owner_ = TextureOwner::Create(
+        std::move(texture), TextureOwner::Mode::kSurfaceTextureInsecure);
     if (!texture_owner_)
       return false;
 
diff --git a/media/gpu/android/image_reader_gl_owner.cc b/media/gpu/android/image_reader_gl_owner.cc
index 5bb536c0..abc0418 100644
--- a/media/gpu/android/image_reader_gl_owner.cc
+++ b/media/gpu/android/image_reader_gl_owner.cc
@@ -76,7 +76,7 @@
 
 ImageReaderGLOwner::ImageReaderGLOwner(
     std::unique_ptr<gpu::gles2::AbstractTexture> texture,
-    SecureMode secure_mode)
+    Mode mode)
     : TextureOwner(std::move(texture)),
       current_image_(nullptr),
       loader_(base::android::AndroidImageReader::GetInstance()),
@@ -90,13 +90,13 @@
   // are/maybe overriden by the producer sending buffers to this imageReader's
   // Surface.
   int32_t width = 1, height = 1, max_images = 4;
-  AIMAGE_FORMATS format = secure_mode == SecureMode::kSecure
+  AIMAGE_FORMATS format = mode == Mode::kAImageReaderSecure
                               ? AIMAGE_FORMAT_PRIVATE
                               : AIMAGE_FORMAT_YUV_420_888;
   AImageReader* reader = nullptr;
   // The usage flag below should be used when the buffer will be read from by
   // the GPU as a texture.
-  const uint64_t usage = secure_mode == SecureMode::kSecure
+  const uint64_t usage = mode == Mode::kAImageReaderSecure
                              ? AHARDWAREBUFFER_USAGE_PROTECTED_CONTENT
                              : AHARDWAREBUFFER_USAGE_GPU_SAMPLED_IMAGE;
 
diff --git a/media/gpu/android/image_reader_gl_owner.h b/media/gpu/android/image_reader_gl_owner.h
index 48b4c978..d52f73f 100644
--- a/media/gpu/android/image_reader_gl_owner.h
+++ b/media/gpu/android/image_reader_gl_owner.h
@@ -55,7 +55,7 @@
   class ScopedHardwareBufferImpl;
 
   ImageReaderGLOwner(std::unique_ptr<gpu::gles2::AbstractTexture> texture,
-                     SecureMode secure_mode);
+                     Mode secure_mode);
   ~ImageReaderGLOwner() override;
 
   // Deletes the current image if it has no pending refs. Returns false on
diff --git a/media/gpu/android/image_reader_gl_owner_unittest.cc b/media/gpu/android/image_reader_gl_owner_unittest.cc
index fe581b3..791059c 100644
--- a/media/gpu/android/image_reader_gl_owner_unittest.cc
+++ b/media/gpu/android/image_reader_gl_owner_unittest.cc
@@ -52,8 +52,8 @@
     image_reader_ = TextureOwner::Create(std::move(texture), SecureMode());
   }
 
-  virtual TextureOwner::SecureMode SecureMode() {
-    return TextureOwner::SecureMode::kInsecure;
+  virtual TextureOwner::Mode SecureMode() {
+    return TextureOwner::Mode::kAImageReaderInsecure;
   }
 
   void TearDown() override {
@@ -145,8 +145,8 @@
 
 class ImageReaderGLOwnerSecureTest : public ImageReaderGLOwnerTest {
  public:
-  TextureOwner::SecureMode SecureMode() final {
-    return TextureOwner::SecureMode::kSecure;
+  TextureOwner::Mode SecureMode() final {
+    return TextureOwner::Mode::kAImageReaderSecure;
   }
 };
 
diff --git a/media/gpu/android/surface_texture_gl_owner_unittest.cc b/media/gpu/android/surface_texture_gl_owner_unittest.cc
index d9e790f..3d42458 100644
--- a/media/gpu/android/surface_texture_gl_owner_unittest.cc
+++ b/media/gpu/android/surface_texture_gl_owner_unittest.cc
@@ -51,7 +51,7 @@
         std::make_unique<MockAbstractTexture>(texture_id_);
     abstract_texture_ = texture->AsWeakPtr();
     surface_texture_ = SurfaceTextureGLOwner::Create(
-        std::move(texture), TextureOwner::SecureMode::kInsecure);
+        std::move(texture), TextureOwner::Mode::kSurfaceTextureInsecure);
     texture_id_ = surface_texture_->GetTextureId();
     EXPECT_TRUE(abstract_texture_);
   }
diff --git a/media/gpu/android/texture_owner.cc b/media/gpu/android/texture_owner.cc
index f3f7e815..bf2f4ee8 100644
--- a/media/gpu/android/texture_owner.cc
+++ b/media/gpu/android/texture_owner.cc
@@ -4,13 +4,11 @@
 
 #include "media/gpu/android/texture_owner.h"
 
-#include "base/android/android_image_reader_compat.h"
 #include "base/bind.h"
 #include "base/feature_list.h"
 #include "base/threading/thread_task_runner_handle.h"
 #include "gpu/command_buffer/service/abstract_texture.h"
 #include "gpu/command_buffer/service/decoder_context.h"
-#include "media/base/media_switches.h"
 #include "media/gpu/android/image_reader_gl_owner.h"
 #include "media/gpu/android/surface_texture_gl_owner.h"
 #include "ui/gl/scoped_binders.h"
@@ -37,23 +35,24 @@
 // static
 scoped_refptr<TextureOwner> TextureOwner::Create(
     std::unique_ptr<gpu::gles2::AbstractTexture> texture,
-    SecureMode secure_mode) {
+    Mode mode) {
   // Set the parameters on the texture.
   texture->SetParameteri(GL_TEXTURE_MAG_FILTER, GL_LINEAR);
   texture->SetParameteri(GL_TEXTURE_MIN_FILTER, GL_LINEAR);
   texture->SetParameteri(GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
   texture->SetParameteri(GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
 
-  // If AImageReader is supported and is enabled by media flag, use it.
-  if (base::android::AndroidImageReader::GetInstance().IsSupported() &&
-      base::FeatureList::IsEnabled(media::kAImageReaderVideoOutput)) {
-    return new ImageReaderGLOwner(std::move(texture), secure_mode);
+  switch (mode) {
+    case Mode::kAImageReaderSecure:
+      return new ImageReaderGLOwner(std::move(texture), mode);
+    case Mode::kAImageReaderInsecure:
+      return new ImageReaderGLOwner(std::move(texture), mode);
+    case Mode::kSurfaceTextureInsecure:
+      return new SurfaceTextureGLOwner(std::move(texture));
   }
 
-  // If not, fall back to legacy path.
-  DCHECK_EQ(secure_mode, SecureMode::kInsecure)
-      << "Can not use secure mode with SurfaceTexture";
-  return new SurfaceTextureGLOwner(std::move(texture));
+  NOTREACHED();
+  return nullptr;
 }
 
 // static
diff --git a/media/gpu/android/texture_owner.h b/media/gpu/android/texture_owner.h
index c2619cc..329ccd1 100644
--- a/media/gpu/android/texture_owner.h
+++ b/media/gpu/android/texture_owner.h
@@ -45,12 +45,16 @@
   // new TextureOwner attached to it. Returns null on failure.
   // |texture| should be either from CreateAbstractTexture() or a mock.  The
   // corresponding GL context must be current.
-  // SecureMode indicates whether the video textures created using this owner
-  // should be hardware protected.
-  enum class SecureMode { kSecure, kInsecure };
+  // Mode indicates which framework API to use and whether the video textures
+  // created using this owner should be hardware protected.
+  enum class Mode {
+    kAImageReaderSecure,
+    kAImageReaderInsecure,
+    kSurfaceTextureInsecure
+  };
   static scoped_refptr<TextureOwner> Create(
       std::unique_ptr<gpu::gles2::AbstractTexture> texture,
-      SecureMode secure_mode);
+      Mode mode);
 
   // Create a texture that's appropriate for a TextureOwner.
   static std::unique_ptr<gpu::gles2::AbstractTexture> CreateTexture(
diff --git a/media/gpu/android/video_frame_factory_impl.cc b/media/gpu/android/video_frame_factory_impl.cc
index 6a7628e..60bccf1 100644
--- a/media/gpu/android/video_frame_factory_impl.cc
+++ b/media/gpu/android/video_frame_factory_impl.cc
@@ -4,6 +4,7 @@
 
 #include "media/gpu/android/video_frame_factory_impl.h"
 
+#include "base/android/android_image_reader_compat.h"
 #include "base/bind.h"
 #include "base/bind_helpers.h"
 #include "base/callback.h"
@@ -18,6 +19,7 @@
 #include "gpu/ipc/service/command_buffer_stub.h"
 #include "gpu/ipc/service/gpu_channel.h"
 #include "media/base/bind_to_current_loop.h"
+#include "media/base/media_switches.h"
 #include "media/base/video_frame.h"
 #include "media/gpu/android/codec_image.h"
 #include "media/gpu/android/codec_image_group.h"
@@ -35,6 +37,30 @@
   return stub && stub->decoder_context()->MakeCurrent();
 }
 
+TextureOwner::Mode GetTextureOwnerMode(
+    VideoFrameFactory::OverlayMode overlay_mode) {
+  const bool a_image_reader_supported =
+      base::android::AndroidImageReader::GetInstance().IsSupported();
+
+  switch (overlay_mode) {
+    case VideoFrameFactory::OverlayMode::kDontRequestPromotionHints:
+    case VideoFrameFactory::OverlayMode::kRequestPromotionHints:
+      return a_image_reader_supported && base::FeatureList::IsEnabled(
+                                             media::kAImageReaderVideoOutput)
+                 ? TextureOwner::Mode::kAImageReaderInsecure
+                 : TextureOwner::Mode::kSurfaceTextureInsecure;
+    case VideoFrameFactory::OverlayMode::kSurfaceControlSecure:
+      DCHECK(a_image_reader_supported);
+      return TextureOwner::Mode::kAImageReaderSecure;
+    case VideoFrameFactory::OverlayMode::kSurfaceControlInsecure:
+      DCHECK(a_image_reader_supported);
+      return TextureOwner::Mode::kAImageReaderInsecure;
+  }
+
+  NOTREACHED();
+  return TextureOwner::Mode::kSurfaceTextureInsecure;
+}
+
 }  // namespace
 
 using gpu::gles2::AbstractTexture;
@@ -143,12 +169,9 @@
 
   decoder_helper_ = GLES2DecoderHelper::Create(stub_->decoder_context());
 
-  auto secure_mode =
-      overlay_mode_ == VideoFrameFactory::OverlayMode::kSurfaceControlSecure
-          ? TextureOwner::SecureMode::kSecure
-          : TextureOwner::SecureMode::kInsecure;
   return TextureOwner::Create(
-      TextureOwner::CreateTexture(stub_->decoder_context()), secure_mode);
+      TextureOwner::CreateTexture(stub_->decoder_context()),
+      GetTextureOwnerMode(overlay_mode_));
 }
 
 void GpuVideoFrameFactory::CreateVideoFrame(
diff --git a/media/gpu/ipc/client/gpu_video_decode_accelerator_host.cc b/media/gpu/ipc/client/gpu_video_decode_accelerator_host.cc
index 7da45b4..4f2cf300 100644
--- a/media/gpu/ipc/client/gpu_video_decode_accelerator_host.cc
+++ b/media/gpu/ipc/client/gpu_video_decode_accelerator_host.cc
@@ -262,6 +262,7 @@
   Picture picture(params.picture_buffer_id, params.bitstream_buffer_id,
                   params.visible_rect, params.color_space,
                   params.allow_overlay);
+  picture.set_read_lock_fences_enabled(params.read_lock_fences_enabled);
   picture.set_size_changed(params.size_changed);
   picture.set_texture_owner(params.surface_texture);
   picture.set_wants_promotion_hint(params.wants_promotion_hint);
diff --git a/media/gpu/ipc/common/media_messages.h b/media/gpu/ipc/common/media_messages.h
index 5ef9243..08e7216 100644
--- a/media/gpu/ipc/common/media_messages.h
+++ b/media/gpu/ipc/common/media_messages.h
@@ -27,6 +27,7 @@
   IPC_STRUCT_MEMBER(gfx::Rect, visible_rect)
   IPC_STRUCT_MEMBER(gfx::ColorSpace, color_space)
   IPC_STRUCT_MEMBER(bool, allow_overlay)
+  IPC_STRUCT_MEMBER(bool, read_lock_fences_enabled)
   IPC_STRUCT_MEMBER(bool, size_changed)
   IPC_STRUCT_MEMBER(bool, surface_texture)
   IPC_STRUCT_MEMBER(bool, wants_promotion_hint)
diff --git a/media/gpu/ipc/service/gpu_video_decode_accelerator.cc b/media/gpu/ipc/service/gpu_video_decode_accelerator.cc
index ae6334d2..206c9ec5 100644
--- a/media/gpu/ipc/service/gpu_video_decode_accelerator.cc
+++ b/media/gpu/ipc/service/gpu_video_decode_accelerator.cc
@@ -288,6 +288,7 @@
   params.visible_rect = picture.visible_rect();
   params.color_space = picture.color_space();
   params.allow_overlay = picture.allow_overlay();
+  params.read_lock_fences_enabled = picture.read_lock_fences_enabled();
   params.size_changed = picture.size_changed();
   params.surface_texture = picture.texture_owner();
   params.wants_promotion_hint = picture.wants_promotion_hint();
diff --git a/media/gpu/ipc/service/picture_buffer_manager.cc b/media/gpu/ipc/service/picture_buffer_manager.cc
index 9cd5b1e1..b005386 100644
--- a/media/gpu/ipc/service/picture_buffer_manager.cc
+++ b/media/gpu/ipc/service/picture_buffer_manager.cc
@@ -220,6 +220,10 @@
 
     if (picture.allow_overlay())
       frame->metadata()->SetBoolean(VideoFrameMetadata::ALLOW_OVERLAY, true);
+    if (picture.read_lock_fences_enabled()) {
+      frame->metadata()->SetBoolean(
+          VideoFrameMetadata::READ_LOCK_FENCES_ENABLED, true);
+    }
 
     // TODO(sandersd): Provide an API for VDAs to control this.
     frame->metadata()->SetBoolean(VideoFrameMetadata::POWER_EFFICIENT, true);
diff --git a/media/gpu/test/image_processor/image_processor_client.cc b/media/gpu/test/image_processor/image_processor_client.cc
index a0b2e9a..bb3e2f5 100644
--- a/media/gpu/test/image_processor/image_processor_client.cc
+++ b/media/gpu/test/image_processor/image_processor_client.cc
@@ -4,6 +4,7 @@
 
 #include "media/gpu/test/image_processor/image_processor_client.h"
 
+#include <functional>
 #include <utility>
 
 #include "base/bind.h"
@@ -106,14 +107,13 @@
   DCHECK_CALLED_ON_VALID_THREAD(test_main_thread_checker_);
   base::WaitableEvent done(base::WaitableEvent::ResetPolicy::AUTOMATIC,
                            base::WaitableEvent::InitialState::NOT_SIGNALED);
-  // base::Unretained(this) and base::ConstRef() are safe here because |this|,
+  // base::Unretained(this) and std::cref() are safe here because |this|,
   // |input_config| and |output_config| must outlive because this task is
   // blocking.
   image_processor_client_thread_.task_runner()->PostTask(
-      FROM_HERE,
-      base::BindOnce(&ImageProcessorClient::CreateImageProcessorTask,
-                     base::Unretained(this), base::ConstRef(input_config),
-                     base::ConstRef(output_config), num_buffers, &done));
+      FROM_HERE, base::BindOnce(&ImageProcessorClient::CreateImageProcessorTask,
+                                base::Unretained(this), std::cref(input_config),
+                                std::cref(output_config), num_buffers, &done));
   done.Wait();
   if (!image_processor_) {
     LOG(ERROR) << "Failed to create ImageProcessor";
@@ -288,7 +288,7 @@
 void ImageProcessorClient::ProcessTask(scoped_refptr<VideoFrame> input_frame,
                                        scoped_refptr<VideoFrame> output_frame) {
   DCHECK_CALLED_ON_VALID_THREAD(image_processor_client_thread_checker_);
-  // base::Unretained(this) and base::ConstRef() for FrameReadyCB is safe here
+  // base::Unretained(this) and std::cref() for FrameReadyCB is safe here
   // because the callback is executed on |image_processor_client_thread_| which
   // is owned by this class.
   image_processor_->Process(std::move(input_frame), std::move(output_frame),
diff --git a/media/gpu/vaapi/BUILD.gn b/media/gpu/vaapi/BUILD.gn
index a6e7cae..0d0a626 100644
--- a/media/gpu/vaapi/BUILD.gn
+++ b/media/gpu/vaapi/BUILD.gn
@@ -144,6 +144,7 @@
     "//base/test:test_support",
     "//media:test_support",
     "//testing/gtest",
+    "//third_party/libyuv:libyuv",
     "//ui/gfx/geometry",
   ]
 }
diff --git a/media/gpu/vaapi/vaapi_jpeg_decoder_unittest.cc b/media/gpu/vaapi/vaapi_jpeg_decoder_unittest.cc
index 7b39e7a..b5f1348 100644
--- a/media/gpu/vaapi/vaapi_jpeg_decoder_unittest.cc
+++ b/media/gpu/vaapi/vaapi_jpeg_decoder_unittest.cc
@@ -2,6 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+#include <stddef.h>
 #include <stdint.h>
 
 #include <memory>
@@ -20,25 +21,36 @@
 #include "base/files/file_path.h"
 #include "base/files/file_util.h"
 #include "base/logging.h"
-#include "base/md5.h"
-#include "base/strings/string_piece.h"
+#include "base/numerics/safe_conversions.h"
 #include "base/synchronization/lock.h"
 #include "base/test/gtest_util.h"
 #include "base/thread_annotations.h"
 #include "media/base/test_data_util.h"
+#include "media/base/video_types.h"
+#include "media/filters/jpeg_parser.h"
 #include "media/gpu/vaapi/vaapi_jpeg_decoder.h"
 #include "media/gpu/vaapi/vaapi_utils.h"
 #include "media/gpu/vaapi/vaapi_wrapper.h"
+#include "third_party/libyuv/include/libyuv.h"
 #include "ui/gfx/geometry/size.h"
 
 namespace media {
 namespace {
 
-// This file is supported by the VAAPI, so define expectations on the decode
-// result.
-constexpr const char* kSupportedFilename = "pixel-1280x720.jpg";
-constexpr const char* kExpectedMd5SumI420 = "6e9e1716073c9a9a1282e3f0e0dab743";
-constexpr const char* kExpectedMd5SumYUYV = "ff313a6aedbc4e157561e5c2d5c2e079";
+constexpr const char* kYuv422Filename = "pixel-1280x720.jpg";
+constexpr const char* kYuv420Filename = "pixel-1280x720-yuv420.jpg";
+
+struct TestParam {
+  const char* filename;
+};
+
+const TestParam kTestCases[] = {
+    {kYuv422Filename},
+    {kYuv420Filename},
+};
+
+// Any number above 99.5% should do, experimentally we like a wee higher.
+constexpr double kMinSsim = 0.997;
 
 // This file is not supported by the VAAPI, so we don't define expectations on
 // the decode result.
@@ -50,9 +62,120 @@
     .bits_per_pixel = 12,
 };
 
+// Compares the result of sw decoding |encoded_image| with |decoded_image| using
+// SSIM. Returns true if all conversions work and SSIM is above a given
+// threshold (kMinSsim), or false otherwise.
+bool CompareImages(base::span<const uint8_t> encoded_image,
+                   const ScopedVAImage* decoded_image) {
+  JpegParseResult parse_result;
+  const bool result = ParseJpegPicture(encoded_image.data(),
+                                       encoded_image.size(), &parse_result);
+  if (!result)
+    return false;
+
+  const uint16_t width = parse_result.frame_header.visible_width;
+  const uint16_t height = parse_result.frame_header.visible_height;
+
+  if (width != decoded_image->image()->width ||
+      height != decoded_image->image()->height) {
+    DLOG(ERROR) << "Wrong expected decoded JPEG size, " << width << "x"
+                << height << " versus VaAPI provided "
+                << decoded_image->image()->width << "x"
+                << decoded_image->image()->height;
+    return false;
+  }
+  const uint16_t even_width = (width + 1) / 2;
+  const uint16_t even_height = (height + 1) / 2;
+
+  auto ref_y = std::make_unique<uint8_t[]>(width * height);
+  auto ref_u = std::make_unique<uint8_t[]>(even_width * even_height);
+  auto ref_v = std::make_unique<uint8_t[]>(even_width * even_height);
+
+  const int conversion_result = libyuv::ConvertToI420(
+      encoded_image.data(), encoded_image.size(), ref_y.get(), width,
+      ref_u.get(), even_width, ref_v.get(), even_width, 0, 0, width, height,
+      width, height, libyuv::kRotate0, libyuv::FOURCC_MJPG);
+  if (conversion_result != 0) {
+    DLOG(ERROR) << "libyuv conversion error";
+    return false;
+  }
+
+  const uint32_t va_fourcc = decoded_image->image()->format.fourcc;
+  if (!(va_fourcc == VA_FOURCC_I420 || va_fourcc == VA_FOURCC_YUY2 ||
+        va_fourcc == VA_FOURCC('Y', 'U', 'Y', 'V'))) {
+    DLOG(ERROR) << "Not supported FourCC: " << FourccToString(va_fourcc);
+    return false;
+  }
+  const uint32_t libyuv_fourcc =
+      (va_fourcc == VA_FOURCC_I420) ? libyuv::FOURCC_I420 : libyuv::FOURCC_YUY2;
+
+  if (libyuv_fourcc == libyuv::FOURCC_I420) {
+    const auto* decoded_data_y =
+        static_cast<const uint8_t*>(decoded_image->va_buffer()->data()) +
+        decoded_image->image()->offsets[0];
+    const auto* decoded_data_u =
+        static_cast<const uint8_t*>(decoded_image->va_buffer()->data()) +
+        decoded_image->image()->offsets[1];
+    const auto* decoded_data_v =
+        static_cast<const uint8_t*>(decoded_image->va_buffer()->data()) +
+        decoded_image->image()->offsets[2];
+
+    const double ssim = libyuv::I420Ssim(
+        ref_y.get(), width, ref_u.get(), even_width, ref_v.get(), even_width,
+        decoded_data_y,
+        base::checked_cast<int>(decoded_image->image()->pitches[0]),
+        decoded_data_u,
+        base::checked_cast<int>(decoded_image->image()->pitches[1]),
+        decoded_data_v,
+        base::checked_cast<int>(decoded_image->image()->pitches[2]), width,
+        height);
+    if (ssim < kMinSsim) {
+      DLOG(ERROR) << "Too low SSIM: " << ssim << " < " << kMinSsim;
+      return false;
+    }
+  } else {
+    auto temp_y = std::make_unique<uint8_t[]>(width * height);
+    auto temp_u = std::make_unique<uint8_t[]>(even_width * even_height);
+    auto temp_v = std::make_unique<uint8_t[]>(even_width * even_height);
+
+    // TODO(crbug.com/868400): support other formats/planarities/pitches.
+    constexpr uint32_t kNumPlanesYuv422 = 1u;
+    constexpr uint32_t kBytesPerPixelYuv422 = 2u;
+    if (decoded_image->image()->num_planes != kNumPlanesYuv422 ||
+        decoded_image->image()->pitches[0] != (width * kBytesPerPixelYuv422)) {
+      DLOG(ERROR) << "Too many planes (got "
+                  << decoded_image->image()->num_planes << ", expected "
+                  << kNumPlanesYuv422 << ") or rows not tightly packed (got "
+                  << decoded_image->image()->pitches[0] << ", expected "
+                  << (width * kBytesPerPixelYuv422) << "), aborting test";
+      return false;
+    }
+
+    const int conversion_result = libyuv::ConvertToI420(
+        static_cast<const uint8_t*>(decoded_image->va_buffer()->data()),
+        base::strict_cast<size_t>(decoded_image->image()->data_size),
+        temp_y.get(), width, temp_u.get(), even_width, temp_v.get(), even_width,
+        0, 0, width, height, width, height, libyuv::kRotate0, libyuv_fourcc);
+    if (conversion_result != 0) {
+      DLOG(ERROR) << "libyuv conversion error";
+      return false;
+    }
+
+    const double ssim = libyuv::I420Ssim(
+        ref_y.get(), width, ref_u.get(), even_width, ref_v.get(), even_width,
+        temp_y.get(), width, temp_u.get(), even_width, temp_v.get(), even_width,
+        width, height);
+    if (ssim < kMinSsim) {
+      DLOG(ERROR) << "Too low SSIM: " << ssim << " < " << kMinSsim;
+      return false;
+    }
+  }
+  return true;
+}
+
 }  // namespace
 
-class VaapiJpegDecoderTest : public ::testing::Test {
+class VaapiJpegDecoderTest : public testing::TestWithParam<TestParam> {
  protected:
   VaapiJpegDecoderTest() {
     const base::CommandLine* cmd_line = base::CommandLine::ForCurrentProcess();
@@ -67,7 +190,6 @@
 
   base::FilePath FindTestDataFilePath(const std::string& file_name);
 
-  bool VerifyDecode(base::span<const uint8_t> encoded_image);
   std::unique_ptr<ScopedVAImage> Decode(
       base::span<const uint8_t> encoded_image);
 
@@ -100,31 +222,6 @@
   return GetTestDataFilePath(file_name);
 }
 
-bool VaapiJpegDecoderTest::VerifyDecode(
-    base::span<const uint8_t> encoded_image) {
-  std::unique_ptr<ScopedVAImage> scoped_image = Decode(encoded_image);
-  if (!scoped_image) {
-    LOG(ERROR) << "Cannot get VAImage";
-    return false;
-  }
-
-  const char* expected_md5sum = nullptr;
-  if (scoped_image->image()->format.fourcc == VA_FOURCC_I420) {
-    expected_md5sum = kExpectedMd5SumI420;
-  } else if (scoped_image->image()->format.fourcc == VA_FOURCC_YUY2 ||
-             scoped_image->image()->format.fourcc ==
-                 VA_FOURCC('Y', 'U', 'Y', 'V')) {
-    expected_md5sum = kExpectedMd5SumYUYV;
-  } else {
-    LOG(FATAL) << "Neither I420 nor YUY2 is supported.";
-  }
-
-  const auto* mem = static_cast<char*>(scoped_image->va_buffer()->data());
-  base::StringPiece result(mem, scoped_image->image()->data_size);
-  EXPECT_EQ(expected_md5sum, base::MD5String(result));
-  return true;
-}
-
 std::unique_ptr<ScopedVAImage> VaapiJpegDecoderTest::Decode(
     base::span<const uint8_t> encoded_image) {
   VaapiJpegDecodeStatus status;
@@ -134,13 +231,18 @@
   return scoped_image;
 }
 
-TEST_F(VaapiJpegDecoderTest, DecodeSuccess) {
-  base::FilePath input_file = FindTestDataFilePath(kSupportedFilename);
+TEST_P(VaapiJpegDecoderTest, DecodeSuccess) {
+  base::FilePath input_file = FindTestDataFilePath(GetParam().filename);
   std::string jpeg_data;
   ASSERT_TRUE(base::ReadFileToString(input_file, &jpeg_data))
       << "failed to read input data from " << input_file.value();
-  EXPECT_TRUE(VerifyDecode(base::make_span<const uint8_t>(
-      reinterpret_cast<const uint8_t*>(jpeg_data.data()), jpeg_data.size())));
+
+  const auto encoded_image = base::make_span<const uint8_t>(
+      reinterpret_cast<const uint8_t*>(jpeg_data.data()), jpeg_data.size());
+  std::unique_ptr<ScopedVAImage> scoped_image = Decode(encoded_image);
+  ASSERT_TRUE(scoped_image);
+
+  ASSERT_TRUE(CompareImages(encoded_image, scoped_image.get()));
 }
 
 TEST_F(VaapiJpegDecoderTest, DecodeFail) {
@@ -226,4 +328,6 @@
   EXPECT_FALSE(scoped_buffer->IsValid());
 }
 
+INSTANTIATE_TEST_SUITE_P(, VaapiJpegDecoderTest, testing::ValuesIn(kTestCases));
+
 }  // namespace media
diff --git a/media/gpu/video_encode_accelerator_unittest.cc b/media/gpu/video_encode_accelerator_unittest.cc
index a94368e1..ed33bbb 100644
--- a/media/gpu/video_encode_accelerator_unittest.cc
+++ b/media/gpu/video_encode_accelerator_unittest.cc
@@ -106,6 +106,8 @@
 const double kBitrateTolerance = 0.1;
 // Minimum required FPS throughput for the basic performance test.
 const uint32_t kMinPerfFPS = 30;
+// The frame size for 2160p (UHD 4K) video in pixels.
+const int k2160PSizeInPixels = 3840 * 2160;
 // Minimum (arbitrary) number of frames required to enforce bitrate requirements
 // over. Streams shorter than this may be too short to realistically require
 // an encoder to be able to converge to the requested bitrate over.
@@ -2207,8 +2209,19 @@
 
 void VEAClient::VerifyMinFPS() {
   DCHECK(thread_checker_.CalledOnValidThread());
-  if (test_perf_)
-    EXPECT_GE(frames_per_second(), kMinPerfFPS);
+  if (test_perf_) {
+    if (input_coded_size_.GetArea() >= k2160PSizeInPixels) {
+      // When |input_coded_size_| is 2160p or more, it is expected that the
+      // calculated FPS might be lower than kMinPerfFPS. Log as warning instead
+      // of failing the test in this case.
+      if (frames_per_second() < kMinPerfFPS) {
+        LOG(WARNING) << "Measured FPS: " << frames_per_second()
+                     << " is below min required: " << kMinPerfFPS << " FPS.";
+      }
+    } else {
+      EXPECT_GE(frames_per_second(), kMinPerfFPS);
+    }
+  }
 }
 
 void VEAClient::VerifyStreamProperties() {
diff --git a/media/gpu/vt_video_decode_accelerator_mac.cc b/media/gpu/vt_video_decode_accelerator_mac.cc
index 31759324..68b7b65 100644
--- a/media/gpu/vt_video_decode_accelerator_mac.cc
+++ b/media/gpu/vt_video_decode_accelerator_mac.cc
@@ -1344,9 +1344,21 @@
 
   DVLOG(3) << "PictureReady(picture_id=" << picture_id << ", "
            << "bitstream_id=" << frame.bitstream_id << ")";
-  client_->PictureReady(Picture(picture_id, frame.bitstream_id,
-                                gfx::Rect(frame.image_size), color_space,
-                                true));
+  Picture picture(picture_id, frame.bitstream_id, gfx::Rect(frame.image_size),
+                  color_space, true);
+  // The GLImageIOSurface keeps the IOSurface alive as long as it exists, but
+  // bound textures do not, and they can outlive the GLImageIOSurface if they
+  // are deleted in the command buffer before they are used by the platform GL
+  // implementation. (https://crbug.com/930479#c69)
+  //
+  // A fence is required whenever a GLImage is bound, but we can't know in
+  // advance whether that will happen.
+  //
+  // TODO(sandersd): Can GLImageIOSurface be responsible for fences, so that
+  // we don't need to use them when the image is never bound? Bindings are
+  // typically only created when WebGL is in use.
+  picture.set_read_lock_fences_enabled(true);
+  client_->PictureReady(std::move(picture));
   return true;
 }
 
diff --git a/media/mojo/BUILD.gn b/media/mojo/BUILD.gn
index 19fc789..2f83888 100644
--- a/media/mojo/BUILD.gn
+++ b/media/mojo/BUILD.gn
@@ -36,9 +36,6 @@
         assert(false, "Invalid mojo media service: $service")
       }
     }
-    assert(
-        enable_mojo_renderer || !enable_runtime_media_renderer_selection,
-        "The mojo renderer must be enabled to use runtime media renderer selection.")
 
     if (mojo_media_host == "browser") {
       enable_mojo_media_in_browser_process = true
diff --git a/media/renderers/audio_renderer_impl_unittest.cc b/media/renderers/audio_renderer_impl_unittest.cc
index e637a842..4e78e33 100644
--- a/media/renderers/audio_renderer_impl_unittest.cc
+++ b/media/renderers/audio_renderer_impl_unittest.cc
@@ -48,22 +48,6 @@
   int value;
 };
 
-class MockMediaClient : public MediaClient {
- public:
-  MockMediaClient() = default;
-  ~MockMediaClient() override = default;
-
-  void AddSupportedKeySystems(
-      std::vector<std::unique_ptr<KeySystemProperties>>* key_systems) override {
-  }
-  bool IsKeySystemsUpdateNeeded() override { return false; }
-  bool IsSupportedAudioType(const AudioType& type) override { return true; }
-  bool IsSupportedVideoType(const VideoType& type) override { return true; }
-  bool IsSupportedBitstreamAudioCodec(AudioCodec codec) override {
-    return true;
-  }
-};
-
 }  // namespace
 
 // Constants to specify the type of audio data used.
@@ -212,6 +196,8 @@
   }
 
   void InitializeBitstreamFormat() {
+    EXPECT_CALL(media_client_, IsSupportedBitstreamAudioCodec(_))
+        .WillRepeatedly(Return(true));
     SetMediaClient(&media_client_);
 
     hardware_params_.Reset(AudioParameters::AUDIO_BITSTREAM_EAC3,
diff --git a/media/renderers/default_decoder_factory.cc b/media/renderers/default_decoder_factory.cc
index b5249ca..55a9b09 100644
--- a/media/renderers/default_decoder_factory.cc
+++ b/media/renderers/default_decoder_factory.cc
@@ -125,8 +125,10 @@
 #if BUILDFLAG(ENABLE_DAV1D_DECODER)
   if (base::FeatureList::IsEnabled(kDav1dVideoDecoder))
     video_decoders->push_back(std::make_unique<Dav1dVideoDecoder>(media_log));
+#if BUILDFLAG(ENABLE_AV1_DECODER)
   else
     video_decoders->push_back(std::make_unique<AomVideoDecoder>(media_log));
+#endif
 #elif BUILDFLAG(ENABLE_AV1_DECODER)
   video_decoders->push_back(std::make_unique<AomVideoDecoder>(media_log));
 #endif
diff --git a/media/video/picture.cc b/media/video/picture.cc
index e7ab1db..316ce15e 100644
--- a/media/video/picture.cc
+++ b/media/video/picture.cc
@@ -74,6 +74,7 @@
       visible_rect_(visible_rect),
       color_space_(color_space),
       allow_overlay_(allow_overlay),
+      read_lock_fences_enabled_(false),
       size_changed_(false),
       texture_owner_(false),
       wants_promotion_hint_(false) {}
diff --git a/media/video/picture.h b/media/video/picture.h
index 95ce5fc..b59053b7 100644
--- a/media/video/picture.h
+++ b/media/video/picture.h
@@ -94,10 +94,6 @@
   // Returns the id of the bitstream buffer from which this frame was decoded.
   int32_t bitstream_buffer_id() const { return bitstream_buffer_id_; }
 
-  void set_bitstream_buffer_id(int32_t bitstream_buffer_id) {
-    bitstream_buffer_id_ = bitstream_buffer_id;
-  }
-
   // Returns the color space of the picture.
   const gfx::ColorSpace& color_space() const { return color_space_; }
 
@@ -108,6 +104,12 @@
 
   bool allow_overlay() const { return allow_overlay_; }
 
+  bool read_lock_fences_enabled() const { return read_lock_fences_enabled_; }
+
+  void set_read_lock_fences_enabled(bool read_lock_fences_enabled) {
+    read_lock_fences_enabled_ = read_lock_fences_enabled;
+  }
+
   // Returns true when the VDA has adjusted the resolution of this Picture
   // without requesting new PictureBuffers. GpuVideoDecoder should read this
   // as a signal to update the size of the corresponding PicutreBuffer using
@@ -132,6 +134,7 @@
   gfx::Rect visible_rect_;
   gfx::ColorSpace color_space_;
   bool allow_overlay_;
+  bool read_lock_fences_enabled_;
   bool size_changed_;
   bool texture_owner_;
   bool wants_promotion_hint_;
diff --git a/mojo/public/js/bindings_lite.js b/mojo/public/js/bindings_lite.js
index 0ffa69ad..c7fd658 100644
--- a/mojo/public/js/bindings_lite.js
+++ b/mojo/public/js/bindings_lite.js
@@ -400,13 +400,11 @@
   }
 
   encodeString(offset, value) {
-    if (!mojo.internal.Message.textEncoder)
-      mojo.internal.Message.textEncoder = new TextEncoder('utf-8');
     if (typeof value !== 'string')
       throw new Error('Unxpected non-string value for string field.');
     this.encodeArray(
         {elementType: mojo.internal.Uint8}, offset,
-        mojo.internal.Message.textEncoder.encode(value));
+        mojo.internal.Encoder.stringToUtf8Bytes(value));
   }
 
   encodeOffset(offset, absoluteOffset) {
@@ -556,10 +554,20 @@
     field['type'].$.encode(
         value[tag], unionEncoder, offset + 8, 0, field['nullable']);
   }
+
+  /**
+   * @param {string} value
+   * @return {!Uint8Array}
+   */
+  static stringToUtf8Bytes(value) {
+    if (!mojo.internal.Encoder.textEncoder)
+      mojo.internal.Encoder.textEncoder = new TextEncoder('utf-8');
+    return mojo.internal.Encoder.textEncoder.encode(value);
+  }
 };
 
 /** @type {TextEncoder} */
-mojo.internal.Message.textEncoder = null;
+mojo.internal.Encoder.textEncoder = null;
 
 /**
  * Helps decode incoming messages. Decoders may be created recursively to
@@ -1161,7 +1169,8 @@
     },
     computePayloadSize: function(value, nullable) {
       return mojo.internal.computeTotalArraySize(
-          {elementType: mojo.internal.Uint8}, value);
+          {elementType: mojo.internal.Uint8},
+          mojo.internal.Encoder.stringToUtf8Bytes(value));
     },
     arrayElementSize: nullable => 8,
     isValidObjectKeyType: true,
diff --git a/mojo/public/js/mojo_bindings_resources.grd b/mojo/public/js/mojo_bindings_resources.grd
index 12da9be..deddde08 100644
--- a/mojo/public/js/mojo_bindings_resources.grd
+++ b/mojo/public/js/mojo_bindings_resources.grd
@@ -23,6 +23,21 @@
           use_base_dir="false"
           type="BINDATA"
           compress="gzip" />
+      <include name="IDR_MOJO_BIG_BUFFER_MOJOM_LITE_JS"
+          file="${root_gen_dir}/mojo/public/mojom/base/big_buffer.mojom-lite.js"
+          use_base_dir="false"
+          type="BINDATA"
+          compress="gzip" />
+      <include name="IDR_MOJO_FILE_MOJOM_LITE_JS"
+          file="${root_gen_dir}/mojo/public/mojom/base/file.mojom-lite.js"
+          use_base_dir="false"
+          type="BINDATA"
+          compress="gzip" />
+      <include name="IDR_MOJO_STRING16_MOJOM_LITE_JS"
+          file="${root_gen_dir}/mojo/public/mojom/base/string16.mojom-lite.js"
+          use_base_dir="false"
+          type="BINDATA"
+          compress="gzip" />
       <if expr="is_win or is_macosx or is_linux">
         <include name="IDR_MOJO_TIME_MOJOM_LITE_JS"
             file="${root_gen_dir}/mojo/public/mojom/base/time.mojom-lite.js"
diff --git a/net/BUILD.gn b/net/BUILD.gn
index 1999e70..96c801b 100644
--- a/net/BUILD.gn
+++ b/net/BUILD.gn
@@ -3062,6 +3062,7 @@
       "third_party/quic/core/quic_packet_reader.cc",
       "third_party/quic/core/quic_packet_reader.h",
       "third_party/quic/platform/api/quic_default_proof_providers.h",
+      "third_party/quic/platform/api/quic_system_event_loop.h",
       "third_party/quic/platform/impl/batch_writer/quic_batch_writer_base.cc",
       "third_party/quic/platform/impl/batch_writer/quic_batch_writer_base.h",
       "third_party/quic/platform/impl/batch_writer/quic_batch_writer_buffer.cc",
@@ -3078,6 +3079,7 @@
       "third_party/quic/platform/impl/quic_linux_socket_utils.h",
       "third_party/quic/platform/impl/quic_socket_utils.cc",
       "third_party/quic/platform/impl/quic_socket_utils.h",
+      "third_party/quic/platform/impl/quic_system_event_loop_impl.h",
       "third_party/quic/tools/quic_client.cc",
       "third_party/quic/tools/quic_client.h",
       "third_party/quic/tools/quic_client_epoll_network_helper.cc",
@@ -3205,6 +3207,7 @@
     "third_party/quic/core/qpack/qpack_test_utils.h",
     "third_party/quic/core/quic_trace_visitor.cc",
     "third_party/quic/core/quic_trace_visitor.h",
+    "third_party/quic/platform/api/quic_epoll_test_tools.h",
     "third_party/quic/platform/api/quic_expect_bug.h",
     "third_party/quic/platform/api/quic_mock_log.h",
     "third_party/quic/platform/api/quic_port_utils.h",
@@ -3213,6 +3216,7 @@
     "third_party/quic/platform/api/quic_test_loopback.h",
     "third_party/quic/platform/api/quic_test_mem_slice_vector.h",
     "third_party/quic/platform/api/quic_test_output.h",
+    "third_party/quic/platform/impl/quic_epoll_test_tools_impl.h",
     "third_party/quic/platform/impl/quic_expect_bug_impl.h",
     "third_party/quic/platform/impl/quic_mock_log_impl.h",
     "third_party/quic/platform/impl/quic_port_utils_impl.cc",
@@ -4950,7 +4954,6 @@
     "http/http_network_transaction_unittest.cc",
     "http/http_proxy_client_socket_pool_unittest.cc",
     "http/http_proxy_client_socket_unittest.cc",
-    "http/http_proxy_client_socket_wrapper_unittest.cc",
     "http/http_proxy_connect_job_unittest.cc",
     "http/http_request_headers_unittest.cc",
     "http/http_response_body_drainer_unittest.cc",
@@ -5056,7 +5059,6 @@
     "socket/socks5_client_socket_unittest.cc",
     "socket/socks_client_socket_unittest.cc",
     "socket/socks_connect_job_unittest.cc",
-    "socket/ssl_client_socket_pool_unittest.cc",
     "socket/ssl_client_socket_unittest.cc",
     "socket/ssl_connect_job_unittest.cc",
     "socket/ssl_server_socket_unittest.cc",
@@ -5244,31 +5246,6 @@
     "third_party/quic/platform/api/quic_text_utils_test.cc",
     "third_party/quic/platform/impl/quic_chromium_clock_test.cc",
     "third_party/quic/platform/impl/quic_uint128_impl_unittest.cc",
-    "third_party/quic/quartc/counting_packet_filter.h",
-    "third_party/quic/quartc/quartc_connection_helper.cc",
-    "third_party/quic/quartc/quartc_connection_helper.h",
-    "third_party/quic/quartc/quartc_crypto_helpers.cc",
-    "third_party/quic/quartc/quartc_crypto_helpers.h",
-    "third_party/quic/quartc/quartc_dispatcher.cc",
-    "third_party/quic/quartc/quartc_dispatcher.h",
-    "third_party/quic/quartc/quartc_endpoint.cc",
-    "third_party/quic/quartc/quartc_endpoint.h",
-    "third_party/quic/quartc/quartc_endpoint_test.cc",
-    "third_party/quic/quartc/quartc_factory.cc",
-    "third_party/quic/quartc/quartc_factory.h",
-    "third_party/quic/quartc/quartc_interval_counter.h",
-    "third_party/quic/quartc/quartc_interval_counter_test.cc",
-    "third_party/quic/quartc/quartc_packet_writer.cc",
-    "third_party/quic/quartc/quartc_packet_writer.h",
-    "third_party/quic/quartc/quartc_session.cc",
-    "third_party/quic/quartc/quartc_session.h",
-    "third_party/quic/quartc/quartc_session_test.cc",
-    "third_party/quic/quartc/quartc_stream.cc",
-    "third_party/quic/quartc/quartc_stream.h",
-    "third_party/quic/quartc/quartc_stream_test.cc",
-    "third_party/quic/quartc/simulated_packet_transport.cc",
-    "third_party/quic/quartc/simulated_packet_transport.h",
-    "third_party/quic/quartc/simulated_packet_transport_test.cc",
     "third_party/quic/test_tools/crypto_test_utils_test.cc",
     "third_party/quic/test_tools/mock_quic_time_wait_list_manager.cc",
     "third_party/quic/test_tools/mock_quic_time_wait_list_manager.h",
@@ -5418,6 +5395,38 @@
   ]
   net_unfiltered_sources = []
 
+  # Disable building Quartc tests on iOS as they appear to be flaky there.
+  if (!is_ios) {
+    sources += [
+      "third_party/quic/quartc/counting_packet_filter.h",
+      "third_party/quic/quartc/quartc_connection_helper.cc",
+      "third_party/quic/quartc/quartc_connection_helper.h",
+      "third_party/quic/quartc/quartc_crypto_helpers.cc",
+      "third_party/quic/quartc/quartc_crypto_helpers.h",
+      "third_party/quic/quartc/quartc_dispatcher.cc",
+      "third_party/quic/quartc/quartc_dispatcher.h",
+      "third_party/quic/quartc/quartc_endpoint.cc",
+      "third_party/quic/quartc/quartc_endpoint.h",
+      "third_party/quic/quartc/quartc_endpoint_test.cc",
+      "third_party/quic/quartc/quartc_factory.cc",
+      "third_party/quic/quartc/quartc_factory.h",
+      "third_party/quic/quartc/quartc_fakes.h",
+      "third_party/quic/quartc/quartc_interval_counter.h",
+      "third_party/quic/quartc/quartc_interval_counter_test.cc",
+      "third_party/quic/quartc/quartc_packet_writer.cc",
+      "third_party/quic/quartc/quartc_packet_writer.h",
+      "third_party/quic/quartc/quartc_session.cc",
+      "third_party/quic/quartc/quartc_session.h",
+      "third_party/quic/quartc/quartc_session_test.cc",
+      "third_party/quic/quartc/quartc_stream.cc",
+      "third_party/quic/quartc/quartc_stream.h",
+      "third_party/quic/quartc/quartc_stream_test.cc",
+      "third_party/quic/quartc/simulated_packet_transport.cc",
+      "third_party/quic/quartc/simulated_packet_transport.h",
+      "third_party/quic/quartc/simulated_packet_transport_test.cc",
+    ]
+  }
+
   configs += [ "//build/config:precompiled_headers" ]
   defines = []
 
@@ -5748,7 +5757,6 @@
       "http/http_content_disposition_unittest.cc",
       "http/http_network_transaction_unittest.cc",
       "http/http_proxy_client_socket_pool_unittest.cc",
-      "socket/ssl_client_socket_pool_unittest.cc",
       "spdy/spdy_network_transaction_unittest.cc",
       "spdy/spdy_proxy_client_socket_unittest.cc",
       "url_request/url_request_job_unittest.cc",
diff --git a/net/cert/cert_verify_proc.cc b/net/cert/cert_verify_proc.cc
index 5859525f..3cd0f3d 100644
--- a/net/cert/cert_verify_proc.cc
+++ b/net/cert/cert_verify_proc.cc
@@ -305,25 +305,6 @@
   }
 }
 
-// Comparison functor used for binary searching whether a given HashValue,
-// which MUST be a SHA-256 hash, is contained with an array of SHA-256
-// hashes.
-struct HashToArrayComparator {
-  template <size_t N>
-  bool operator()(const uint8_t(&lhs)[N], const HashValue& rhs) const {
-    static_assert(N == crypto::kSHA256Length,
-                  "Only SHA-256 hashes are supported");
-    return memcmp(lhs, rhs.data(), crypto::kSHA256Length) < 0;
-  }
-
-  template <size_t N>
-  bool operator()(const HashValue& lhs, const uint8_t(&rhs)[N]) const {
-    static_assert(N == crypto::kSHA256Length,
-                  "Only SHA-256 hashes are supported");
-    return memcmp(lhs.data(), rhs, crypto::kSHA256Length) < 0;
-  }
-};
-
 bool AreSHA1IntermediatesAllowed() {
 #if defined(OS_WIN)
   // TODO(rsleevi): Remove this once https://crbug.com/588789 is resolved
@@ -513,6 +494,7 @@
     return ERR_CERT_REVOKED;
   }
 
+  DCHECK(crl_set);
   int rv = VerifyInternal(cert, hostname, ocsp_response, flags, crl_set,
                           additional_trust_anchors, verify_result);
 
@@ -532,13 +514,6 @@
   BestEffortCheckOCSP(ocsp_response, *verify_result->verified_cert,
                       &verify_result->ocsp_result);
 
-  // This check is done after VerifyInternal so that VerifyInternal can fill
-  // in the list of public key hashes.
-  if (IsPublicKeyBlacklisted(verify_result->public_key_hashes)) {
-    verify_result->cert_status |= CERT_STATUS_REVOKED;
-    rv = MapCertStatusToNetError(verify_result->cert_status);
-  }
-
   std::vector<std::string> dns_names, ip_addrs;
   cert->GetSubjectAltName(&dns_names, &ip_addrs);
   if (HasNameConstraintsViolation(verify_result->public_key_hashes,
@@ -659,23 +634,6 @@
   return false;
 }
 
-// static
-bool CertVerifyProc::IsPublicKeyBlacklisted(
-    const HashValueVector& public_key_hashes) {
-// Defines kBlacklistedSPKIs.
-#include "net/cert/cert_verify_proc_blacklist.inc"
-  for (const auto& hash : public_key_hashes) {
-    if (hash.tag() != HASH_VALUE_SHA256)
-      continue;
-    if (std::binary_search(std::begin(kBlacklistedSPKIs),
-                           std::end(kBlacklistedSPKIs), hash,
-                           HashToArrayComparator())) {
-      return true;
-    }
-  }
-  return false;
-}
-
 // CheckNameConstraints verifies that every name in |dns_names| is in one of
 // the domains specified by |domains|.
 static bool CheckNameConstraints(const std::vector<std::string>& dns_names,
diff --git a/net/cert/cert_verify_proc.h b/net/cert/cert_verify_proc.h
index b770c52..0b804c1 100644
--- a/net/cert/cert_verify_proc.h
+++ b/net/cert/cert_verify_proc.h
@@ -74,8 +74,9 @@
   // based revocation checking is always enabled, regardless of this flag, if
   // |crl_set| is given.
   //
-  // |crl_set| points to an optional CRLSet structure which can be used to
-  // avoid revocation checks over the network.
+  // |crl_set|, which is required, points to an CRLSet structure which can be
+  // used to avoid revocation checks over the network.  If you do not have one
+  // handy, use CRLSet::BuiltinCRLSet().
   //
   // |additional_trust_anchors| lists certificates that can be trusted when
   // building a certificate chain, in addition to the anchors known to the
@@ -136,10 +137,6 @@
   // Returns true if |cert| is explicitly blacklisted.
   static bool IsBlacklisted(X509Certificate* cert);
 
-  // IsPublicKeyBlacklisted returns true iff one of |public_key_hashes| (which
-  // are hashes of SubjectPublicKeyInfo structures) is explicitly blocked.
-  static bool IsPublicKeyBlacklisted(const HashValueVector& public_key_hashes);
-
   // HasNameConstraintsViolation returns true iff one of |public_key_hashes|
   // (which are hashes of SubjectPublicKeyInfo structures) has name constraints
   // imposed on it and the names in |dns_names| are not permitted.
diff --git a/net/cert/cert_verify_proc_android_unittest.cc b/net/cert/cert_verify_proc_android_unittest.cc
index 06ebbf4..10a989438 100644
--- a/net/cert/cert_verify_proc_android_unittest.cc
+++ b/net/cert/cert_verify_proc_android_unittest.cc
@@ -10,6 +10,7 @@
 #include "net/cert/cert_net_fetcher.h"
 #include "net/cert/cert_verify_proc_android.h"
 #include "net/cert/cert_verify_result.h"
+#include "net/cert/crl_set.h"
 #include "net/cert/internal/test_helpers.h"
 #include "net/cert/test_root_certs.h"
 #include "net/cert/x509_certificate.h"
@@ -182,8 +183,9 @@
   ASSERT_TRUE(
       CreateCertificateChainFromFiles({"target_one_aia.pem", "i.pem"}, &leaf));
   CertVerifyResult verify_result;
-  EXPECT_EQ(OK, proc->Verify(leaf.get(), "target", std::string(), 0, nullptr,
-                             empty_cert_list_, &verify_result));
+  EXPECT_EQ(OK, proc->Verify(leaf.get(), "target", std::string(), 0,
+                             CRLSet::BuiltinCRLSet().get(), empty_cert_list_,
+                             &verify_result));
 }
 
 // Tests that if the certificate does not contain an AIA URL, no AIA fetch
@@ -196,8 +198,9 @@
   ASSERT_TRUE(ReadTestCert("target_no_aia.pem", &cert));
   CertVerifyResult verify_result;
   EXPECT_EQ(ERR_CERT_AUTHORITY_INVALID,
-            proc->Verify(cert.get(), "target", std::string(), 0, nullptr,
-                         empty_cert_list_, &verify_result));
+            proc->Verify(cert.get(), "target", std::string(), 0,
+                         CRLSet::BuiltinCRLSet().get(), empty_cert_list_,
+                         &verify_result));
 }
 
 // Tests that if a certificate contains one file:// URL and one http:// URL,
@@ -225,8 +228,9 @@
           ByMove(CreateMockRequestFromX509Certificate(OK, intermediate))));
 
   CertVerifyResult verify_result;
-  EXPECT_EQ(OK, proc->Verify(cert.get(), "target", std::string(), 0, nullptr,
-                             empty_cert_list_, &verify_result));
+  EXPECT_EQ(OK, proc->Verify(cert.get(), "target", std::string(), 0,
+                             CRLSet::BuiltinCRLSet().get(), empty_cert_list_,
+                             &verify_result));
 }
 
 // Tests that if an AIA request returns the wrong intermediate, certificate
@@ -247,8 +251,9 @@
 
   CertVerifyResult verify_result;
   EXPECT_EQ(ERR_CERT_AUTHORITY_INVALID,
-            proc->Verify(cert.get(), "target", std::string(), 0, nullptr,
-                         empty_cert_list_, &verify_result));
+            proc->Verify(cert.get(), "target", std::string(), 0,
+                         CRLSet::BuiltinCRLSet().get(), empty_cert_list_,
+                         &verify_result));
 }
 
 // Tests that if an AIA request returns an error, certificate verification
@@ -266,8 +271,9 @@
 
   CertVerifyResult verify_result;
   EXPECT_EQ(ERR_CERT_AUTHORITY_INVALID,
-            proc->Verify(cert.get(), "target", std::string(), 0, nullptr,
-                         empty_cert_list_, &verify_result));
+            proc->Verify(cert.get(), "target", std::string(), 0,
+                         CRLSet::BuiltinCRLSet().get(), empty_cert_list_,
+                         &verify_result));
 }
 
 // Tests that if an AIA request returns an unparseable cert, certificate
@@ -285,8 +291,9 @@
 
   CertVerifyResult verify_result;
   EXPECT_EQ(ERR_CERT_AUTHORITY_INVALID,
-            proc->Verify(cert.get(), "target", std::string(), 0, nullptr,
-                         empty_cert_list_, &verify_result));
+            proc->Verify(cert.get(), "target", std::string(), 0,
+                         CRLSet::BuiltinCRLSet().get(), empty_cert_list_,
+                         &verify_result));
 }
 
 // Tests that if a certificate has two HTTP AIA URLs, they are both fetched. If
@@ -317,8 +324,9 @@
           ByMove(CreateMockRequestFromX509Certificate(OK, intermediate))));
 
   CertVerifyResult verify_result;
-  EXPECT_EQ(OK, proc->Verify(cert.get(), "target", std::string(), 0, nullptr,
-                             empty_cert_list_, &verify_result));
+  EXPECT_EQ(OK, proc->Verify(cert.get(), "target", std::string(), 0,
+                             CRLSet::BuiltinCRLSet().get(), empty_cert_list_,
+                             &verify_result));
 }
 
 // Tests that if an intermediate is fetched via AIA, and the intermediate itself
@@ -350,8 +358,9 @@
   // This chain results in an AUTHORITY_INVALID root because |root_| is not
   // trusted.
   EXPECT_EQ(ERR_CERT_AUTHORITY_INVALID,
-            proc->Verify(cert.get(), "target", std::string(), 0, nullptr,
-                         empty_cert_list_, &verify_result));
+            proc->Verify(cert.get(), "target", std::string(), 0,
+                         CRLSet::BuiltinCRLSet().get(), empty_cert_list_,
+                         &verify_result));
 }
 
 // Tests that if a certificate contains six AIA URLs, only the first five are
@@ -372,8 +381,9 @@
 
   CertVerifyResult verify_result;
   EXPECT_EQ(ERR_CERT_AUTHORITY_INVALID,
-            proc->Verify(cert.get(), "target", std::string(), 0, nullptr,
-                         empty_cert_list_, &verify_result));
+            proc->Verify(cert.get(), "target", std::string(), 0,
+                         CRLSet::BuiltinCRLSet().get(), empty_cert_list_,
+                         &verify_result));
 }
 
 // Tests that if the supplied chain contains an intermediate with an AIA URL,
@@ -398,8 +408,9 @@
   // This chain results in an AUTHORITY_INVALID root because |root_| is not
   // trusted.
   EXPECT_EQ(ERR_CERT_AUTHORITY_INVALID,
-            proc->Verify(leaf.get(), "target", std::string(), 0, nullptr,
-                         empty_cert_list_, &verify_result));
+            proc->Verify(leaf.get(), "target", std::string(), 0,
+                         CRLSet::BuiltinCRLSet().get(), empty_cert_list_,
+                         &verify_result));
 }
 
 }  // namespace net
diff --git a/net/cert/cert_verify_proc_builtin.cc b/net/cert/cert_verify_proc_builtin.cc
index aed93e2..057b47b 100644
--- a/net/cert/cert_verify_proc_builtin.cc
+++ b/net/cert/cert_verify_proc_builtin.cc
@@ -139,24 +139,22 @@
     RevocationPolicy policy =
         ChooseRevocationPolicy(path->certs, &crlset_leaf_coverage_sufficient);
 
-    // Check for revocations using the CRLSet (if available).
-    if (crl_set_) {
-      switch (CheckChainRevocationUsingCRLSet(crl_set_, path->certs,
-                                              &path->errors)) {
-        case CRLSet::Result::REVOKED:
-          return;
-        case CRLSet::Result::GOOD:
-          if (crlset_leaf_coverage_sufficient) {
-            // Weaken the revocation checking requirement as it has been
-            // satisfied. (Don't early-return, since still want to consult
-            // cached OCSP/CRL if available).
-            policy = NoRevocationChecking();
-          }
-          break;
-        case CRLSet::Result::UNKNOWN:
-          // CRLSet was inconclusive.
-          break;
-      }
+    // Check for revocations using the CRLSet.
+    switch (
+        CheckChainRevocationUsingCRLSet(crl_set_, path->certs, &path->errors)) {
+      case CRLSet::Result::REVOKED:
+        return;
+      case CRLSet::Result::GOOD:
+        if (crlset_leaf_coverage_sufficient) {
+          // Weaken the revocation checking requirement as it has been
+          // satisfied. (Don't early-return, since still want to consult
+          // cached OCSP/CRL if available).
+          policy = NoRevocationChecking();
+        }
+        break;
+      case CRLSet::Result::UNKNOWN:
+        // CRLSet was inconclusive.
+        break;
     }
 
     if (policy.check_revocation)
diff --git a/net/cert/cert_verify_proc_mac.cc b/net/cert/cert_verify_proc_mac.cc
index ae80b29..025c759 100644
--- a/net/cert/cert_verify_proc_mac.cc
+++ b/net/cert/cert_verify_proc_mac.cc
@@ -778,9 +778,7 @@
       // solution might be to have CRLSets contain enough knowledge about what
       // the 'desired' path might be, but for the time being, the
       // implementation is kept as 'simple' as it can be.
-      CRLSetResult crl_result = kCRLSetUnknown;
-      if (crl_set)
-        crl_result = CheckRevocationWithCRLSet(temp_chain, crl_set);
+      CRLSetResult crl_result = CheckRevocationWithCRLSet(temp_chain, crl_set);
       bool untrusted = (temp_trust_result != kSecTrustResultUnspecified &&
                         temp_trust_result != kSecTrustResultProceed) ||
                        crl_result == kCRLSetRevoked;
diff --git a/net/cert/cert_verify_proc_mac_unittest.cc b/net/cert/cert_verify_proc_mac_unittest.cc
index 55118fec..5a6044e 100644
--- a/net/cert/cert_verify_proc_mac_unittest.cc
+++ b/net/cert/cert_verify_proc_mac_unittest.cc
@@ -165,9 +165,9 @@
   int flags = 0;
   CertVerifyResult verify_result;
   scoped_refptr<CertVerifyProc> verify_proc = new CertVerifyProcMac;
-  int error = verify_proc->Verify(cert.get(), "gms.hongleong.com.my",
-                                  std::string(), flags, nullptr /* crl_set */,
-                                  CertificateList(), &verify_result);
+  int error = verify_proc->Verify(
+      cert.get(), "gms.hongleong.com.my", std::string(), flags,
+      CRLSet::BuiltinCRLSet().get(), CertificateList(), &verify_result);
 
   ASSERT_EQ(OK, error);
   EXPECT_FALSE(verify_result.has_sha1);
@@ -213,7 +213,8 @@
   CertVerifyResult verify_result;
   scoped_refptr<CertVerifyProc> verify_proc = new CertVerifyProcMac;
   int error = verify_proc->Verify(cert.get(), "127.0.0.1", std::string(), flags,
-                                  NULL, CertificateList(), &verify_result);
+                                  CRLSet::BuiltinCRLSet().get(),
+                                  CertificateList(), &verify_result);
   EXPECT_THAT(error, IsError(ERR_CERT_INVALID));
   EXPECT_TRUE(verify_result.cert_status & CERT_STATUS_INVALID);
 }
diff --git a/net/cert/cert_verify_proc_nss.cc b/net/cert/cert_verify_proc_nss.cc
index 9615b13..2e4bc71 100644
--- a/net/cert/cert_verify_proc_nss.cc
+++ b/net/cert/cert_verify_proc_nss.cc
@@ -770,14 +770,11 @@
   // This second PKIXVerifyCert call could have found a different certification
   // path and one or more of the certificates on this new path, that weren't on
   // the old path, might have been revoked.
-  if (crl_set) {
-    CRLSetResult crl_set_result = CheckRevocationWithCRLSet(
-        cvout[cvout_cert_list_index].value.pointer.chain,
-        cvout[cvout_trust_anchor_index].value.pointer.cert,
-        crl_set);
-    if (crl_set_result == kCRLSetRevoked)
-      return false;
-  }
+  CRLSetResult crl_set_result = CheckRevocationWithCRLSet(
+      cvout[cvout_cert_list_index].value.pointer.chain,
+      cvout[cvout_trust_anchor_index].value.pointer.cert, crl_set);
+  if (crl_set_result == kCRLSetRevoked)
+    return false;
 
   SHA256HashValue fingerprint;
   crypto::SHA256HashString(
@@ -946,27 +943,25 @@
   }
 
   CRLSetResult crl_set_result = kCRLSetUnknown;
-  if (crl_set) {
-    if (status == SECSuccess) {
-      // Reverify the returned chain; NSS should have already called
-      // CheckChainRevocationWithCRLSet prior to returning, but given the
-      // edge cases (self-signed certs that are trusted; cached chains;
-      // unreadable code), this is more about defense in depth than
-      // functional necessity.
-      crl_set_result = CheckRevocationWithCRLSet(
-          cvout[cvout_cert_list_index].value.pointer.chain,
-          cvout[cvout_trust_anchor_index].value.pointer.cert, crl_set);
-      if (crl_set_result == kCRLSetRevoked) {
-        PORT_SetError(SEC_ERROR_REVOKED_CERTIFICATE);
-        status = SECFailure;
-      }
-    } else if (PORT_GetError() == SEC_ERROR_APPLICATION_CALLBACK_ERROR &&
-               check_chain_revocation_args.was_revoked) {
-      // If a CRLSet was supplied, and the error was an application callback
-      // error, then it was directed through the CRLSet code and that
-      // particular chain was revoked.
+  if (status == SECSuccess) {
+    // Reverify the returned chain; NSS should have already called
+    // CheckChainRevocationWithCRLSet prior to returning, but given the
+    // edge cases (self-signed certs that are trusted; cached chains;
+    // unreadable code), this is more about defense in depth than
+    // functional necessity.
+    crl_set_result = CheckRevocationWithCRLSet(
+        cvout[cvout_cert_list_index].value.pointer.chain,
+        cvout[cvout_trust_anchor_index].value.pointer.cert, crl_set);
+    if (crl_set_result == kCRLSetRevoked) {
       PORT_SetError(SEC_ERROR_REVOKED_CERTIFICATE);
+      status = SECFailure;
     }
+  } else if (PORT_GetError() == SEC_ERROR_APPLICATION_CALLBACK_ERROR &&
+             check_chain_revocation_args.was_revoked) {
+    // If a CRLSet was supplied, and the error was an application callback
+    // error, then it was directed through the CRLSet code and that
+    // particular chain was revoked.
+    PORT_SetError(SEC_ERROR_REVOKED_CERTIFICATE);
   }
 
   if (status != SECSuccess) {
diff --git a/net/cert/cert_verify_proc_unittest.cc b/net/cert/cert_verify_proc_unittest.cc
index 4e28792..237ad0e 100644
--- a/net/cert/cert_verify_proc_unittest.cc
+++ b/net/cert/cert_verify_proc_unittest.cc
@@ -840,8 +840,9 @@
 
   CertVerifyResult verify_result;
   int flags = 0;
-  int error = Verify(cert.get(), "policy_test.example", flags,
-                     nullptr /*crl_set*/, CertificateList(), &verify_result);
+  int error =
+      Verify(cert.get(), "policy_test.example", flags,
+             CRLSet::BuiltinCRLSet().get(), CertificateList(), &verify_result);
   if (ScopedTestRootCanTrustTargetCert(verify_proc_type())) {
     EXPECT_THAT(error, IsOk());
     ASSERT_TRUE(verify_result.verified_cert);
@@ -877,8 +878,9 @@
 
   CertVerifyResult verify_result;
   int flags = 0;
-  int error = Verify(cert.get(), "policy_test.example", flags,
-                     nullptr /*crl_set*/, CertificateList(), &verify_result);
+  int error =
+      Verify(cert.get(), "policy_test.example", flags,
+             CRLSet::BuiltinCRLSet().get(), CertificateList(), &verify_result);
   if (ScopedTestRootCanTrustTargetCert(verify_proc_type())) {
     EXPECT_THAT(error, IsOk());
     ASSERT_TRUE(verify_result.verified_cert);
@@ -911,8 +913,9 @@
 
   int flags = 0;
   CertVerifyResult verify_result;
-  int error = Verify(paypal_null_cert.get(), "www.paypal.com", flags, NULL,
-                     CertificateList(), &verify_result);
+  int error =
+      Verify(paypal_null_cert.get(), "www.paypal.com", flags,
+             CRLSet::BuiltinCRLSet().get(), CertificateList(), &verify_result);
 
   if (verify_proc_type() == CERT_VERIFY_PROC_NSS ||
       verify_proc_type() == CERT_VERIFY_PROC_ANDROID) {
@@ -963,8 +966,9 @@
 
   int flags = 0;
   CertVerifyResult verify_result;
-  int error = Verify(cert_with_bad_target.get(), "127.0.0.1", flags, NULL,
-                     CertificateList(), &verify_result);
+  int error =
+      Verify(cert_with_bad_target.get(), "127.0.0.1", flags,
+             CRLSet::BuiltinCRLSet().get(), CertificateList(), &verify_result);
 
   EXPECT_TRUE(verify_result.cert_status & CERT_STATUS_INVALID);
   EXPECT_THAT(error, IsError(ERR_CERT_INVALID));
@@ -998,8 +1002,9 @@
 
   int flags = 0;
   CertVerifyResult verify_result;
-  int error = Verify(cert_with_bad_intermediate.get(), "127.0.0.1", flags, NULL,
-                     CertificateList(), &verify_result);
+  int error =
+      Verify(cert_with_bad_intermediate.get(), "127.0.0.1", flags,
+             CRLSet::BuiltinCRLSet().get(), CertificateList(), &verify_result);
 
   EXPECT_THAT(error, IsOk());
   EXPECT_EQ(0u, verify_result.cert_status);
@@ -1031,8 +1036,9 @@
 
   int flags = 0;
   CertVerifyResult verify_result;
-  int error = Verify(cert.get(), "policy_test.example", flags, NULL,
-                     CertificateList(), &verify_result);
+  int error =
+      Verify(cert.get(), "policy_test.example", flags,
+             CRLSet::BuiltinCRLSet().get(), CertificateList(), &verify_result);
   EXPECT_THAT(error, IsOk());
   EXPECT_EQ(0u, verify_result.cert_status);
 }
@@ -1051,8 +1057,9 @@
 
   int flags = 0;
   CertVerifyResult verify_result;
-  int error = Verify(cert.get(), "127.0.0.1", flags, NULL, CertificateList(),
-                     &verify_result);
+  int error =
+      Verify(cert.get(), "127.0.0.1", flags, CRLSet::BuiltinCRLSet().get(),
+             CertificateList(), &verify_result);
   EXPECT_THAT(error, IsError(ERR_CERT_DATE_INVALID));
   EXPECT_TRUE(verify_result.cert_status & CERT_STATUS_DATE_INVALID);
 }
@@ -1118,8 +1125,9 @@
       ASSERT_TRUE(cert_chain);
 
       CertVerifyResult verify_result;
-      int error = Verify(cert_chain.get(), "127.0.0.1", 0, NULL,
-                         CertificateList(), &verify_result);
+      int error = Verify(cert_chain.get(), "127.0.0.1", 0,
+                         CRLSet::BuiltinCRLSet().get(), CertificateList(),
+                         &verify_result);
 
       if (IsWeakKeyType(*ee_type) || IsWeakKeyType(*signer_type)) {
         EXPECT_NE(OK, error);
@@ -1175,8 +1183,9 @@
 
   CertVerifyResult verify_result;
   int flags = 0;
-  int error = Verify(cert_chain.get(), "127.0.0.1", flags, NULL,
-                     CertificateList(), &verify_result);
+  int error =
+      Verify(cert_chain.get(), "127.0.0.1", flags,
+             CRLSet::BuiltinCRLSet().get(), CertificateList(), &verify_result);
   EXPECT_THAT(error, IsOk());
 
   // The extra MD5 root should be discarded
@@ -1209,60 +1218,20 @@
 
   CertVerifyResult verify_result;
   int flags = CertVerifyProc::VERIFY_REV_CHECKING_ENABLED;
-  int error = Verify(cert_chain.get(), "mail.google.com", flags, NULL,
-                     CertificateList(), &verify_result);
+  int error =
+      Verify(cert_chain.get(), "mail.google.com", flags,
+             CRLSet::BuiltinCRLSet().get(), CertificateList(), &verify_result);
   EXPECT_NE(OK, error);
 
   // Now turn off revocation checking.  Certificate verification should still
   // fail.
   flags = 0;
-  error = Verify(cert_chain.get(), "mail.google.com", flags, NULL,
-                 CertificateList(), &verify_result);
+  error =
+      Verify(cert_chain.get(), "mail.google.com", flags,
+             CRLSet::BuiltinCRLSet().get(), CertificateList(), &verify_result);
   EXPECT_NE(OK, error);
 }
 
-// Ensures the CertVerifyProc blacklist remains in sorted order, so that it
-// can be binary-searched.
-TEST(CertVerifyProcTest, BlacklistIsSorted) {
-// Defines kBlacklistedSPKIs.
-#include "net/cert/cert_verify_proc_blacklist.inc"
-  for (size_t i = 0; i < base::size(kBlacklistedSPKIs) - 1; ++i) {
-    EXPECT_GT(0, memcmp(kBlacklistedSPKIs[i], kBlacklistedSPKIs[i + 1],
-                        crypto::kSHA256Length))
-        << " at index " << i;
-  }
-}
-
-TEST(CertVerifyProcTest, DigiNotarCerts) {
-  static const char* const kDigiNotarFilenames[] = {
-      "diginotar_root_ca.pem",          "diginotar_cyber_ca.pem",
-      "diginotar_services_1024_ca.pem", "diginotar_pkioverheid.pem",
-      "diginotar_pkioverheid_g2.pem",   NULL,
-  };
-
-  base::FilePath certs_dir = GetTestCertsDirectory();
-
-  for (size_t i = 0; kDigiNotarFilenames[i]; i++) {
-    scoped_refptr<X509Certificate> diginotar_cert =
-        ImportCertFromFile(certs_dir, kDigiNotarFilenames[i]);
-    base::StringPiece spki;
-    ASSERT_TRUE(asn1::ExtractSPKIFromDERCert(
-        x509_util::CryptoBufferAsStringPiece(diginotar_cert->cert_buffer()),
-        &spki));
-
-    std::string spki_sha256 = crypto::SHA256HashString(spki);
-
-    HashValueVector public_keys;
-    HashValue hash(HASH_VALUE_SHA256);
-    ASSERT_EQ(hash.size(), spki_sha256.size());
-    memcpy(hash.data(), spki_sha256.data(), spki_sha256.size());
-    public_keys.push_back(hash);
-
-    EXPECT_TRUE(CertVerifyProc::IsPublicKeyBlacklisted(public_keys))
-        << "Public key not blocked for " << kDigiNotarFilenames[i];
-  }
-}
-
 TEST_P(CertVerifyProcInternalTest, NameConstraintsOk) {
   CertificateList ca_cert_list =
       CreateCertificateListFromFile(GetTestCertsDirectory(), "root_ca_cert.pem",
@@ -1278,13 +1247,15 @@
 
   int flags = 0;
   CertVerifyResult verify_result;
-  int error = Verify(leaf.get(), "test.example.com", flags, NULL,
-                     CertificateList(), &verify_result);
+  int error =
+      Verify(leaf.get(), "test.example.com", flags,
+             CRLSet::BuiltinCRLSet().get(), CertificateList(), &verify_result);
   EXPECT_THAT(error, IsOk());
   EXPECT_EQ(0U, verify_result.cert_status);
 
-  error = Verify(leaf.get(), "foo.test2.example.com", flags, NULL,
-                 CertificateList(), &verify_result);
+  error =
+      Verify(leaf.get(), "foo.test2.example.com", flags,
+             CRLSet::BuiltinCRLSet().get(), CertificateList(), &verify_result);
   EXPECT_THAT(error, IsOk());
   EXPECT_EQ(0U, verify_result.cert_status);
 }
@@ -1379,7 +1350,8 @@
         new MockCertVerifyProc(dummy_result);
 
     return verify_proc->Verify(chain.get(), "test.example.com", std::string(),
-                               flags, NULL, CertificateList(), &verify_result);
+                               flags, CRLSet::BuiltinCRLSet().get(),
+                               CertificateList(), &verify_result);
   }
 
  private:
@@ -1673,8 +1645,9 @@
 
   int flags = 0;
   CertVerifyResult verify_result;
-  int error = Verify(leaf.get(), "test.example.com", flags, NULL,
-                     CertificateList(), &verify_result);
+  int error =
+      Verify(leaf.get(), "test.example.com", flags,
+             CRLSet::BuiltinCRLSet().get(), CertificateList(), &verify_result);
   EXPECT_THAT(error, IsError(ERR_CERT_NAME_CONSTRAINT_VIOLATION));
   EXPECT_EQ(CERT_STATUS_NAME_CONSTRAINT_VIOLATION,
             verify_result.cert_status & CERT_STATUS_NAME_CONSTRAINT_VIOLATION);
@@ -1723,8 +1696,9 @@
 
   int flags = 0;
   CertVerifyResult verify_result;
-  int error = Verify(cert_chain.get(), "daltonridgeapts.com", flags, NULL,
-                     CertificateList(), &verify_result);
+  int error =
+      Verify(cert_chain.get(), "daltonridgeapts.com", flags,
+             CRLSet::BuiltinCRLSet().get(), CertificateList(), &verify_result);
   EXPECT_THAT(error, IsOk()) << "This test relies on a real certificate that "
                              << "expires on May 28, 2021. If failing on/after "
                              << "that date, please disable and file a bug "
@@ -1758,8 +1732,9 @@
 
   int flags = 0;
   CertVerifyResult verify_result;
-  int error = Verify(cert_chain.get(), "127.0.0.1", flags, NULL,
-                     CertificateList(), &verify_result);
+  int error =
+      Verify(cert_chain.get(), "127.0.0.1", flags,
+             CRLSet::BuiltinCRLSet().get(), CertificateList(), &verify_result);
   EXPECT_THAT(error, IsOk());
 
   EXPECT_EQ(3u, verify_result.public_key_hashes.size());
@@ -1796,8 +1771,9 @@
 
   int flags = 0;
   CertVerifyResult verify_result;
-  int error = Verify(server_cert.get(), "jira.aquameta.com", flags, NULL,
-                     CertificateList(), &verify_result);
+  int error =
+      Verify(server_cert.get(), "jira.aquameta.com", flags,
+             CRLSet::BuiltinCRLSet().get(), CertificateList(), &verify_result);
 
   EXPECT_TRUE(verify_result.cert_status & CERT_STATUS_COMMON_NAME_INVALID);
 
@@ -1843,8 +1819,9 @@
 
   int flags = 0;
   CertVerifyResult verify_result;
-  int error = Verify(cert_chain.get(), "test.example", flags, NULL,
-                     CertificateList(), &verify_result);
+  int error =
+      Verify(cert_chain.get(), "test.example", flags,
+             CRLSet::BuiltinCRLSet().get(), CertificateList(), &verify_result);
 
   if (AreSHA1IntermediatesAllowed()) {
     EXPECT_THAT(error, IsOk());
@@ -1889,8 +1866,9 @@
   CertVerifyResult verify_result;
   EXPECT_EQ(static_cast<X509Certificate*>(NULL),
             verify_result.verified_cert.get());
-  int error = Verify(google_full_chain.get(), "127.0.0.1", 0, NULL,
-                     CertificateList(), &verify_result);
+  int error =
+      Verify(google_full_chain.get(), "127.0.0.1", 0,
+             CRLSet::BuiltinCRLSet().get(), CertificateList(), &verify_result);
   EXPECT_THAT(error, IsOk());
   ASSERT_NE(static_cast<X509Certificate*>(NULL),
             verify_result.verified_cert.get());
@@ -1927,8 +1905,9 @@
   dummy_result.is_issued_by_known_root = true;
   scoped_refptr<CertVerifyProc> verify_proc =
       new MockCertVerifyProc(dummy_result);
-  error = verify_proc->Verify(cert.get(), "webmail", std::string(), 0, nullptr,
-                              CertificateList(), &verify_result);
+  error = verify_proc->Verify(cert.get(), "webmail", std::string(), 0,
+                              CRLSet::BuiltinCRLSet().get(), CertificateList(),
+                              &verify_result);
   EXPECT_THAT(error, IsOk());
   EXPECT_TRUE(verify_result.cert_status & CERT_STATUS_NON_UNIQUE_NAME);
 
@@ -1936,8 +1915,9 @@
   dummy_result.Reset();
   dummy_result.is_issued_by_known_root = false;
   verify_proc = base::MakeRefCounted<MockCertVerifyProc>(dummy_result);
-  error = verify_proc->Verify(cert.get(), "webmail", std::string(), 0, nullptr,
-                              CertificateList(), &verify_result);
+  error = verify_proc->Verify(cert.get(), "webmail", std::string(), 0,
+                              CRLSet::BuiltinCRLSet().get(), CertificateList(),
+                              &verify_result);
   EXPECT_THAT(error, IsOk());
   EXPECT_FALSE(verify_result.cert_status & CERT_STATUS_NON_UNIQUE_NAME);
 }
@@ -1979,7 +1959,8 @@
 
     CertVerifyResult test_result_1;
     error = verify_proc->Verify(cert.get(), "127.0.0.1", std::string(), 0,
-                                nullptr, CertificateList(), &test_result_1);
+                                CRLSet::BuiltinCRLSet().get(),
+                                CertificateList(), &test_result_1);
     EXPECT_THAT(error, IsError(ERR_CERT_SYMANTEC_LEGACY));
     EXPECT_TRUE(test_result_1.cert_status & CERT_STATUS_SYMANTEC_LEGACY);
 
@@ -1994,16 +1975,17 @@
 
     CertVerifyResult test_result_2;
     error = verify_proc->Verify(cert.get(), "127.0.0.1", std::string(), 0,
-                                nullptr, CertificateList(), &test_result_2);
+                                CRLSet::BuiltinCRLSet().get(),
+                                CertificateList(), &test_result_2);
     EXPECT_THAT(error, IsOk());
     EXPECT_FALSE(test_result_2.cert_status & CERT_STATUS_AUTHORITY_INVALID);
 
     // ... Or the caller disabled enforcement of Symantec policies.
     CertVerifyResult test_result_3;
-    error =
-        verify_proc->Verify(cert.get(), "127.0.0.1", std::string(),
-                            CertVerifyProc::VERIFY_DISABLE_SYMANTEC_ENFORCEMENT,
-                            nullptr, CertificateList(), &test_result_3);
+    error = verify_proc->Verify(
+        cert.get(), "127.0.0.1", std::string(),
+        CertVerifyProc::VERIFY_DISABLE_SYMANTEC_ENFORCEMENT,
+        CRLSet::BuiltinCRLSet().get(), CertificateList(), &test_result_3);
     EXPECT_THAT(error, IsOk());
     EXPECT_FALSE(test_result_3.cert_status & CERT_STATUS_SYMANTEC_LEGACY);
   }
@@ -2034,7 +2016,8 @@
 
     CertVerifyResult test_result_1;
     error = verify_proc->Verify(cert.get(), "127.0.0.1", std::string(), 0,
-                                nullptr, CertificateList(), &test_result_1);
+                                CRLSet::BuiltinCRLSet().get(),
+                                CertificateList(), &test_result_1);
     if (feature_flag_enabled) {
       EXPECT_THAT(error, IsError(ERR_CERT_SYMANTEC_LEGACY));
       EXPECT_TRUE(test_result_1.cert_status & CERT_STATUS_SYMANTEC_LEGACY);
@@ -2054,16 +2037,17 @@
 
     CertVerifyResult test_result_2;
     error = verify_proc->Verify(cert.get(), "127.0.0.1", std::string(), 0,
-                                nullptr, CertificateList(), &test_result_2);
+                                CRLSet::BuiltinCRLSet().get(),
+                                CertificateList(), &test_result_2);
     EXPECT_THAT(error, IsOk());
     EXPECT_FALSE(test_result_2.cert_status & CERT_STATUS_AUTHORITY_INVALID);
 
     // ... Or the caller disabled enforcement of Symantec policies.
     CertVerifyResult test_result_3;
-    error =
-        verify_proc->Verify(cert.get(), "127.0.0.1", std::string(),
-                            CertVerifyProc::VERIFY_DISABLE_SYMANTEC_ENFORCEMENT,
-                            nullptr, CertificateList(), &test_result_3);
+    error = verify_proc->Verify(
+        cert.get(), "127.0.0.1", std::string(),
+        CertVerifyProc::VERIFY_DISABLE_SYMANTEC_ENFORCEMENT,
+        CRLSet::BuiltinCRLSet().get(), CertificateList(), &test_result_3);
     EXPECT_THAT(error, IsOk());
     EXPECT_FALSE(test_result_3.cert_status & CERT_STATUS_SYMANTEC_LEGACY);
   }
@@ -2100,8 +2084,9 @@
 
   CertVerifyResult verify_result;
   EXPECT_FALSE(verify_result.verified_cert);
-  int error = Verify(google_full_chain.get(), "127.0.0.1", 0, NULL,
-                     CertificateList(), &verify_result);
+  int error =
+      Verify(google_full_chain.get(), "127.0.0.1", 0,
+             CRLSet::BuiltinCRLSet().get(), CertificateList(), &verify_result);
   EXPECT_THAT(error, IsOk());
   ASSERT_TRUE(verify_result.verified_cert);
 
@@ -2154,8 +2139,9 @@
 
   CertVerifyResult verify_result;
   EXPECT_FALSE(verify_result.verified_cert);
-  int error = Verify(google_full_chain.get(), "127.0.0.1", 0, NULL,
-                     CertificateList(), &verify_result);
+  int error =
+      Verify(google_full_chain.get(), "127.0.0.1", 0,
+             CRLSet::BuiltinCRLSet().get(), CertificateList(), &verify_result);
   EXPECT_THAT(error, IsOk());
   ASSERT_TRUE(verify_result.verified_cert);
 
@@ -2194,8 +2180,9 @@
   // list.
   int flags = 0;
   CertVerifyResult verify_result;
-  int error = Verify(cert.get(), "127.0.0.1", flags, NULL, CertificateList(),
-                     &verify_result);
+  int error =
+      Verify(cert.get(), "127.0.0.1", flags, CRLSet::BuiltinCRLSet().get(),
+             CertificateList(), &verify_result);
   EXPECT_THAT(error, IsError(ERR_CERT_AUTHORITY_INVALID));
   EXPECT_EQ(CERT_STATUS_AUTHORITY_INVALID, verify_result.cert_status);
   EXPECT_FALSE(verify_result.is_issued_by_additional_trust_anchor);
@@ -2203,16 +2190,16 @@
   // Now add the |ca_cert| to the |trust_anchors|, and verification should pass.
   CertificateList trust_anchors;
   trust_anchors.push_back(ca_cert);
-  error = Verify(cert.get(), "127.0.0.1", flags, NULL, trust_anchors,
-                 &verify_result);
+  error = Verify(cert.get(), "127.0.0.1", flags, CRLSet::BuiltinCRLSet().get(),
+                 trust_anchors, &verify_result);
   EXPECT_THAT(error, IsOk());
   EXPECT_EQ(0U, verify_result.cert_status);
   EXPECT_TRUE(verify_result.is_issued_by_additional_trust_anchor);
 
   // Clearing the |trust_anchors| makes verification fail again (the cache
   // should be skipped).
-  error = Verify(cert.get(), "127.0.0.1", flags, NULL, CertificateList(),
-                 &verify_result);
+  error = Verify(cert.get(), "127.0.0.1", flags, CRLSet::BuiltinCRLSet().get(),
+                 CertificateList(), &verify_result);
   EXPECT_THAT(error, IsError(ERR_CERT_AUTHORITY_INVALID));
   EXPECT_EQ(CERT_STATUS_AUTHORITY_INVALID, verify_result.cert_status);
   EXPECT_FALSE(verify_result.is_issued_by_additional_trust_anchor);
@@ -2232,8 +2219,9 @@
   // Verification should pass.
   int flags = 0;
   CertVerifyResult verify_result;
-  int error = Verify(cert.get(), "127.0.0.1", flags, NULL, CertificateList(),
-                     &verify_result);
+  int error =
+      Verify(cert.get(), "127.0.0.1", flags, CRLSet::BuiltinCRLSet().get(),
+             CertificateList(), &verify_result);
   EXPECT_THAT(error, IsOk());
   EXPECT_EQ(0U, verify_result.cert_status);
   // But should not be marked as a known root.
@@ -2261,8 +2249,9 @@
 
   int flags = 0;
   CertVerifyResult verify_result;
-  int error = Verify(cert.get(), "127.0.0.1", flags, NULL, CertificateList(),
-                     &verify_result);
+  int error =
+      Verify(cert.get(), "127.0.0.1", flags, CRLSet::BuiltinCRLSet().get(),
+             CertificateList(), &verify_result);
   EXPECT_THAT(error, IsOk());
   EXPECT_EQ(0U, verify_result.cert_status);
 
@@ -2312,8 +2301,9 @@
 
   int flags = 0;
   CertVerifyResult verify_result;
-  int error = Verify(leaf.get(), "127.0.0.1", flags, NULL, CertificateList(),
-                     &verify_result);
+  int error =
+      Verify(leaf.get(), "127.0.0.1", flags, CRLSet::BuiltinCRLSet().get(),
+             CertificateList(), &verify_result);
   EXPECT_THAT(error, IsOk());
 
   // Test revocation by serial number of a certificate not under the root.
@@ -2750,8 +2740,8 @@
 
   // Verifying the chain should fail as the intermediate is missing, and
   // cannot be fetched via AIA.
-  error = Verify(leaf.get(), kHostname, flags, nullptr, CertificateList(),
-                 &verify_result);
+  error = Verify(leaf.get(), kHostname, flags, CRLSet::BuiltinCRLSet().get(),
+                 CertificateList(), &verify_result);
   EXPECT_NE(OK, error);
 
   if (verify_proc_type() == CERT_VERIFY_PROC_WIN) {
@@ -2802,8 +2792,8 @@
 
   // Verifying the chain should succeed as the missing intermediate can be
   // fetched via AIA.
-  error = Verify(leaf.get(), kHostname, flags, nullptr, CertificateList(),
-                 &verify_result);
+  error = Verify(leaf.get(), kHostname, flags, CRLSet::BuiltinCRLSet().get(),
+                 CertificateList(), &verify_result);
   EXPECT_THAT(error, IsOk());
 }
 
@@ -2851,8 +2841,8 @@
 
   // Verifying the chain should succeed as the missing intermediate can be
   // fetched via AIA.
-  error = Verify(leaf.get(), kHostname, flags, nullptr, CertificateList(),
-                 &verify_result);
+  error = Verify(leaf.get(), kHostname, flags, CRLSet::BuiltinCRLSet().get(),
+                 CertificateList(), &verify_result);
 
   if (verify_proc_type() == CERT_VERIFY_PROC_ANDROID) {
     // Android doesn't support PEM - https://crbug.com/725180
@@ -2904,8 +2894,8 @@
 
   // Verifying the chain should succeed as the missing intermediate can be
   // fetched via AIA.
-  error = Verify(leaf.get(), kHostname, flags, nullptr, CertificateList(),
-                 &verify_result);
+  error = Verify(leaf.get(), kHostname, flags, CRLSet::BuiltinCRLSet().get(),
+                 CertificateList(), &verify_result);
 
   if (verify_proc_type() == CERT_VERIFY_PROC_ANDROID) {
     // Android doesn't support PEM - https://crbug.com/725180
@@ -2974,8 +2964,9 @@
 
   const int flags = 0;
   CertVerifyResult verify_result;
-  int error = Verify(chain_sha1.get(), kHostname, flags, nullptr,
-                     CertificateList(), &verify_result);
+  int error =
+      Verify(chain_sha1.get(), kHostname, flags, CRLSet::BuiltinCRLSet().get(),
+             CertificateList(), &verify_result);
 
   if (verify_proc_type() == CERT_VERIFY_PROC_BUILTIN ||
       verify_proc_type() == CERT_VERIFY_PROC_MAC) {
@@ -3016,8 +3007,8 @@
   int flags = 0;
   CertVerifyResult verify_result;
   int error = verify_proc->Verify(cert.get(), "127.0.0.1", std::string(), flags,
-                                  nullptr /* crl_set */, CertificateList(),
-                                  &verify_result);
+                                  CRLSet::BuiltinCRLSet().get(),
+                                  CertificateList(), &verify_result);
   EXPECT_THAT(error, IsError(ERR_CERT_INVALID));
   EXPECT_TRUE(verify_result.cert_status & CERT_STATUS_INVALID);
 }
@@ -3034,8 +3025,8 @@
   int flags = 0;
   CertVerifyResult verify_result;
   int error = verify_proc->Verify(cert.get(), "127.0.0.1", std::string(), flags,
-                                  nullptr /* crl_set */, CertificateList(),
-                                  &verify_result);
+                                  CRLSet::BuiltinCRLSet().get(),
+                                  CertificateList(), &verify_result);
   EXPECT_THAT(error, IsError(ERR_CERT_INVALID));
   EXPECT_TRUE(verify_result.cert_status & CERT_STATUS_INVALID);
 }
@@ -3052,8 +3043,8 @@
   int flags = 0;
   CertVerifyResult verify_result;
   int error = verify_proc->Verify(cert.get(), "127.0.0.1", std::string(), flags,
-                                  nullptr /* crl_set */, CertificateList(),
-                                  &verify_result);
+                                  CRLSet::BuiltinCRLSet().get(),
+                                  CertificateList(), &verify_result);
   EXPECT_THAT(error, IsError(ERR_CERT_WEAK_SIGNATURE_ALGORITHM));
   EXPECT_TRUE(verify_result.cert_status & CERT_STATUS_WEAK_SIGNATURE_ALGORITHM);
 }
@@ -3072,8 +3063,8 @@
   int flags = 0;
   CertVerifyResult verify_result;
   int error = verify_proc->Verify(cert.get(), "127.0.0.1", std::string(), flags,
-                                  nullptr /* crl_set */, CertificateList(),
-                                  &verify_result);
+                                  CRLSet::BuiltinCRLSet().get(),
+                                  CertificateList(), &verify_result);
   EXPECT_THAT(error, IsError(ERR_CERT_WEAK_SIGNATURE_ALGORITHM));
   EXPECT_TRUE(verify_result.cert_status & CERT_STATUS_WEAK_SIGNATURE_ALGORITHM);
 }
@@ -3092,8 +3083,8 @@
   int flags = 0;
   CertVerifyResult verify_result;
   int error = verify_proc->Verify(cert.get(), "127.0.0.1", std::string(), flags,
-                                  nullptr /* crl_set */, CertificateList(),
-                                  &verify_result);
+                                  CRLSet::BuiltinCRLSet().get(),
+                                  CertificateList(), &verify_result);
   if (AreSHA1IntermediatesAllowed()) {
     EXPECT_THAT(error, IsOk());
     EXPECT_TRUE(verify_result.cert_status & CERT_STATUS_SHA1_SIGNATURE_PRESENT);
@@ -3119,8 +3110,8 @@
   int flags = 0;
   CertVerifyResult verify_result;
   int error = verify_proc->Verify(cert.get(), "127.0.0.1", std::string(), flags,
-                                  nullptr /* crl_set */, CertificateList(),
-                                  &verify_result);
+                                  CRLSet::BuiltinCRLSet().get(),
+                                  CertificateList(), &verify_result);
   EXPECT_THAT(error, IsError(ERR_CERT_WEAK_SIGNATURE_ALGORITHM));
   EXPECT_TRUE(verify_result.cert_status & CERT_STATUS_SHA1_SIGNATURE_PRESENT);
 
@@ -3128,7 +3119,7 @@
   flags = CertVerifyProc::VERIFY_ENABLE_SHA1_LOCAL_ANCHORS;
   verify_result.Reset();
   error = verify_proc->Verify(cert.get(), "127.0.0.1", std::string(), flags,
-                              nullptr /* crl_set */, CertificateList(),
+                              CRLSet::BuiltinCRLSet().get(), CertificateList(),
                               &verify_result);
   EXPECT_THAT(error, IsOk());
   EXPECT_TRUE(verify_result.cert_status & CERT_STATUS_SHA1_SIGNATURE_PRESENT);
@@ -3214,8 +3205,9 @@
   // hash algorithms is done by CertVerifyProc::Verify().
   scoped_refptr<CertVerifyProc> proc =
       new MockCertVerifyProc(CertVerifyResult());
-  proc->Verify(ee_chain.get(), "127.0.0.1", std::string(), flags, nullptr,
-               CertificateList(), &verify_result);
+  proc->Verify(ee_chain.get(), "127.0.0.1", std::string(), flags,
+               CRLSet::BuiltinCRLSet().get(), CertificateList(),
+               &verify_result);
   EXPECT_EQ(!!(data.expected_algorithms & EXPECT_MD2), verify_result.has_md2);
   EXPECT_EQ(!!(data.expected_algorithms & EXPECT_MD4), verify_result.has_md4);
   EXPECT_EQ(!!(data.expected_algorithms & EXPECT_MD5), verify_result.has_md5);
@@ -3343,7 +3335,8 @@
 
     CertVerifyResult verify_result;
     int error = verify_proc->Verify(cert.get(), hostname, std::string(), 0,
-                                    nullptr, CertificateList(), &verify_result);
+                                    CRLSet::BuiltinCRLSet().get(),
+                                    CertificateList(), &verify_result);
     if (valid) {
       EXPECT_THAT(error, IsOk());
       EXPECT_FALSE(verify_result.cert_status & CERT_STATUS_COMMON_NAME_INVALID);
@@ -3437,7 +3430,8 @@
   int flags = 0;
   CertVerifyResult verify_result;
   int error = verify_proc->Verify(cert.get(), "127.0.0.1", std::string(), flags,
-                                  NULL, CertificateList(), &verify_result);
+                                  CRLSet::BuiltinCRLSet().get(),
+                                  CertificateList(), &verify_result);
   EXPECT_EQ(OK, error);
   histograms.ExpectTotalCount(kTLSFeatureExtensionHistogram, 1);
   histograms.ExpectBucketCount(kTLSFeatureExtensionHistogram, true, 1);
@@ -3462,9 +3456,9 @@
 
   int flags = 0;
   CertVerifyResult verify_result;
-  int error =
-      verify_proc->Verify(cert.get(), "127.0.0.1", "dummy response", flags,
-                          nullptr, CertificateList(), &verify_result);
+  int error = verify_proc->Verify(cert.get(), "127.0.0.1", "dummy response",
+                                  flags, CRLSet::BuiltinCRLSet().get(),
+                                  CertificateList(), &verify_result);
   EXPECT_EQ(OK, error);
   histograms.ExpectTotalCount(kTLSFeatureExtensionHistogram, 1);
   histograms.ExpectBucketCount(kTLSFeatureExtensionHistogram, true, 1);
@@ -3490,7 +3484,8 @@
   int flags = 0;
   CertVerifyResult verify_result;
   int error = verify_proc->Verify(cert.get(), "127.0.0.1", std::string(), flags,
-                                  NULL, CertificateList(), &verify_result);
+                                  CRLSet::BuiltinCRLSet().get(),
+                                  CertificateList(), &verify_result);
   EXPECT_EQ(OK, error);
   histograms.ExpectTotalCount(kTLSFeatureExtensionHistogram, 1);
   histograms.ExpectBucketCount(kTLSFeatureExtensionHistogram, false, 1);
@@ -3514,7 +3509,8 @@
   int flags = 0;
   CertVerifyResult verify_result;
   int error = verify_proc->Verify(cert.get(), "127.0.0.1", std::string(), flags,
-                                  NULL, CertificateList(), &verify_result);
+                                  CRLSet::BuiltinCRLSet().get(),
+                                  CertificateList(), &verify_result);
   EXPECT_EQ(OK, error);
   histograms.ExpectTotalCount(kTLSFeatureExtensionHistogram, 0);
   histograms.ExpectTotalCount(kTLSFeatureExtensionOCSPHistogram, 0);
@@ -3554,7 +3550,8 @@
   int flags = 0;
   CertVerifyResult verify_result;
   int error = verify_proc->Verify(cert.get(), "127.0.0.1", std::string(), flags,
-                                  NULL, CertificateList(), &verify_result);
+                                  CRLSet::BuiltinCRLSet().get(),
+                                  CertificateList(), &verify_result);
   EXPECT_EQ(OK, error);
   histograms.ExpectUniqueSample(kTrustAnchorVerifyHistogram,
                                 kGTSRootR4HistogramID, 1);
@@ -3600,7 +3597,8 @@
   int flags = 0;
   CertVerifyResult verify_result;
   int error = verify_proc->Verify(cert.get(), "127.0.0.1", std::string(), flags,
-                                  NULL, CertificateList(), &verify_result);
+                                  CRLSet::BuiltinCRLSet().get(),
+                                  CertificateList(), &verify_result);
   EXPECT_EQ(OK, error);
 
   // Only GTS Root R3 should be recorded.
@@ -3637,7 +3635,8 @@
   int flags = 0;
   CertVerifyResult verify_result;
   int error = verify_proc->Verify(cert.get(), "127.0.0.1", std::string(), flags,
-                                  NULL, CertificateList(), &verify_result);
+                                  CRLSet::BuiltinCRLSet().get(),
+                                  CertificateList(), &verify_result);
   EXPECT_EQ(OK, error);
   const base::HistogramBase::Sample kUnknownRootHistogramID = 0;
   histograms.ExpectUniqueSample(kTrustAnchorVerifyHistogram,
diff --git a/net/cert/cert_verify_proc_win.cc b/net/cert/cert_verify_proc_win.cc
index a7ae954..03f2e60 100644
--- a/net/cert/cert_verify_proc_win.cc
+++ b/net/cert/cert_verify_proc_win.cc
@@ -1014,9 +1014,8 @@
   // should have prevented invalid paths from being built, the behaviour and
   // timing of how a Revocation Provider is invoked is not well documented. This
   // is just defense in depth.
-  CRLSetResult crl_set_result = kCRLSetUnknown;
-  if (crl_set)
-    crl_set_result = CheckChainRevocationWithCRLSet(chain_context, crl_set);
+  CRLSetResult crl_set_result =
+      CheckChainRevocationWithCRLSet(chain_context, crl_set);
 
   if (crl_set_result == kCRLSetRevoked) {
     verify_result->cert_status |= CERT_STATUS_REVOKED;
diff --git a/net/cert/crl_set.cc b/net/cert/crl_set.cc
index 93d3a16..737f233 100644
--- a/net/cert/crl_set.cc
+++ b/net/cert/crl_set.cc
@@ -4,6 +4,8 @@
 
 #include "net/cert/crl_set.h"
 
+#include <algorithm>
+
 #include "base/base64.h"
 #include "base/json/json_reader.h"
 #include "base/time/time.h"
@@ -252,18 +254,22 @@
     return false;
   }
 
+  // Defines kBlacklistedSPKIs.
+#include "net/cert/cert_verify_proc_blacklist.inc"
+  for (const auto& hash : kBlacklistedSPKIs) {
+    crl_set->blocked_spkis_.push_back(std::string(
+        reinterpret_cast<const char*>(hash), crypto::kSHA256Length));
+  }
+  std::sort(crl_set->blocked_spkis_.begin(), crl_set->blocked_spkis_.end());
+
   *out_crl_set = std::move(crl_set);
   return true;
 }
 
 CRLSet::Result CRLSet::CheckSPKI(const base::StringPiece& spki_hash) const {
-  for (auto i = blocked_spkis_.begin(); i != blocked_spkis_.end(); ++i) {
-    if (spki_hash.size() == i->size() &&
-        memcmp(spki_hash.data(), i->data(), i->size()) == 0) {
-      return REVOKED;
-    }
-  }
-
+  if (std::binary_search(blocked_spkis_.begin(), blocked_spkis_.end(),
+                         spki_hash))
+    return REVOKED;
   return GOOD;
 }
 
@@ -328,6 +334,16 @@
 }
 
 // static
+scoped_refptr<CRLSet> CRLSet::BuiltinCRLSet() {
+  constexpr char kCRLSet[] =
+      "\x31\x00{\"ContentType\":\"CRLSet\",\"Sequence\":0,\"Version\":0}";
+  scoped_refptr<CRLSet> ret;
+  bool parsed = CRLSet::Parse({kCRLSet, sizeof(kCRLSet) - 1}, &ret);
+  DCHECK(parsed);
+  return ret;
+}
+
+// static
 scoped_refptr<CRLSet> CRLSet::EmptyCRLSetForTesting() {
   return ForTesting(false, nullptr, "", "", {});
 }
diff --git a/net/cert/crl_set.h b/net/cert/crl_set.h
index 26219f2..52afa8f 100644
--- a/net/cert/crl_set.h
+++ b/net/cert/crl_set.h
@@ -72,6 +72,11 @@
   // testing.
   const CRLList& CrlsForTesting() const;
 
+  // BuiltinCRLSet() returns the default CRLSet, to be used when no CRLSet is
+  // available from the network.  The default CRLSet includes a statically-
+  // configured blacklist.
+  static scoped_refptr<CRLSet> BuiltinCRLSet();
+
   // EmptyCRLSetForTesting returns a valid, but empty, CRLSet for unit tests.
   static scoped_refptr<CRLSet> EmptyCRLSetForTesting();
 
diff --git a/net/cert/crl_set_unittest.cc b/net/cert/crl_set_unittest.cc
index 296a7eb..b9c89e6 100644
--- a/net/cert/crl_set_unittest.cc
+++ b/net/cert/crl_set_unittest.cc
@@ -126,6 +126,43 @@
             set->CheckSPKI(reinterpret_cast<const char*>(spki_hash)));
 }
 
+TEST(CertVerifyProcTest, CRLSetIncorporatesStaticBlacklist) {
+  // Test both the builtin CRLSet and a parsed CRLSet to be sure that both
+  // include the blacklist.
+  scoped_refptr<CRLSet> set1 = CRLSet::BuiltinCRLSet();
+  ASSERT_TRUE(set1);
+  base::StringPiece s(reinterpret_cast<const char*>(kGIACRLSet),
+                      sizeof(kGIACRLSet));
+  scoped_refptr<CRLSet> set2;
+  EXPECT_TRUE(CRLSet::Parse(s, &set2));
+  ASSERT_TRUE(set2);
+
+  static const char* const kDigiNotarFilenames[] = {
+      "diginotar_root_ca.pem",          "diginotar_cyber_ca.pem",
+      "diginotar_services_1024_ca.pem", "diginotar_pkioverheid.pem",
+      "diginotar_pkioverheid_g2.pem",   NULL,
+  };
+
+  base::FilePath certs_dir = GetTestCertsDirectory();
+
+  for (size_t i = 0; kDigiNotarFilenames[i]; i++) {
+    scoped_refptr<X509Certificate> diginotar_cert =
+        ImportCertFromFile(certs_dir, kDigiNotarFilenames[i]);
+    ASSERT_TRUE(diginotar_cert);
+    base::StringPiece spki;
+    ASSERT_TRUE(asn1::ExtractSPKIFromDERCert(
+        x509_util::CryptoBufferAsStringPiece(diginotar_cert->cert_buffer()),
+        &spki));
+
+    std::string spki_sha256 = crypto::SHA256HashString(spki);
+
+    EXPECT_EQ(CRLSet::REVOKED, set1->CheckSPKI(spki_sha256))
+        << "Public key not blocked for " << kDigiNotarFilenames[i];
+    EXPECT_EQ(CRLSet::REVOKED, set2->CheckSPKI(spki_sha256))
+        << "Public key not blocked for " << kDigiNotarFilenames[i];
+  }
+}
+
 TEST(CRLSetTest, BlockedSubjects) {
   std::string crl_set_bytes;
   EXPECT_TRUE(base::ReadFileToString(
diff --git a/net/cert/ct_log_verifier.cc b/net/cert/ct_log_verifier.cc
index 7559d33..ea79f5c7 100644
--- a/net/cert/ct_log_verifier.cc
+++ b/net/cert/ct_log_verifier.cc
@@ -108,7 +108,9 @@
     return false;
 
   std::string serialized_data;
-  ct::EncodeTreeHeadSignature(signed_tree_head, &serialized_data);
+  if (!ct::EncodeTreeHeadSignature(signed_tree_head, &serialized_data))
+    return false;
+
   if (VerifySignature(serialized_data,
                       signed_tree_head.signature.signature_data)) {
     if (signed_tree_head.tree_size == 0) {
diff --git a/net/cert/ct_serialization.cc b/net/cert/ct_serialization.cc
index ff843d0..401b8fe 100644
--- a/net/cert/ct_serialization.cc
+++ b/net/cert/ct_serialization.cc
@@ -4,17 +4,12 @@
 
 #include "net/cert/ct_serialization.h"
 
-#include <stdint.h>
-
-#include <algorithm>
-#include <limits>
-
 #include "base/logging.h"
-#include "base/numerics/safe_math.h"
 #include "crypto/sha2.h"
 #include "net/cert/merkle_tree_leaf.h"
 #include "net/cert/signed_certificate_timestamp.h"
 #include "net/cert/signed_tree_head.h"
+#include "third_party/boringssl/src/include/openssl/bytestring.h"
 
 namespace net {
 
@@ -22,132 +17,39 @@
 
 namespace {
 
-// Note: length is always specified in bytes.
-// CT protocol version length
-const size_t kVersionLength = 1;
-
-// Common V1 struct members
-const size_t kTimestampLength = 8;
-const size_t kSignedEntryTypeLength = 2;
-const size_t kAsn1CertificateLengthBytes = 3;
-const size_t kTbsCertificateLengthBytes = 3;
-const size_t kExtensionsLengthBytes = 2;
-
-// Members of a V1 SCT
 const size_t kLogIdLength = crypto::kSHA256Length;
-const size_t kHashAlgorithmLength = 1;
-const size_t kSigAlgorithmLength = 1;
-const size_t kSignatureLengthBytes = 2;
-
-// Members of the digitally-signed struct of a V1 SCT
-const size_t kSignatureTypeLength = 1;
-
-const size_t kSCTListLengthBytes = 2;
-const size_t kSerializedSCTLengthBytes = 2;
-
-// Members of digitally-signed struct of a STH
-const size_t kTreeSizeLength = 8;
-
-// Members of a V1 MerkleTreeLeaf
-const size_t kMerkleLeafTypeLength = 1;
-const size_t kIssuerKeyHashLength = crypto::kSHA256Length;
 
 enum SignatureType {
   SIGNATURE_TYPE_CERTIFICATE_TIMESTAMP = 0,
   TREE_HASH = 1,
 };
 
-// Reads a TLS-encoded variable length unsigned integer from |in|.
-// The integer is expected to be in big-endian order, which is used by TLS.
-// The bytes read from |in| are discarded (i.e. |in|'s prefix removed)
-// |length| indicates the size (in bytes) of the integer. On success, returns
-// true and stores the result in |*out|.
-template <typename T>
-bool ReadUint(size_t length, base::StringPiece* in, T* out) {
-  if (in->size() < length)
-    return false;
-  DCHECK_NE(length, 0u);
-  DCHECK_LE(length, sizeof(T));
-
-  T result = static_cast<uint8_t>((*in)[0]);
-  // This loop only executes if sizeof(T) > 1, because the first operation is
-  // to shift left by 1 byte, which is undefined behaviour if T is a 1 byte
-  // integer.
-  for (size_t i = 1; i < length; ++i) {
-    result = (result << 8) | static_cast<uint8_t>((*in)[i]);
-  }
-  in->remove_prefix(length);
-  *out = result;
-  return true;
-}
-
-// Reads a TLS-encoded field length from |in|.
-// The bytes read from |in| are discarded (i.e. |in|'s prefix removed).
-// |prefix_length| indicates the bytes needed to represent the length (e.g. 3).
-// Max |prefix_length| is 8.
-// success, returns true and stores the result in |*out|.
-bool ReadLength(size_t prefix_length, base::StringPiece* in, size_t* out) {
-  uint64_t length = 0;
-  if (!ReadUint(prefix_length, in, &length))
-    return false;
-  base::CheckedNumeric<size_t> checked_length = length;
-  if (!checked_length.IsValid())
-    return false;
-  *out = checked_length.ValueOrDie();
-  return true;
-}
-
-// Reads |length| bytes from |*in|. If |*in| is too small, returns false.
-// The bytes read from |in| are discarded (i.e. |in|'s prefix removed)
-bool ReadFixedBytes(size_t length,
-                    base::StringPiece* in,
-                    base::StringPiece* out) {
-  if (in->length() < length)
-    return false;
-  out->set(in->data(), length);
-  in->remove_prefix(length);
-  return true;
-}
-
-// Reads a length-prefixed variable amount of bytes from |in|, updating |out|
-// on success. |prefix_length| indicates the number of bytes needed to represent
-// the length.
-// The bytes read from |in| are discarded (i.e. |in|'s prefix removed)
-bool ReadVariableBytes(size_t prefix_length,
-                       base::StringPiece* in,
-                       base::StringPiece* out) {
-  size_t length = 0;
-  if (!ReadLength(prefix_length, in, &length))
-    return false;
-  return ReadFixedBytes(length, in, out);
-}
-
-// Reads a variable-length list that has been TLS encoded.
+// Reads a variable-length SCT list that has been TLS encoded.
 // The bytes read from |in| are discarded (i.e. |in|'s prefix removed)
 // |max_list_length| contains the overall length of the encoded list.
 // |max_item_length| contains the maximum length of a single item.
 // On success, returns true and updates |*out| with the encoded list.
-bool ReadList(size_t max_list_length,
-              size_t max_item_length,
-              base::StringPiece* in,
-              std::vector<base::StringPiece>* out) {
+bool ReadSCTList(CBS* in, std::vector<base::StringPiece>* out) {
   std::vector<base::StringPiece> result;
 
-  base::StringPiece list_data;
-  if (!ReadVariableBytes(max_list_length, in, &list_data))
+  CBS sct_list_data;
+
+  if (!CBS_get_u16_length_prefixed(in, &sct_list_data))
     return false;
 
-  while (!list_data.empty()) {
-    base::StringPiece list_item;
-    if (!ReadVariableBytes(max_item_length, &list_data, &list_item)) {
+  while (CBS_len(&sct_list_data) != 0) {
+    CBS sct_list_item;
+    if (!CBS_get_u16_length_prefixed(&sct_list_data, &sct_list_item)) {
       DVLOG(1) << "Failed to read item in list.";
       return false;
     }
-    if (list_item.empty()) {
+    if (CBS_len(&sct_list_item) == 0) {
       DVLOG(1) << "Empty item in list";
       return false;
     }
-    result.push_back(list_item);
+
+    result.emplace_back(reinterpret_cast<const char*>(CBS_data(&sct_list_item)),
+                        CBS_len(&sct_list_item));
   }
 
   result.swap(*out);
@@ -195,103 +97,73 @@
   return true;
 }
 
-// Writes a TLS-encoded variable length unsigned integer to |output|.
-// |length| indicates the size (in bytes) of the integer. This must be able to
-// accomodate |value|.
-// |value| the value itself to be written.
-void WriteUint(size_t length, uint64_t value, std::string* output) {
-  // Check that |value| fits into |length| bytes.
-  DCHECK(length >= sizeof(value) || value >> (length * 8) == 0);
-
-  for (; length > 0; --length) {
-    output->push_back((value >> ((length - 1) * 8)) & 0xFF);
-  }
-}
-
-// Writes an array to |output| from |input|.
-// Should be used in one of two cases:
-// * The length of |input| has already been encoded into the |output| stream.
-// * The length of |input| is fixed and the reader is expected to specify that
-// length when reading.
-// If the length of |input| is dynamic and data is expected to follow it,
-// WriteVariableBytes must be used.
-// Returns the number of bytes written (the length of |input|).
-size_t WriteEncodedBytes(const base::StringPiece& input, std::string* output) {
-  input.AppendToString(output);
-  return input.size();
-}
-
-// Writes a variable-length array to |output|.
-// |prefix_length| indicates the number of bytes needed to represent the length.
-// |input| is the array itself.
-// If 1 <= |prefix_length| <= 8 and the size of |input| is less than
-// 2^|prefix_length| - 1, encode the length and data and return true.
-// Otherwise, return false.
-bool WriteVariableBytes(size_t prefix_length,
-                        const base::StringPiece& input,
-                        std::string* output) {
-  DCHECK_GE(prefix_length, 1u);
-  DCHECK_LE(prefix_length, 8u);
-
-  uint64_t input_size = input.size();
-  uint64_t max_input_size = (prefix_length == 8)
-                                ? UINT64_MAX
-                                : ((UINT64_C(1) << (prefix_length * 8)) - 1);
-
-  if (input_size > max_input_size)
-    return false;
-
-  WriteUint(prefix_length, input_size, output);
-  WriteEncodedBytes(input, output);
-
-  return true;
-}
-
-// Writes a SignedEntryData of type X.509 cert to |output|.
+// Writes a SignedEntryData of type X.509 cert to |*output|.
 // |input| is the SignedEntryData containing the certificate.
 // Returns true if the leaf_certificate in the SignedEntryData does not exceed
 // kMaxAsn1CertificateLength and so can be written to |output|.
-bool EncodeAsn1CertSignedEntry(const SignedEntryData& input,
-                               std::string* output) {
-  return WriteVariableBytes(kAsn1CertificateLengthBytes,
-                            input.leaf_certificate, output);
+bool EncodeAsn1CertSignedEntry(const SignedEntryData& input, CBB* output) {
+  CBB child;
+  return CBB_add_u24_length_prefixed(output, &child) &&
+         CBB_add_bytes(
+             &child,
+             reinterpret_cast<const uint8_t*>(input.leaf_certificate.data()),
+             input.leaf_certificate.size()) &&
+         CBB_flush(output);
 }
 
-// Writes a SignedEntryData of type PreCertificate to |output|.
+// Writes a SignedEntryData of type PreCertificate to |*output|.
 // |input| is the SignedEntryData containing the TBSCertificate and issuer key
 // hash. Returns true if the TBSCertificate component in the SignedEntryData
 // does not exceed kMaxTbsCertificateLength and so can be written to |output|.
-bool EncodePrecertSignedEntry(const SignedEntryData& input,
-                              std::string* output) {
-  WriteEncodedBytes(
-      base::StringPiece(
-          reinterpret_cast<const char*>(input.issuer_key_hash.data),
-          kIssuerKeyHashLength),
-      output);
-  return WriteVariableBytes(kTbsCertificateLengthBytes,
-                            input.tbs_certificate, output);
+bool EncodePrecertSignedEntry(const SignedEntryData& input, CBB* output) {
+  CBB child;
+  return CBB_add_bytes(
+             output,
+             reinterpret_cast<const uint8_t*>(input.issuer_key_hash.data),
+             kLogIdLength) &&
+         CBB_add_u24_length_prefixed(output, &child) &&
+         CBB_add_bytes(
+             &child,
+             reinterpret_cast<const uint8_t*>(input.tbs_certificate.data()),
+             input.tbs_certificate.size()) &&
+         CBB_flush(output);
 }
 
 }  // namespace
 
-bool EncodeDigitallySigned(const DigitallySigned& input,
-                           std::string* output) {
-  WriteUint(kHashAlgorithmLength, input.hash_algorithm, output);
-  WriteUint(kSigAlgorithmLength, input.signature_algorithm,
-            output);
-  return WriteVariableBytes(kSignatureLengthBytes, input.signature_data,
-                            output);
+bool EncodeDigitallySigned(const DigitallySigned& input, CBB* output_cbb) {
+  CBB child;
+  return CBB_add_u8(output_cbb, input.hash_algorithm) &&
+         CBB_add_u8(output_cbb, input.signature_algorithm) &&
+         CBB_add_u16_length_prefixed(output_cbb, &child) &&
+         CBB_add_bytes(
+             &child,
+             reinterpret_cast<const uint8_t*>(input.signature_data.data()),
+             input.signature_data.size()) &&
+         CBB_flush(output_cbb);
 }
 
-bool DecodeDigitallySigned(base::StringPiece* input,
-                           DigitallySigned* output) {
-  unsigned hash_algo;
-  unsigned sig_algo;
-  base::StringPiece sig_data;
+bool EncodeDigitallySigned(const DigitallySigned& input,
+                           std::string* output) {
+  bssl::ScopedCBB output_cbb;
+  if (!CBB_init(output_cbb.get(), 64) ||
+      !EncodeDigitallySigned(input, output_cbb.get()) ||
+      !CBB_flush(output_cbb.get())) {
+    return false;
+  }
 
-  if (!ReadUint(kHashAlgorithmLength, input, &hash_algo) ||
-      !ReadUint(kSigAlgorithmLength, input, &sig_algo) ||
-      !ReadVariableBytes(kSignatureLengthBytes, input, &sig_data)) {
+  output->append(reinterpret_cast<const char*>(CBB_data(output_cbb.get())),
+                 CBB_len(output_cbb.get()));
+  return true;
+}
+
+bool DecodeDigitallySigned(CBS* input, DigitallySigned* output) {
+  uint8_t hash_algo;
+  uint8_t sig_algo;
+  CBS sig_data;
+
+  if (!CBS_get_u8(input, &hash_algo) || !CBS_get_u8(input, &sig_algo) ||
+      !CBS_get_u16_length_prefixed(input, &sig_data)) {
     return false;
   }
 
@@ -304,14 +176,26 @@
     DVLOG(1) << "Invalid signature algorithm " << sig_algo;
     return false;
   }
-  sig_data.CopyToString(&result.signature_data);
+  result.signature_data.assign(
+      reinterpret_cast<const char*>(CBS_data(&sig_data)), CBS_len(&sig_data));
 
   *output = result;
   return true;
 }
 
-bool EncodeSignedEntry(const SignedEntryData& input, std::string* output) {
-  WriteUint(kSignedEntryTypeLength, input.type, output);
+bool DecodeDigitallySigned(base::StringPiece* input, DigitallySigned* output) {
+  CBS input_cbs;
+  CBS_init(&input_cbs, reinterpret_cast<const uint8_t*>(input->data()),
+           input->size());
+  bool result = DecodeDigitallySigned(&input_cbs, output);
+  input->remove_prefix(input->size() - CBS_len(&input_cbs));
+  return result;
+}
+
+static bool EncodeSignedEntry(const SignedEntryData& input, CBB* output) {
+  if (!CBB_add_u16(output, input.type)) {
+    return false;
+  }
   switch (input.type) {
     case SignedEntryData::LOG_ENTRY_TYPE_X509:
       return EncodeAsn1CertSignedEntry(input, output);
@@ -321,9 +205,23 @@
   return false;
 }
 
-static bool ReadTimeSinceEpoch(base::StringPiece* input, base::Time* output) {
+bool EncodeSignedEntry(const SignedEntryData& input, std::string* output) {
+  bssl::ScopedCBB output_cbb;
+
+  if (!CBB_init(output_cbb.get(), 64) ||
+      !EncodeSignedEntry(input, output_cbb.get()) ||
+      !CBB_flush(output_cbb.get())) {
+    return false;
+  }
+
+  output->append(reinterpret_cast<const char*>(CBB_data(output_cbb.get())),
+                 CBB_len(output_cbb.get()));
+  return true;
+}
+
+static bool ReadTimeSinceEpoch(CBS* input, base::Time* output) {
   uint64_t time_since_epoch = 0;
-  if (!ReadUint(kTimestampLength, input, &time_since_epoch))
+  if (!CBS_get_u64(input, &time_since_epoch))
     return false;
 
   base::CheckedNumeric<int64_t> time_since_epoch_signed = time_since_epoch;
@@ -341,21 +239,28 @@
   return true;
 }
 
-static void WriteTimeSinceEpoch(const base::Time& timestamp,
-                                std::string* output) {
+static bool WriteTimeSinceEpoch(const base::Time& timestamp, CBB* output) {
   base::TimeDelta time_since_epoch = timestamp - base::Time::UnixEpoch();
-  WriteUint(kTimestampLength, time_since_epoch.InMilliseconds(), output);
+  return CBB_add_u64(output, time_since_epoch.InMilliseconds());
 }
 
 bool EncodeTreeLeaf(const MerkleTreeLeaf& leaf, std::string* output) {
-  WriteUint(kVersionLength, 0, output);         // version: 1
-  WriteUint(kMerkleLeafTypeLength, 0, output);  // type: timestamped entry
-  WriteTimeSinceEpoch(leaf.timestamp, output);
-  if (!EncodeSignedEntry(leaf.signed_entry, output))
+  bssl::ScopedCBB output_cbb;
+  CBB child;
+  if (!CBB_init(output_cbb.get(), 64) ||
+      !CBB_add_u8(output_cbb.get(), 0) ||  // version: 1
+      !CBB_add_u8(output_cbb.get(), 0) ||  // type: timestamped entry
+      !WriteTimeSinceEpoch(leaf.timestamp, output_cbb.get()) ||
+      !EncodeSignedEntry(leaf.signed_entry, output_cbb.get()) ||
+      !CBB_add_u16_length_prefixed(output_cbb.get(), &child) ||
+      !CBB_add_bytes(&child,
+                     reinterpret_cast<const uint8_t*>(leaf.extensions.data()),
+                     leaf.extensions.size()) ||
+      !CBB_flush(output_cbb.get())) {
     return false;
-  if (!WriteVariableBytes(kExtensionsLengthBytes, leaf.extensions, output))
-    return false;
-
+  }
+  output->append(reinterpret_cast<const char*>(CBB_data(output_cbb.get())),
+                 CBB_len(output_cbb.get()));
   return true;
 }
 
@@ -363,38 +268,60 @@
                            const std::string& serialized_log_entry,
                            const std::string& extensions,
                            std::string* output) {
-  WriteUint(kVersionLength, SignedCertificateTimestamp::V1,
-            output);
-  WriteUint(kSignatureTypeLength, SIGNATURE_TYPE_CERTIFICATE_TIMESTAMP,
-            output);
-  WriteTimeSinceEpoch(timestamp, output);
-  // NOTE: serialized_log_entry must already be serialized and contain the
-  // length as the prefix.
-  WriteEncodedBytes(serialized_log_entry, output);
-  return WriteVariableBytes(kExtensionsLengthBytes, extensions, output);
+  bssl::ScopedCBB output_cbb;
+  CBB child;
+  if (!CBB_init(output_cbb.get(), 64) ||
+      !CBB_add_u8(output_cbb.get(), SignedCertificateTimestamp::V1) ||
+      !CBB_add_u8(output_cbb.get(), SIGNATURE_TYPE_CERTIFICATE_TIMESTAMP) ||
+      !WriteTimeSinceEpoch(timestamp, output_cbb.get()) ||
+      // NOTE: serialized_log_entry must already be serialized and contain the
+      // length as the prefix.
+      !CBB_add_bytes(
+          output_cbb.get(),
+          reinterpret_cast<const uint8_t*>(serialized_log_entry.data()),
+          serialized_log_entry.size()) ||
+      !CBB_add_u16_length_prefixed(output_cbb.get(), &child) ||
+      !CBB_add_bytes(&child,
+                     reinterpret_cast<const uint8_t*>(extensions.data()),
+                     extensions.size()) ||
+      !CBB_flush(output_cbb.get())) {
+    return false;
+  }
+  output->append(reinterpret_cast<const char*>(CBB_data(output_cbb.get())),
+                 CBB_len(output_cbb.get()));
+  return true;
 }
 
-void EncodeTreeHeadSignature(const SignedTreeHead& signed_tree_head,
+bool EncodeTreeHeadSignature(const SignedTreeHead& signed_tree_head,
                              std::string* output) {
-  WriteUint(kVersionLength, signed_tree_head.version, output);
-  WriteUint(kSignatureTypeLength, TREE_HASH, output);
-  WriteTimeSinceEpoch(signed_tree_head.timestamp, output);
-  WriteUint(kTreeSizeLength, signed_tree_head.tree_size, output);
-  WriteEncodedBytes(
-      base::StringPiece(signed_tree_head.sha256_root_hash, kSthRootHashLength),
-      output);
+  bssl::ScopedCBB output_cbb;
+  if (!CBB_init(output_cbb.get(), 64) ||
+      !CBB_add_u8(output_cbb.get(), signed_tree_head.version) ||
+      !CBB_add_u8(output_cbb.get(), TREE_HASH) ||
+      !WriteTimeSinceEpoch(signed_tree_head.timestamp, output_cbb.get()) ||
+      !CBB_add_u64(output_cbb.get(), signed_tree_head.tree_size) ||
+      !CBB_add_bytes(
+          output_cbb.get(),
+          reinterpret_cast<const uint8_t*>(signed_tree_head.sha256_root_hash),
+          kSthRootHashLength)) {
+    return false;
+  }
+  output->append(reinterpret_cast<const char*>(CBB_data(output_cbb.get())),
+                 CBB_len(output_cbb.get()));
+  return true;
 }
 
 bool DecodeSCTList(base::StringPiece input,
                    std::vector<base::StringPiece>* output) {
   std::vector<base::StringPiece> result;
-  if (!ReadList(kSCTListLengthBytes, kSerializedSCTLengthBytes, &input,
-                &result)) {
+  CBS input_cbs;
+  CBS_init(&input_cbs, reinterpret_cast<const uint8_t*>(input.data()),
+           input.size());
+  if (!ReadSCTList(&input_cbs, &result) || CBS_len(&input_cbs) != 0 ||
+      result.empty()) {
     return false;
   }
 
-  if (!input.empty() || result.empty())
-    return false;
   output->swap(result);
   return true;
 }
@@ -404,8 +331,11 @@
     scoped_refptr<SignedCertificateTimestamp>* output) {
   scoped_refptr<SignedCertificateTimestamp> result(
       new SignedCertificateTimestamp());
-  unsigned version;
-  if (!ReadUint(kVersionLength, input, &version))
+  uint8_t version;
+  CBS input_cbs;
+  CBS_init(&input_cbs, reinterpret_cast<const uint8_t*>(input->data()),
+           input->size());
+  if (!CBS_get_u8(&input_cbs, &version))
     return false;
   if (version != SignedCertificateTimestamp::V1) {
     DVLOG(1) << "Unsupported/invalid version " << version;
@@ -413,42 +343,73 @@
   }
 
   result->version = SignedCertificateTimestamp::V1;
-  base::StringPiece log_id;
-  base::StringPiece extensions;
-  if (!ReadFixedBytes(kLogIdLength, input, &log_id) ||
-      !ReadTimeSinceEpoch(input, &result->timestamp) ||
-      !ReadVariableBytes(kExtensionsLengthBytes, input, &extensions) ||
-      !DecodeDigitallySigned(input, &result->signature)) {
+  CBS log_id;
+  CBS extensions;
+  if (!CBS_get_bytes(&input_cbs, &log_id, kLogIdLength) ||
+      !ReadTimeSinceEpoch(&input_cbs, &result->timestamp) ||
+      !CBS_get_u16_length_prefixed(&input_cbs, &extensions) ||
+      !DecodeDigitallySigned(&input_cbs, &result->signature)) {
     return false;
   }
 
-  log_id.CopyToString(&result->log_id);
-  extensions.CopyToString(&result->extensions);
+  result->log_id.assign(reinterpret_cast<const char*>(CBS_data(&log_id)),
+                        CBS_len(&log_id));
+  result->extensions.assign(
+      reinterpret_cast<const char*>(CBS_data(&extensions)),
+      CBS_len(&extensions));
   output->swap(result);
+  input->remove_prefix(input->size() - CBS_len(&input_cbs));
   return true;
 }
 
-void EncodeSignedCertificateTimestamp(
+bool EncodeSignedCertificateTimestamp(
     const scoped_refptr<ct::SignedCertificateTimestamp>& input,
     std::string* output) {
   // This function only supports serialization of V1 SCTs.
   DCHECK_EQ(SignedCertificateTimestamp::V1, input->version);
-  WriteUint(kVersionLength, input->version, output);
   DCHECK_EQ(kLogIdLength, input->log_id.size());
-  WriteEncodedBytes(
-      base::StringPiece(reinterpret_cast<const char*>(input->log_id.data()),
-                        kLogIdLength),
-      output);
-  WriteTimeSinceEpoch(input->timestamp, output);
-  WriteVariableBytes(kExtensionsLengthBytes, input->extensions, output);
-  EncodeDigitallySigned(input->signature, output);
+
+  bssl::ScopedCBB output_cbb;
+  CBB child;
+  if (!CBB_init(output_cbb.get(), 64) ||
+      !CBB_add_u8(output_cbb.get(), input->version) ||
+      !CBB_add_bytes(output_cbb.get(),
+                     reinterpret_cast<const uint8_t*>(input->log_id.data()),
+                     kLogIdLength) ||
+      !WriteTimeSinceEpoch(input->timestamp, output_cbb.get()) ||
+      !CBB_add_u16_length_prefixed(output_cbb.get(), &child) ||
+      !CBB_add_bytes(&child,
+                     reinterpret_cast<const uint8_t*>(input->extensions.data()),
+                     input->extensions.size()) ||
+      !EncodeDigitallySigned(input->signature, output_cbb.get()) ||
+      !CBB_flush(output_cbb.get())) {
+    return false;
+  }
+  output->append(reinterpret_cast<const char*>(CBB_data(output_cbb.get())),
+                 CBB_len(output_cbb.get()));
+  return true;
 }
 
 bool EncodeSCTListForTesting(const base::StringPiece& sct,
                              std::string* output) {
-  std::string encoded_sct;
-  return WriteVariableBytes(kSerializedSCTLengthBytes, sct, &encoded_sct) &&
-         WriteVariableBytes(kSCTListLengthBytes, encoded_sct, output);
+  bssl::ScopedCBB encoded_sct, output_cbb;
+  CBB encoded_sct_child, output_child;
+  if (!CBB_init(encoded_sct.get(), 64) || !CBB_init(output_cbb.get(), 64) ||
+      !CBB_add_u16_length_prefixed(encoded_sct.get(), &encoded_sct_child) ||
+      !CBB_add_bytes(&encoded_sct_child,
+                     reinterpret_cast<const uint8_t*>(sct.data()),
+                     sct.size()) ||
+      !CBB_flush(encoded_sct.get()) ||
+      !CBB_add_u16_length_prefixed(output_cbb.get(), &output_child) ||
+      !CBB_add_bytes(&output_child, CBB_data(encoded_sct.get()),
+                     CBB_len(encoded_sct.get())) ||
+      !CBB_flush(output_cbb.get())) {
+    return false;
+  }
+
+  output->append(reinterpret_cast<const char*>(CBB_data(output_cbb.get())),
+                 CBB_len(output_cbb.get()));
+  return true;
 }
 
 }  // namespace ct
diff --git a/net/cert/ct_serialization.h b/net/cert/ct_serialization.h
index 4ade8b5..dcfa3fc 100644
--- a/net/cert/ct_serialization.h
+++ b/net/cert/ct_serialization.h
@@ -64,7 +64,9 @@
 // Encodes the data signed by a Signed Tree Head (STH) |signed_tree_head| into
 // |output|. The signature included in the |signed_tree_head| can then be
 // verified over these bytes.
-NET_EXPORT_PRIVATE void EncodeTreeHeadSignature(
+// Returns true if the data could be encoded successfully, false
+// otherwise.
+NET_EXPORT_PRIVATE bool EncodeTreeHeadSignature(
     const SignedTreeHead& signed_tree_head,
     std::string* output);
 
@@ -86,7 +88,9 @@
     scoped_refptr<ct::SignedCertificateTimestamp>* output);
 
 // Serializes a Signed Certificate Timestamp (SCT) into |output|.
-NET_EXPORT void EncodeSignedCertificateTimestamp(
+// Returns true if the SCT could be encoded successfully, false
+// otherwise.
+NET_EXPORT bool EncodeSignedCertificateTimestamp(
     const scoped_refptr<ct::SignedCertificateTimestamp>& input,
     std::string* output);
 
diff --git a/net/cert/ct_serialization_unittest.cc b/net/cert/ct_serialization_unittest.cc
index 004a897..9241a4e 100644
--- a/net/cert/ct_serialization_unittest.cc
+++ b/net/cert/ct_serialization_unittest.cc
@@ -161,7 +161,7 @@
   ASSERT_TRUE(ct::DecodeSignedCertificateTimestamp(&encoded_sct, &sct));
 
   std::string serialized;
-  ct::EncodeSignedCertificateTimestamp(sct, &serialized);
+  ASSERT_TRUE(ct::EncodeSignedCertificateTimestamp(sct, &serialized));
   EXPECT_EQ(serialized, encoded_test_sct);
 }
 
@@ -256,7 +256,7 @@
   ASSERT_TRUE(GetSampleSignedTreeHead(&signed_tree_head));
 
   std::string encoded;
-  ct::EncodeTreeHeadSignature(signed_tree_head, &encoded);
+  ASSERT_TRUE(ct::EncodeTreeHeadSignature(signed_tree_head, &encoded));
   // Expected size is 50 bytes:
   // Byte 0 is version, byte 1 is signature type
   // Bytes 2-9 are timestamp
diff --git a/net/cert/multi_threaded_cert_verifier.cc b/net/cert/multi_threaded_cert_verifier.cc
index cce5207..a58f89e3 100644
--- a/net/cert/multi_threaded_cert_verifier.cc
+++ b/net/cert/multi_threaded_cert_verifier.cc
@@ -245,6 +245,7 @@
       flags &= ~CertVerifyProc::VERIFY_REV_CHECKING_ENABLED;
       flags &= ~CertVerifyProc::VERIFY_REV_CHECKING_REQUIRED_LOCAL_ANCHORS;
     }
+    DCHECK(config.crl_set);
     base::PostTaskWithTraitsAndReplyWithResult(
         FROM_HERE,
         {base::MayBlock(), base::TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN},
@@ -350,7 +351,12 @@
 
 MultiThreadedCertVerifier::MultiThreadedCertVerifier(
     scoped_refptr<CertVerifyProc> verify_proc)
-    : requests_(0), inflight_joins_(0), verify_proc_(verify_proc) {}
+    : config_id_(0),
+      requests_(0),
+      inflight_joins_(0),
+      verify_proc_(verify_proc) {
+  config_.crl_set = CRLSet::BuiltinCRLSet();
+}
 
 MultiThreadedCertVerifier::~MultiThreadedCertVerifier() {
   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
@@ -410,6 +416,8 @@
 void MultiThreadedCertVerifier::SetConfig(const CertVerifier::Config& config) {
   ++config_id_;
   config_ = config;
+  if (!config_.crl_set)
+    config_.crl_set = CRLSet::BuiltinCRLSet();
 
   // In C++17, this would be a .merge() call to combine |joinable_| into
   // |inflight_|.
@@ -428,12 +436,10 @@
     scoped_refptr<CertVerifyProc> verify_proc,
     VerifyCompleteCallback verify_complete_callback,
     bool should_record_histograms)
-    : config_id_(0),
-      requests_(0),
-      inflight_joins_(0),
-      verify_proc_(verify_proc),
-      verify_complete_callback_(std::move(verify_complete_callback)),
-      should_record_histograms_(should_record_histograms) {}
+    : MultiThreadedCertVerifier(verify_proc) {
+  verify_complete_callback_ = std::move(verify_complete_callback);
+  should_record_histograms_ = should_record_histograms;
+}
 
 std::unique_ptr<CertVerifierJob> MultiThreadedCertVerifier::RemoveJob(
     CertVerifierJob* job) {
diff --git a/net/cert/nss_cert_database_unittest.cc b/net/cert/nss_cert_database_unittest.cc
index e09361e..367ca2574 100644
--- a/net/cert/nss_cert_database_unittest.cc
+++ b/net/cert/nss_cert_database_unittest.cc
@@ -26,6 +26,7 @@
 #include "net/cert/cert_status_flags.h"
 #include "net/cert/cert_verify_proc_nss.h"
 #include "net/cert/cert_verify_result.h"
+#include "net/cert/crl_set.h"
 #include "net/cert/x509_certificate.h"
 #include "net/cert/x509_util_nss.h"
 #include "net/test/cert_test_util.h"
@@ -69,6 +70,7 @@
         crypto::ScopedPK11Slot(
             PK11_ReferenceSlot(test_nssdb_.slot())) /* private slot */));
     public_slot_ = cert_db_->GetPublicSlot();
+    crl_set_ = CRLSet::BuiltinCRLSet();
 
     // Test db should be empty at start of test.
     EXPECT_EQ(0U, ListCerts().size());
@@ -129,6 +131,7 @@
   const CertificateList empty_cert_list_ = CertificateList();
   crypto::ScopedTestNSSDB test_nssdb_;
   crypto::ScopedPK11Slot public_slot_;
+  scoped_refptr<CRLSet> crl_set_;
 };
 
 TEST_F(CertDatabaseNSSTest, ListCertsSync) {
@@ -571,8 +574,8 @@
   int flags = 0;
   CertVerifyResult verify_result;
   int error = verify_proc->Verify(x509_found_server_cert.get(), "127.0.0.1",
-                                  std::string(), flags, NULL, empty_cert_list_,
-                                  &verify_result);
+                                  std::string(), flags, crl_set_.get(),
+                                  empty_cert_list_, &verify_result);
   EXPECT_THAT(error, IsError(ERR_CERT_AUTHORITY_INVALID));
   EXPECT_EQ(CERT_STATUS_AUTHORITY_INVALID, verify_result.cert_status);
 }
@@ -602,8 +605,8 @@
   int flags = 0;
   CertVerifyResult verify_result;
   int error = verify_proc->Verify(x509_puny_cert.get(), "xn--wgv71a119e.com",
-                                  std::string(), flags, NULL, empty_cert_list_,
-                                  &verify_result);
+                                  std::string(), flags, crl_set_.get(),
+                                  empty_cert_list_, &verify_result);
   EXPECT_THAT(error, IsError(ERR_CERT_AUTHORITY_INVALID));
   EXPECT_EQ(CERT_STATUS_AUTHORITY_INVALID, verify_result.cert_status);
 }
@@ -634,8 +637,8 @@
   int flags = 0;
   CertVerifyResult verify_result;
   int error = verify_proc->Verify(x509_puny_cert.get(), "xn--wgv71a119e.com",
-                                  std::string(), flags, NULL, empty_cert_list_,
-                                  &verify_result);
+                                  std::string(), flags, crl_set_.get(),
+                                  empty_cert_list_, &verify_result);
   EXPECT_THAT(error, IsOk());
   EXPECT_EQ(0U, verify_result.cert_status);
 }
@@ -668,9 +671,9 @@
   scoped_refptr<CertVerifyProc> verify_proc(new CertVerifyProcNSS());
   int flags = 0;
   CertVerifyResult verify_result;
-  int error =
-      verify_proc->Verify(x509_server_cert.get(), "127.0.0.1", std::string(),
-                          flags, NULL, empty_cert_list_, &verify_result);
+  int error = verify_proc->Verify(x509_server_cert.get(), "127.0.0.1",
+                                  std::string(), flags, crl_set_.get(),
+                                  empty_cert_list_, &verify_result);
   EXPECT_THAT(error, IsOk());
   EXPECT_EQ(0U, verify_result.cert_status);
 }
@@ -708,9 +711,9 @@
   scoped_refptr<CertVerifyProc> verify_proc(new CertVerifyProcNSS());
   int flags = 0;
   CertVerifyResult verify_result;
-  int error =
-      verify_proc->Verify(x509_server_cert.get(), "127.0.0.1", std::string(),
-                          flags, NULL, empty_cert_list_, &verify_result);
+  int error = verify_proc->Verify(x509_server_cert.get(), "127.0.0.1",
+                                  std::string(), flags, crl_set_.get(),
+                                  empty_cert_list_, &verify_result);
   EXPECT_THAT(error, IsError(ERR_CERT_REVOKED));
   EXPECT_EQ(CERT_STATUS_REVOKED, verify_result.cert_status);
 }
@@ -757,9 +760,9 @@
   scoped_refptr<CertVerifyProc> verify_proc(new CertVerifyProcNSS());
   int flags = 0;
   CertVerifyResult verify_result;
-  int error =
-      verify_proc->Verify(x509_server_cert.get(), "127.0.0.1", std::string(),
-                          flags, NULL, empty_cert_list_, &verify_result);
+  int error = verify_proc->Verify(x509_server_cert.get(), "127.0.0.1",
+                                  std::string(), flags, crl_set_.get(),
+                                  empty_cert_list_, &verify_result);
   EXPECT_THAT(error, IsOk());
   EXPECT_EQ(0U, verify_result.cert_status);
 
@@ -782,9 +785,9 @@
 
   // Server cert should fail to verify.
   CertVerifyResult verify_result2;
-  error =
-      verify_proc->Verify(x509_server_cert.get(), "127.0.0.1", std::string(),
-                          flags, NULL, empty_cert_list_, &verify_result2);
+  error = verify_proc->Verify(x509_server_cert.get(), "127.0.0.1",
+                              std::string(), flags, crl_set_.get(),
+                              empty_cert_list_, &verify_result2);
   EXPECT_THAT(error, IsError(ERR_CERT_REVOKED));
   EXPECT_EQ(CERT_STATUS_REVOKED, verify_result2.cert_status);
 }
@@ -822,9 +825,9 @@
   scoped_refptr<CertVerifyProc> verify_proc(new CertVerifyProcNSS());
   int flags = 0;
   CertVerifyResult verify_result;
-  int error =
-      verify_proc->Verify(x509_server_cert.get(), "127.0.0.1", std::string(),
-                          flags, NULL, empty_cert_list_, &verify_result);
+  int error = verify_proc->Verify(x509_server_cert.get(), "127.0.0.1",
+                                  std::string(), flags, crl_set_.get(),
+                                  empty_cert_list_, &verify_result);
   EXPECT_THAT(error, IsOk());
   EXPECT_EQ(0U, verify_result.cert_status);
 
@@ -834,9 +837,9 @@
 
   // Server cert should fail to verify.
   CertVerifyResult verify_result2;
-  error =
-      verify_proc->Verify(x509_server_cert.get(), "127.0.0.1", std::string(),
-                          flags, NULL, empty_cert_list_, &verify_result2);
+  error = verify_proc->Verify(x509_server_cert.get(), "127.0.0.1",
+                              std::string(), flags, crl_set_.get(),
+                              empty_cert_list_, &verify_result2);
   EXPECT_THAT(error, IsError(ERR_CERT_AUTHORITY_INVALID));
   EXPECT_EQ(CERT_STATUS_AUTHORITY_INVALID, verify_result2.cert_status);
 }
@@ -884,9 +887,9 @@
   scoped_refptr<CertVerifyProc> verify_proc(new CertVerifyProcNSS());
   int flags = 0;
   CertVerifyResult verify_result;
-  int error =
-      verify_proc->Verify(x509_server_cert.get(), "127.0.0.1", std::string(),
-                          flags, NULL, empty_cert_list_, &verify_result);
+  int error = verify_proc->Verify(x509_server_cert.get(), "127.0.0.1",
+                                  std::string(), flags, crl_set_.get(),
+                                  empty_cert_list_, &verify_result);
   EXPECT_THAT(error, IsOk());
   EXPECT_EQ(0U, verify_result.cert_status);
 
@@ -896,9 +899,9 @@
 
   // Server cert should fail to verify.
   CertVerifyResult verify_result2;
-  error =
-      verify_proc->Verify(x509_server_cert.get(), "127.0.0.1", std::string(),
-                          flags, NULL, empty_cert_list_, &verify_result2);
+  error = verify_proc->Verify(x509_server_cert.get(), "127.0.0.1",
+                              std::string(), flags, crl_set_.get(),
+                              empty_cert_list_, &verify_result2);
   EXPECT_THAT(error, IsError(ERR_CERT_AUTHORITY_INVALID));
   EXPECT_EQ(CERT_STATUS_AUTHORITY_INVALID, verify_result2.cert_status);
 }
@@ -946,9 +949,9 @@
   scoped_refptr<CertVerifyProc> verify_proc(new CertVerifyProcNSS());
   int flags = 0;
   CertVerifyResult verify_result;
-  int error =
-      verify_proc->Verify(x509_server_cert.get(), "127.0.0.1", std::string(),
-                          flags, NULL, empty_cert_list_, &verify_result);
+  int error = verify_proc->Verify(x509_server_cert.get(), "127.0.0.1",
+                                  std::string(), flags, crl_set_.get(),
+                                  empty_cert_list_, &verify_result);
   EXPECT_THAT(error, IsError(ERR_CERT_REVOKED));
   EXPECT_EQ(CERT_STATUS_REVOKED, verify_result.cert_status);
 
@@ -958,9 +961,9 @@
 
   // Server cert should verify.
   CertVerifyResult verify_result2;
-  error =
-      verify_proc->Verify(x509_server_cert.get(), "127.0.0.1", std::string(),
-                          flags, NULL, empty_cert_list_, &verify_result2);
+  error = verify_proc->Verify(x509_server_cert.get(), "127.0.0.1",
+                              std::string(), flags, crl_set_.get(),
+                              empty_cert_list_, &verify_result2);
   EXPECT_THAT(error, IsOk());
   EXPECT_EQ(0U, verify_result2.cert_status);
 }
diff --git a/net/cert/test_root_certs_unittest.cc b/net/cert/test_root_certs_unittest.cc
index 022dd96..ef68752 100644
--- a/net/cert/test_root_certs_unittest.cc
+++ b/net/cert/test_root_certs_unittest.cc
@@ -2,13 +2,15 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+#include "net/cert/test_root_certs.h"
+
 #include "base/files/file_path.h"
 #include "build/build_config.h"
 #include "net/base/net_errors.h"
 #include "net/cert/cert_status_flags.h"
 #include "net/cert/cert_verify_proc.h"
 #include "net/cert/cert_verify_result.h"
-#include "net/cert/test_root_certs.h"
+#include "net/cert/crl_set.h"
 #include "net/cert/x509_certificate.h"
 #include "net/test/cert_test_util.h"
 #include "net/test/gtest_util.h"
@@ -89,7 +91,8 @@
   scoped_refptr<CertVerifyProc> verify_proc(CertVerifyProc::CreateDefault());
   int bad_status =
       verify_proc->Verify(test_cert.get(), "127.0.0.1", std::string(), flags,
-                          NULL, CertificateList(), &bad_verify_result);
+                          net::CRLSet::BuiltinCRLSet().get(), CertificateList(),
+                          &bad_verify_result);
   EXPECT_NE(OK, bad_status);
   EXPECT_NE(0u, bad_verify_result.cert_status & CERT_STATUS_AUTHORITY_INVALID);
 
@@ -101,9 +104,9 @@
   // Test that the certificate verification now succeeds, because the
   // TestRootCerts is successfully imbuing trust.
   CertVerifyResult good_verify_result;
-  int good_status =
-      verify_proc->Verify(test_cert.get(), "127.0.0.1", std::string(), flags,
-                          NULL, CertificateList(), &good_verify_result);
+  int good_status = verify_proc->Verify(
+      test_cert.get(), "127.0.0.1", std::string(), flags,
+      CRLSet::BuiltinCRLSet().get(), CertificateList(), &good_verify_result);
   EXPECT_THAT(good_status, IsOk());
   EXPECT_EQ(0u, good_verify_result.cert_status);
 
@@ -116,7 +119,8 @@
   CertVerifyResult restored_verify_result;
   int restored_status =
       verify_proc->Verify(test_cert.get(), "127.0.0.1", std::string(), flags,
-                          NULL, CertificateList(), &restored_verify_result);
+                          CRLSet::BuiltinCRLSet().get(), CertificateList(),
+                          &restored_verify_result);
   EXPECT_NE(OK, restored_status);
   EXPECT_NE(0u,
             restored_verify_result.cert_status & CERT_STATUS_AUTHORITY_INVALID);
diff --git a/net/cookies/canonical_cookie.cc b/net/cookies/canonical_cookie.cc
index aaa27709..3b03a50c 100644
--- a/net/cookies/canonical_cookie.cc
+++ b/net/cookies/canonical_cookie.cc
@@ -383,14 +383,14 @@
   // Don't include same-site cookies for cross-site requests.
   switch (SameSite()) {
     case CookieSameSite::STRICT_MODE:
-      if (options.same_site_cookie_mode() !=
-          CookieOptions::SameSiteCookieMode::INCLUDE_STRICT_AND_LAX) {
+      if (options.same_site_cookie_context() <
+          CookieOptions::SameSiteCookieContext::SAME_SITE_STRICT) {
         return CanonicalCookie::CookieInclusionStatus::EXCLUDE_SAMESITE_STRICT;
       }
       break;
     case CookieSameSite::LAX_MODE:
-      if (options.same_site_cookie_mode() ==
-          CookieOptions::SameSiteCookieMode::DO_NOT_INCLUDE) {
+      if (options.same_site_cookie_context() <
+          CookieOptions::SameSiteCookieContext::SAME_SITE_LAX) {
         return CanonicalCookie::CookieInclusionStatus::EXCLUDE_SAMESITE_LAX;
       }
       break;
diff --git a/net/cookies/canonical_cookie_unittest.cc b/net/cookies/canonical_cookie_unittest.cc
index 0b2420fe..2602b80 100644
--- a/net/cookies/canonical_cookie_unittest.cc
+++ b/net/cookies/canonical_cookie_unittest.cc
@@ -118,8 +118,8 @@
 
   // Test creating SameSite cookies.
   CookieOptions same_site_options;
-  same_site_options.set_same_site_cookie_mode(
-      CookieOptions::SameSiteCookieMode::INCLUDE_STRICT_AND_LAX);
+  same_site_options.set_same_site_cookie_context(
+      CookieOptions::SameSiteCookieContext::SAME_SITE_STRICT);
   cookie = CanonicalCookie::Create(url, "A=2; SameSite=Strict", creation_time,
                                    same_site_options);
   EXPECT_TRUE(cookie.get());
@@ -160,8 +160,8 @@
   std::unique_ptr<CanonicalCookie> cookie;
   CookieOptions options;
 
-  options.set_same_site_cookie_mode(
-      CookieOptions::SameSiteCookieMode::INCLUDE_STRICT_AND_LAX);
+  options.set_same_site_cookie_context(
+      CookieOptions::SameSiteCookieContext::SAME_SITE_STRICT);
 
   // Non-standard value for the SameSite attribute.
   cookie =
@@ -482,38 +482,38 @@
   std::unique_ptr<CanonicalCookie> cookie;
 
   // `SameSite=Strict` cookies are included for a URL only if the options'
-  // SameSiteCookieMode is INCLUDE_STRICT_AND_LAX.
+  // SameSiteCookieMode is SAME_SITE_STRICT.
   cookie = CanonicalCookie::Create(url, "A=2; SameSite=Strict", creation_time,
                                    options);
   EXPECT_EQ(CookieSameSite::STRICT_MODE, cookie->SameSite());
-  options.set_same_site_cookie_mode(
-      CookieOptions::SameSiteCookieMode::DO_NOT_INCLUDE);
+  options.set_same_site_cookie_context(
+      CookieOptions::SameSiteCookieContext::CROSS_SITE);
   EXPECT_EQ(cookie->IncludeForRequestURL(url, options),
             CanonicalCookie::CookieInclusionStatus::EXCLUDE_SAMESITE_STRICT);
-  options.set_same_site_cookie_mode(
-      CookieOptions::SameSiteCookieMode::INCLUDE_LAX);
+  options.set_same_site_cookie_context(
+      CookieOptions::SameSiteCookieContext::SAME_SITE_LAX);
   EXPECT_EQ(cookie->IncludeForRequestURL(url, options),
             CanonicalCookie::CookieInclusionStatus::EXCLUDE_SAMESITE_STRICT);
-  options.set_same_site_cookie_mode(
-      CookieOptions::SameSiteCookieMode::INCLUDE_STRICT_AND_LAX);
+  options.set_same_site_cookie_context(
+      CookieOptions::SameSiteCookieContext::SAME_SITE_STRICT);
   EXPECT_EQ(cookie->IncludeForRequestURL(url, options),
             CanonicalCookie::CookieInclusionStatus::INCLUDE);
 
   // `SameSite=Lax` cookies are included for a URL only if the options'
-  // SameSiteCookieMode is INCLUDE_STRICT_AND_LAX.
+  // SameSiteCookieMode is SAME_SITE_STRICT or SAME_SITE_LAX.
   cookie =
       CanonicalCookie::Create(url, "A=2; SameSite=Lax", creation_time, options);
   EXPECT_EQ(CookieSameSite::LAX_MODE, cookie->SameSite());
-  options.set_same_site_cookie_mode(
-      CookieOptions::SameSiteCookieMode::DO_NOT_INCLUDE);
+  options.set_same_site_cookie_context(
+      CookieOptions::SameSiteCookieContext::CROSS_SITE);
   EXPECT_EQ(cookie->IncludeForRequestURL(url, options),
             CanonicalCookie::CookieInclusionStatus::EXCLUDE_SAMESITE_LAX);
-  options.set_same_site_cookie_mode(
-      CookieOptions::SameSiteCookieMode::INCLUDE_LAX);
+  options.set_same_site_cookie_context(
+      CookieOptions::SameSiteCookieContext::SAME_SITE_LAX);
   EXPECT_EQ(cookie->IncludeForRequestURL(url, options),
             CanonicalCookie::CookieInclusionStatus::INCLUDE);
-  options.set_same_site_cookie_mode(
-      CookieOptions::SameSiteCookieMode::INCLUDE_STRICT_AND_LAX);
+  options.set_same_site_cookie_context(
+      CookieOptions::SameSiteCookieContext::SAME_SITE_STRICT);
   EXPECT_EQ(cookie->IncludeForRequestURL(url, options),
             CanonicalCookie::CookieInclusionStatus::INCLUDE);
 }
diff --git a/net/cookies/cookie_deletion_info.cc b/net/cookies/cookie_deletion_info.cc
index 88e5ad10..3dd42902 100644
--- a/net/cookies/cookie_deletion_info.cc
+++ b/net/cookies/cookie_deletion_info.cc
@@ -80,8 +80,8 @@
   // a particular URL.  These options will make sure that all
   // cookies associated with the URL are deleted.
   cookie_options.set_include_httponly();
-  cookie_options.set_same_site_cookie_mode(
-      net::CookieOptions::SameSiteCookieMode::INCLUDE_STRICT_AND_LAX);
+  cookie_options.set_same_site_cookie_context(
+      net::CookieOptions::SameSiteCookieContext::SAME_SITE_STRICT);
 }
 
 CookieDeletionInfo::CookieDeletionInfo(CookieDeletionInfo&& other) = default;
diff --git a/net/cookies/cookie_monster_change_dispatcher.cc b/net/cookies/cookie_monster_change_dispatcher.cc
index d60c888..39ccf3b 100644
--- a/net/cookies/cookie_monster_change_dispatcher.cc
+++ b/net/cookies/cookie_monster_change_dispatcher.cc
@@ -44,8 +44,8 @@
   // different options. For example, JavaScript observers will not be allowed to
   // see HTTP-only changes.
   options_.set_include_httponly();
-  options_.set_same_site_cookie_mode(
-      CookieOptions::SameSiteCookieMode::INCLUDE_STRICT_AND_LAX);
+  options_.set_same_site_cookie_context(
+      CookieOptions::SameSiteCookieContext::SAME_SITE_STRICT);
 }
 
 CookieMonsterChangeDispatcher::Subscription::~Subscription() {
diff --git a/net/cookies/cookie_options.cc b/net/cookies/cookie_options.cc
index f7b0426..135bf6d 100644
--- a/net/cookies/cookie_options.cc
+++ b/net/cookies/cookie_options.cc
@@ -11,7 +11,7 @@
 // Keep default values in sync with content/public/common/cookie_manager.mojom.
 CookieOptions::CookieOptions()
     : exclude_httponly_(true),
-      same_site_cookie_mode_(SameSiteCookieMode::DO_NOT_INCLUDE),
+      same_site_cookie_context_(SameSiteCookieContext::CROSS_SITE),
       update_access_time_(true),
       server_time_(),
       return_excluded_cookies_(false) {}
diff --git a/net/cookies/cookie_options.h b/net/cookies/cookie_options.h
index 6d8f3e4d..52d4fe54 100644
--- a/net/cookies/cookie_options.h
+++ b/net/cookies/cookie_options.h
@@ -16,10 +16,12 @@
 
 class NET_EXPORT CookieOptions {
  public:
-  enum class SameSiteCookieMode {
-    INCLUDE_STRICT_AND_LAX,
-    INCLUDE_LAX,
-    DO_NOT_INCLUDE
+  // Relation between the cookie and the navigational environment.
+  // Ordered from least to most trusted environment.
+  enum class SameSiteCookieContext {
+    CROSS_SITE,
+    SAME_SITE_LAX,
+    SAME_SITE_STRICT
   };
 
   // Creates a CookieOptions object which:
@@ -32,9 +34,8 @@
   // These settings can be altered by calling:
   //
   // * |set_{include,exclude}_httponly()|
-  // * |set_same_site_cookie_mode(
-  //        CookieOptions::SameSiteCookieMode::INCLUDE_STRICT_AND_LAX)|
-  // * |set_enforce_prefixes()|
+  // * |set_same_site_cookie_context(
+  //        CookieOptions::SameSiteCookieContext::SAME_SITE_STRICT)|
   // * |set_do_not_update_access_time()|
   CookieOptions();
 
@@ -42,12 +43,13 @@
   void set_include_httponly() { exclude_httponly_ = false; }
   bool exclude_httponly() const { return exclude_httponly_; }
 
-  // Default is to exclude 'same_site' cookies.
-  void set_same_site_cookie_mode(SameSiteCookieMode mode) {
-    same_site_cookie_mode_ = mode;
+  // How trusted is the current browser environment when it comes to accessing
+  // SameSite cookies. Default is not trusted, e.g. CROSS_SITE.
+  void set_same_site_cookie_context(SameSiteCookieContext context) {
+    same_site_cookie_context_ = context;
   }
-  SameSiteCookieMode same_site_cookie_mode() const {
-    return same_site_cookie_mode_;
+  SameSiteCookieContext same_site_cookie_context() const {
+    return same_site_cookie_context_;
   }
 
   // |server_time| indicates what the server sending us the Cookie thought the
@@ -69,7 +71,7 @@
 
  private:
   bool exclude_httponly_;
-  SameSiteCookieMode same_site_cookie_mode_;
+  SameSiteCookieContext same_site_cookie_context_;
   bool update_access_time_;
   base::Time server_time_;
   bool return_excluded_cookies_;
diff --git a/net/cookies/cookie_store.cc b/net/cookies/cookie_store.cc
index 2b2eb7e..64d6a6f 100644
--- a/net/cookies/cookie_store.cc
+++ b/net/cookies/cookie_store.cc
@@ -25,8 +25,8 @@
                                            GetCookieListCallback callback) {
   CookieOptions options;
   options.set_include_httponly();
-  options.set_same_site_cookie_mode(
-      CookieOptions::SameSiteCookieMode::INCLUDE_STRICT_AND_LAX);
+  options.set_same_site_cookie_context(
+      CookieOptions::SameSiteCookieContext::SAME_SITE_STRICT);
   options.set_do_not_update_access_time();
   GetCookieListWithOptionsAsync(url, options, std::move(callback));
 }
diff --git a/net/cookies/cookie_store_unittest.h b/net/cookies/cookie_store_unittest.h
index 34f8f23..5608fea 100644
--- a/net/cookies/cookie_store_unittest.h
+++ b/net/cookies/cookie_store_unittest.h
@@ -174,8 +174,8 @@
     GetCookieListCallback callback;
     CookieOptions options;
     options.set_include_httponly();
-    options.set_same_site_cookie_mode(
-        CookieOptions::SameSiteCookieMode::INCLUDE_STRICT_AND_LAX);
+    options.set_same_site_cookie_context(
+        CookieOptions::SameSiteCookieContext::SAME_SITE_STRICT);
     options.set_return_excluded_cookies();
     cs->GetCookieListWithOptionsAsync(
         url, options,
@@ -456,8 +456,8 @@
   // make that difficult.
   CookieOptions options;
   options.set_include_httponly();
-  options.set_same_site_cookie_mode(
-      CookieOptions::SameSiteCookieMode::INCLUDE_STRICT_AND_LAX);
+  options.set_same_site_cookie_context(
+      CookieOptions::SameSiteCookieContext::SAME_SITE_STRICT);
   options.set_do_not_update_access_time();
 
   CookieList cookies =
@@ -641,8 +641,8 @@
   // make that difficult.
   CookieOptions options;
   options.set_include_httponly();
-  options.set_same_site_cookie_mode(
-      CookieOptions::SameSiteCookieMode::INCLUDE_STRICT_AND_LAX);
+  options.set_same_site_cookie_context(
+      CookieOptions::SameSiteCookieContext::SAME_SITE_STRICT);
   options.set_do_not_update_access_time();
 
   CookieList cookies =
diff --git a/net/cookies/cookie_util.cc b/net/cookies/cookie_util.cc
index 72fbf5d..1da63d1 100644
--- a/net/cookies/cookie_util.cc
+++ b/net/cookies/cookie_util.cc
@@ -15,6 +15,7 @@
 #include "build/build_config.h"
 #include "net/base/registry_controlled_domains/registry_controlled_domain.h"
 #include "net/base/url_util.h"
+#include "net/http/http_util.h"
 #include "url/gurl.h"
 
 namespace net {
@@ -367,5 +368,68 @@
   return buffer;
 }
 
+CookieOptions::SameSiteCookieContext ComputeSameSiteContext(
+    const GURL& url,
+    const GURL& site_for_cookies,
+    const base::Optional<url::Origin>& initiator) {
+  if (registry_controlled_domains::SameDomainOrHost(
+          url, site_for_cookies,
+          registry_controlled_domains::INCLUDE_PRIVATE_REGISTRIES)) {
+    if (!initiator ||
+        registry_controlled_domains::SameDomainOrHost(
+            url, initiator.value(),
+            registry_controlled_domains::INCLUDE_PRIVATE_REGISTRIES)) {
+      return CookieOptions::SameSiteCookieContext::SAME_SITE_STRICT;
+    } else {
+      return CookieOptions::SameSiteCookieContext::SAME_SITE_LAX;
+    }
+  }
+  return CookieOptions::SameSiteCookieContext::CROSS_SITE;
+}
+
+CookieOptions::SameSiteCookieContext ComputeSameSiteContextForRequest(
+    const std::string& http_method,
+    const GURL& url,
+    const GURL& site_for_cookies,
+    const base::Optional<url::Origin>& initiator,
+    bool attach_same_site_cookies) {
+  // Set SameSiteCookieMode according to the rules laid out in
+  // https://tools.ietf.org/html/draft-ietf-httpbis-rfc6265bis-02:
+  //
+  // * Include both "strict" and "lax" same-site cookies if the request's
+  //   |url|, |initiator|, and |site_for_cookies| all have the same
+  //   registrable domain. Note: this also covers the case of a request
+  //   without an initiator (only happens for browser-initiated main frame
+  //   navigations).
+  //
+  // * Include only "lax" same-site cookies if the request's |URL| and
+  //   |site_for_cookies| have the same registrable domain, _and_ the
+  //   request's |http_method| is "safe" ("GET" or "HEAD").
+  //
+  //   This case should generally occur only for cross-site requests which
+  //   target a top-level browsing context.
+  //
+  // * Include both "strict" and "lax" same-site cookies if the request is
+  //   tagged with a flag allowing it and "lax" would have been allowed had
+  //   |http_method| been safe.
+  //
+  //   Note that this can be the case for requests initiated by extensions,
+  //   which need to behave as though they are made by the document itself,
+  //   but appear like cross-site ones.
+  //
+  // * Otherwise, do not include same-site cookies.
+  CookieOptions::SameSiteCookieContext same_site_context =
+      ComputeSameSiteContext(url, site_for_cookies, initiator);
+  if (same_site_context ==
+      CookieOptions::SameSiteCookieContext::SAME_SITE_LAX) {
+    if (attach_same_site_cookies)
+      same_site_context =
+          CookieOptions::SameSiteCookieContext::SAME_SITE_STRICT;
+    else if (!net::HttpUtil::IsMethodSafe(http_method))
+      same_site_context = CookieOptions::SameSiteCookieContext::CROSS_SITE;
+  }
+  return same_site_context;
+}
+
 }  // namespace cookie_util
 }  // namespace net
diff --git a/net/cookies/cookie_util.h b/net/cookies/cookie_util.h
index 2c9ed99d..706617d2 100644
--- a/net/cookies/cookie_util.h
+++ b/net/cookies/cookie_util.h
@@ -11,6 +11,8 @@
 
 #include "base/time/time.h"
 #include "net/base/net_export.h"
+#include "net/cookies/cookie_options.h"
+#include "url/origin.h"
 
 class GURL;
 
@@ -75,6 +77,40 @@
 NET_EXPORT std::string SerializeRequestCookieLine(
     const ParsedRequestCookies& parsed_cookies);
 
+// Determines which of the cookies for |url| can be accessed, with respect to
+// the SameSite attribute.
+//
+// |site_for_cookies| is the currently navigated to site that should be
+// considered "first-party" for cookies.
+//
+// |initiator| is the origin ultimately responsible for getting the request
+// issued; it may be different from |site_for_cookies| in that it may be some
+// other website that caused the navigation to |site_for_cookies| to occur.
+//
+// base::nullopt for |initiator| denotes that the navigation was initiated by
+// the user directly interacting with the browser UI, e.g. entering a URL
+// or selecting a bookmark.
+//
+// See also documentation for corresponding methods on net::URLRequest.
+NET_EXPORT CookieOptions::SameSiteCookieContext ComputeSameSiteContext(
+    const GURL& url,
+    const GURL& site_for_cookies,
+    const base::Optional<url::Origin>& initiator);
+
+// As above, but applying to a request. |http_method| is used to enforce
+// the requirement that, in a context that's lax same-site but not strict
+// same-site, SameSite=lax cookies be only sent when the method is "safe" in the
+// RFC7231 section 4.2.1 sense.
+//
+// This also applies the net feature |URLRequest::site_for_cookies|, which
+// upgrades SameSite=Lax level access to Strict-level access if on.
+NET_EXPORT CookieOptions::SameSiteCookieContext
+ComputeSameSiteContextForRequest(const std::string& http_method,
+                                 const GURL& url,
+                                 const GURL& site_for_cookies,
+                                 const base::Optional<url::Origin>& initiator,
+                                 bool attach_same_site_cookies);
+
 }  // namespace cookie_util
 }  // namespace net
 
diff --git a/net/cookies/cookie_util_unittest.cc b/net/cookies/cookie_util_unittest.cc
index 7812e05..7f02f28 100644
--- a/net/cookies/cookie_util_unittest.cc
+++ b/net/cookies/cookie_util_unittest.cc
@@ -251,6 +251,111 @@
   EXPECT_FALSE(cookie_util::IsDomainMatch(".example.de", "example.de.vu"));
 }
 
+TEST(CookieUtilTest, TestComputeSameSiteContext) {
+  // |site_for_cookies| not matching the URL -> it's cross-site.
+  EXPECT_EQ(CookieOptions::SameSiteCookieContext::CROSS_SITE,
+            cookie_util::ComputeSameSiteContext(GURL("http://example.com"),
+                                                GURL("http://notexample.com"),
+                                                base::nullopt /*initiator*/));
+
+  EXPECT_EQ(CookieOptions::SameSiteCookieContext::CROSS_SITE,
+            cookie_util::ComputeSameSiteContext(
+                GURL("http://example.com"), GURL("http://notexample.com"),
+                url::Origin::Create(GURL("http://example.com"))));
+
+  EXPECT_EQ(CookieOptions::SameSiteCookieContext::CROSS_SITE,
+            cookie_util::ComputeSameSiteContext(
+                GURL("http://a.com"), GURL("http://b.com"),
+                url::Origin::Create(GURL("http://from-elsewhere.com"))));
+
+  // Same |site_for_cookies|, but not |initiator| -> it's same-site lax.
+  EXPECT_EQ(CookieOptions::SameSiteCookieContext::SAME_SITE_LAX,
+            cookie_util::ComputeSameSiteContext(
+                GURL("http://example.com"), GURL("http://example.com"),
+                url::Origin::Create(GURL("http://from-elsewhere.com"))));
+
+  // This isn't a full on origin check --- subdomains and different schema are
+  // accepted.
+  EXPECT_EQ(CookieOptions::SameSiteCookieContext::SAME_SITE_LAX,
+            cookie_util::ComputeSameSiteContext(
+                GURL("https://example.com"), GURL("http://example.com"),
+                url::Origin::Create(GURL("http://from-elsewhere.com"))));
+
+  EXPECT_EQ(CookieOptions::SameSiteCookieContext::SAME_SITE_LAX,
+            cookie_util::ComputeSameSiteContext(
+                GURL("http://sub.example.com"), GURL("http://sub2.example.com"),
+                url::Origin::Create(GURL("http://from-elsewhere.com"))));
+
+  EXPECT_EQ(
+      CookieOptions::SameSiteCookieContext::SAME_SITE_LAX,
+      cookie_util::ComputeSameSiteContext(
+          GURL("http://sub.example.com"), GURL("http://sub.example.com:8080"),
+          url::Origin::Create(GURL("http://from-elsewhere.com"))));
+
+  // nullopt |initiator| is trusted for purposes of strict, an opaque one isn't.
+  EXPECT_EQ(CookieOptions::SameSiteCookieContext::SAME_SITE_STRICT,
+            cookie_util::ComputeSameSiteContext(
+                GURL("http://example.com"), GURL("http://example.com"),
+                url::Origin::Create(GURL("http://example.com"))));
+
+  EXPECT_EQ(CookieOptions::SameSiteCookieContext::SAME_SITE_STRICT,
+            cookie_util::ComputeSameSiteContext(GURL("http://example.com"),
+                                                GURL("http://example.com"),
+                                                base::nullopt /*initiator*/));
+
+  EXPECT_EQ(CookieOptions::SameSiteCookieContext::SAME_SITE_LAX,
+            cookie_util::ComputeSameSiteContext(GURL("http://example.com"),
+                                                GURL("http://example.com"),
+                                                url::Origin()));
+}
+
+TEST(CookieUtilTest, ComputeSameSiteContextForRequest) {
+  EXPECT_EQ(
+      CookieOptions::SameSiteCookieContext::CROSS_SITE,
+      cookie_util::ComputeSameSiteContextForRequest(
+          "GET", GURL("http://example.com"), GURL("http://notexample.com"),
+          base::nullopt /*initiator*/, false /*attach_same_site_cookies*/));
+
+  // |attach_same_site_cookies| = true bypasses method and initiator
+  // checks, but not the |site_for_cookies| one.
+  EXPECT_EQ(CookieOptions::SameSiteCookieContext::SAME_SITE_STRICT,
+            cookie_util::ComputeSameSiteContextForRequest(
+                "GET", GURL("http://example.com"), GURL("http://example.com"),
+                url::Origin::Create(GURL("http://from-elsewhere.com")),
+                true /*attach_same_site_cookies*/));
+
+  EXPECT_EQ(CookieOptions::SameSiteCookieContext::SAME_SITE_STRICT,
+            cookie_util::ComputeSameSiteContextForRequest(
+                "POST", GURL("http://example.com"), GURL("http://example.com"),
+                url::Origin::Create(GURL("http://from-elsewhere.com")),
+                true /*attach_same_site_cookies*/));
+
+  EXPECT_EQ(CookieOptions::SameSiteCookieContext::CROSS_SITE,
+            cookie_util::ComputeSameSiteContextForRequest(
+                "GET", GURL("http://example.com"), GURL("http://question.com"),
+                url::Origin::Create(GURL("http://from-elsewhere.com")),
+                true /*attach_same_site_cookies*/));
+
+  // Normally, lax requests also require a safe method.
+  EXPECT_EQ(CookieOptions::SameSiteCookieContext::SAME_SITE_LAX,
+            cookie_util::ComputeSameSiteContextForRequest(
+                "GET", GURL("http://example.com"), GURL("http://example.com"),
+                url::Origin::Create(GURL("http://from-elsewhere.com")),
+                false /*attach_same_site_cookies*/));
+
+  EXPECT_EQ(CookieOptions::SameSiteCookieContext::SAME_SITE_LAX,
+            cookie_util::ComputeSameSiteContextForRequest(
+                "HEAD", GURL("http://example.com"), GURL("http://example.com"),
+                url::Origin::Create(GURL("http://from-elsewhere.com")),
+                false /*attach_same_site_cookies*/));
+
+  EXPECT_EQ(CookieOptions::SameSiteCookieContext::CROSS_SITE,
+            cookie_util::ComputeSameSiteContextForRequest(
+                "POST", GURL("http://example.com"), GURL("http://example.com"),
+                url::Origin::Create(GURL("http://from-elsewhere.com")),
+                false /*attach_same_site_cookies*/));
+}
+
 }  // namespace
 
 }  // namespace net
diff --git a/net/dns/address_sorter_posix_unittest.cc b/net/dns/address_sorter_posix_unittest.cc
index 8f34bc9..698cbe9 100644
--- a/net/dns/address_sorter_posix_unittest.cc
+++ b/net/dns/address_sorter_posix_unittest.cc
@@ -159,14 +159,6 @@
     return nullptr;
   }
   std::unique_ptr<SSLClientSocket> CreateSSLClientSocket(
-      std::unique_ptr<ClientSocketHandle>,
-      const HostPortPair&,
-      const SSLConfig&,
-      const SSLClientSocketContext&) override {
-    NOTIMPLEMENTED();
-    return std::unique_ptr<SSLClientSocket>();
-  }
-  std::unique_ptr<SSLClientSocket> CreateSSLClientSocket(
       std::unique_ptr<StreamSocket>,
       const HostPortPair&,
       const SSLConfig&,
@@ -175,21 +167,6 @@
     return std::unique_ptr<SSLClientSocket>();
   }
   std::unique_ptr<ProxyClientSocket> CreateProxyClientSocket(
-      std::unique_ptr<ClientSocketHandle> transport_socket,
-      const std::string& user_agent,
-      const HostPortPair& endpoint,
-      const ProxyServer& proxy_server,
-      HttpAuthController* http_auth_controller,
-      bool tunnel,
-      bool using_spdy,
-      NextProto negotiated_protocol,
-      ProxyDelegate* proxy_delegate,
-      bool is_https_proxy,
-      const NetworkTrafficAnnotationTag& traffic_annotation) override {
-    NOTIMPLEMENTED();
-    return nullptr;
-  }
-  std::unique_ptr<ProxyClientSocket> CreateProxyClientSocket(
       std::unique_ptr<StreamSocket> stream_socket,
       const std::string& user_agent,
       const HostPortPair& endpoint,
diff --git a/net/dns/dns_session_unittest.cc b/net/dns/dns_session_unittest.cc
index 5789ed97..3889439 100644
--- a/net/dns/dns_session_unittest.cc
+++ b/net/dns/dns_session_unittest.cc
@@ -45,7 +45,7 @@
   }
 
   std::unique_ptr<SSLClientSocket> CreateSSLClientSocket(
-      std::unique_ptr<ClientSocketHandle> transport_socket,
+      std::unique_ptr<StreamSocket> stream_socket,
       const HostPortPair& host_and_port,
       const SSLConfig& ssl_config,
       const SSLClientSocketContext& context) override {
@@ -53,31 +53,6 @@
     return std::unique_ptr<SSLClientSocket>();
   }
 
-  std::unique_ptr<SSLClientSocket> CreateSSLClientSocket(
-      std::unique_ptr<StreamSocket> nested_socket,
-      const HostPortPair& host_and_port,
-      const SSLConfig& ssl_config,
-      const SSLClientSocketContext& context) override {
-    NOTIMPLEMENTED();
-    return std::unique_ptr<SSLClientSocket>();
-  }
-
-  std::unique_ptr<ProxyClientSocket> CreateProxyClientSocket(
-      std::unique_ptr<ClientSocketHandle> transport_socket,
-      const std::string& user_agent,
-      const HostPortPair& endpoint,
-      const ProxyServer& proxy_server,
-      HttpAuthController* http_auth_controller,
-      bool tunnel,
-      bool using_spdy,
-      NextProto negotiated_protocol,
-      ProxyDelegate* proxy_delegate,
-      bool is_https_proxy,
-      const NetworkTrafficAnnotationTag& traffic_annotation) override {
-    NOTIMPLEMENTED();
-    return nullptr;
-  }
-
   std::unique_ptr<ProxyClientSocket> CreateProxyClientSocket(
       std::unique_ptr<StreamSocket> stream_socket,
       const std::string& user_agent,
diff --git a/net/dns/fuzzed_host_resolver.cc b/net/dns/fuzzed_host_resolver.cc
index 9360fb1..702bff01 100644
--- a/net/dns/fuzzed_host_resolver.cc
+++ b/net/dns/fuzzed_host_resolver.cc
@@ -33,7 +33,6 @@
 #include "net/dns/public/util.h"
 #include "net/log/net_log_with_source.h"
 #include "net/socket/datagram_server_socket.h"
-#include "net/socket/fuzzed_datagram_client_socket.h"
 
 namespace net {
 
@@ -284,22 +283,11 @@
   explicit FuzzedMdnsSocketFactory(base::FuzzedDataProvider* data_provider)
       : data_provider_(data_provider) {}
 
-  void CreateSocketPairs(
-      std::vector<MDnsSendRecvSocketPair>* socket_pairs) override {
-    // TODO(qingsi): Fuzz with 0 to 4 socket pairs after HostResolverImp can
-    // handle the false return from MDnsConnection::Init with zero socket pair.
-    int num_socket_pairs = data_provider_->ConsumeIntegralInRange(1, 4);
-    for (int i = 0; i < num_socket_pairs; ++i) {
-      auto send_socket =
-          std::make_unique<FuzzedDatagramClientSocket>(data_provider_);
-      AddressFamily address_family = data_provider_->ConsumeBool()
-                                         ? ADDRESS_FAMILY_IPV4
-                                         : ADDRESS_FAMILY_IPV6;
-      send_socket->Connect(dns_util::GetMdnsGroupEndPoint(address_family));
-      auto recv_socket = std::make_unique<FuzzedMdnsSocket>(data_provider_);
-      socket_pairs->push_back(
-          std::make_pair(std::move(send_socket), std::move(recv_socket)));
-    }
+  void CreateSockets(
+      std::vector<std::unique_ptr<DatagramServerSocket>>* sockets) override {
+    int num_sockets = data_provider_->ConsumeIntegralInRange(1, 4);
+    for (int i = 0; i < num_sockets; ++i)
+      sockets->push_back(std::make_unique<FuzzedMdnsSocket>(data_provider_));
   }
 
  private:
diff --git a/net/dns/mdns_client.cc b/net/dns/mdns_client.cc
index d1be032..7b17874 100644
--- a/net/dns/mdns_client.cc
+++ b/net/dns/mdns_client.cc
@@ -4,8 +4,6 @@
 
 #include "net/dns/mdns_client.h"
 
-#include <utility>
-
 #include "net/base/address_family.h"
 #include "net/base/net_errors.h"
 #include "net/base/network_interfaces.h"
@@ -18,22 +16,13 @@
 
 namespace {
 
-int BindSendSocket(AddressFamily address_family,
-                   uint32_t interface_index,
-                   DatagramClientSocket* socket) {
-  int rv = socket->SetMulticastInterface(interface_index);
-  DCHECK_EQ(OK, rv);
-  return socket->Connect(dns_util::GetMdnsGroupEndPoint(address_family));
-}
-
-int BindRecvSocket(AddressFamily address_family,
-                   uint32_t interface_index,
-                   DatagramServerSocket* socket) {
+int Bind(AddressFamily address_family,
+         uint32_t interface_index,
+         DatagramServerSocket* socket) {
   socket->AllowAddressSharingForMulticast();
-  int rv = socket->SetMulticastInterface(interface_index);
-  DCHECK_EQ(OK, rv);
+  socket->SetMulticastInterface(interface_index);
 
-  rv = socket->Listen(dns_util::GetMdnsReceiveEndPoint(address_family));
+  int rv = socket->Listen(dns_util::GetMdnsReceiveEndPoint(address_family));
   if (rv < OK)
     return rv;
 
@@ -75,26 +64,20 @@
   return interfaces;
 }
 
-MDnsSendRecvSocketPair CreateAndBindMDnsSocketPair(AddressFamily address_family,
-                                                   uint32_t interface_index,
-                                                   NetLog* net_log) {
-  auto send_socket = std::make_unique<UDPClientSocket>(
-      DatagramSocket::RANDOM_BIND, net_log, NetLogSource());
-  int rv = BindSendSocket(address_family, interface_index, send_socket.get());
-  if (rv != OK) {
-    VLOG(1) << "MDNS send socket bind failed, address_family=" << address_family
-            << ", error=" << rv;
-    return std::make_pair(nullptr, nullptr);
-  }
+std::unique_ptr<DatagramServerSocket> CreateAndBindMDnsSocket(
+    AddressFamily address_family,
+    uint32_t interface_index,
+    NetLog* net_log) {
+  std::unique_ptr<DatagramServerSocket> socket(
+      new UDPServerSocket(net_log, NetLogSource()));
 
-  auto recv_socket = std::make_unique<UDPServerSocket>(net_log, NetLogSource());
-  rv = BindRecvSocket(address_family, interface_index, recv_socket.get());
+  int rv = Bind(address_family, interface_index, socket.get());
   if (rv != OK) {
-    VLOG(1) << "MDNS recv socket bind failed, address_family=" << address_family
+    socket.reset();
+    VLOG(1) << "MDNS bind failed, address_family=" << address_family
             << ", error=" << rv;
-    return std::make_pair(nullptr, nullptr);
   }
-  return std::make_pair(std::move(send_socket), std::move(recv_socket));
+  return socket;
 }
 
 }  // namespace net
diff --git a/net/dns/mdns_client.h b/net/dns/mdns_client.h
index 3cba46b..89011cc 100644
--- a/net/dns/mdns_client.h
+++ b/net/dns/mdns_client.h
@@ -22,14 +22,9 @@
 namespace net {
 
 class DatagramServerSocket;
-class DatagramClientSocket;
 class NetLog;
 class RecordParsed;
 
-typedef std::pair<std::unique_ptr<DatagramClientSocket>,
-                  std::unique_ptr<DatagramServerSocket>>
-    MDnsSendRecvSocketPair;
-
 // Represents a one-time record lookup. A transaction takes one
 // associated callback (see |MDnsClient::CreateTransaction|) and calls it
 // whenever a matching record has been found, either from the cache or
@@ -145,8 +140,8 @@
 class NET_EXPORT MDnsSocketFactory {
  public:
   virtual ~MDnsSocketFactory() {}
-  virtual void CreateSocketPairs(
-      std::vector<MDnsSendRecvSocketPair>* socket_pairs) = 0;
+  virtual void CreateSockets(
+      std::vector<std::unique_ptr<DatagramServerSocket>>* sockets) = 0;
 
   static std::unique_ptr<MDnsSocketFactory> CreateDefault();
 };
@@ -186,19 +181,31 @@
   static std::unique_ptr<MDnsClient> CreateDefault();
 };
 
+// Gets the endpoint for the multicast group a socket should join to receive
+// MDNS messages. Such sockets should also bind to the endpoint from
+// GetMDnsReceiveEndPoint().
+//
+// This is also the endpoint messages should be sent to to send MDNS messages.
+NET_EXPORT IPEndPoint GetMDnsGroupEndPoint(AddressFamily address_family);
+
+// Gets the endpoint sockets should be bound to to receive MDNS messages. Such
+// sockets should also join the multicast group from GetMDnsGroupEndPoint().
+NET_EXPORT IPEndPoint GetMDnsReceiveEndPoint(AddressFamily address_family);
+
 typedef std::vector<std::pair<uint32_t, AddressFamily>>
     InterfaceIndexFamilyList;
 // Returns pairs of interface and address family to bind. Current
 // implementation returns unique list of all available interfaces.
 NET_EXPORT InterfaceIndexFamilyList GetMDnsInterfacesToBind();
 
-// Creates a pair of sockets for sending and receiving, binds them to mDNS
-// endpoints, and sets their multicast interfaces and joins the multicast group
-// on for |interface_index|. Returns NULL if failed.
-NET_EXPORT MDnsSendRecvSocketPair
-CreateAndBindMDnsSocketPair(AddressFamily address_family,
-                            uint32_t interface_index,
-                            NetLog* net_log);
+// Create sockets, binds socket to MDns endpoint, and sets multicast interface
+// and joins multicast group on for |interface_index|.
+// Returns NULL if failed.
+NET_EXPORT std::unique_ptr<DatagramServerSocket> CreateAndBindMDnsSocket(
+    AddressFamily address_family,
+    uint32_t interface_index,
+    NetLog* net_log);
+
 }  // namespace net
 
 #endif  // NET_DNS_MDNS_CLIENT_H_
diff --git a/net/dns/mdns_client_impl.cc b/net/dns/mdns_client_impl.cc
index cfcd6d3..164fdbf 100644
--- a/net/dns/mdns_client_impl.cc
+++ b/net/dns/mdns_client_impl.cc
@@ -39,75 +39,39 @@
 const double kListenerRefreshRatio1 = 0.85;
 const double kListenerRefreshRatio2 = 0.95;
 
-constexpr net::NetworkTrafficAnnotationTag kTrafficAnnotation =
-    net::DefineNetworkTrafficAnnotation("mdns_client", R"(
-        semantics {
-          sender: "mDNS Client"
-          description:
-            "mDNS client implements a multicast DNS querier as defined in RFC "
-            "6762."
-          trigger:
-            "Any network request that may require mDNS resolution, including "
-            "navigations and service discovery."
-          data:
-            "Domain name with the TLD .local that needs resolution."
-          destination: OTHER
-          destination_other:
-            "The connection is made to the mDNS multicast group within the "
-            "subnet where the user resides."
-          }
-        policy {
-          cookies_allowed: NO
-          setting:
-            "No setting for this feature."
-          policy_exception_justification:
-            "This feature is enabled on most platforms and is core networking "
-            "functionality on local networks."
-        })");
-
 }  // namespace
 
-void MDnsSocketFactoryImpl::CreateSocketPairs(
-    std::vector<MDnsSendRecvSocketPair>* socket_pairs) {
+void MDnsSocketFactoryImpl::CreateSockets(
+    std::vector<std::unique_ptr<DatagramServerSocket>>* sockets) {
   InterfaceIndexFamilyList interfaces(GetMDnsInterfacesToBind());
   for (size_t i = 0; i < interfaces.size(); ++i) {
     DCHECK(interfaces[i].second == ADDRESS_FAMILY_IPV4 ||
            interfaces[i].second == ADDRESS_FAMILY_IPV6);
-    MDnsSendRecvSocketPair socket_pair(CreateAndBindMDnsSocketPair(
+    std::unique_ptr<DatagramServerSocket> socket(CreateAndBindMDnsSocket(
         interfaces[i].second, interfaces[i].first, net_log_));
-    const auto& send_socket = socket_pair.first;
-    const auto& recv_socket = socket_pair.second;
-    if (send_socket && recv_socket)
-      socket_pairs->push_back(std::move(socket_pair));
+    if (socket)
+      sockets->push_back(std::move(socket));
   }
 }
 
-MDnsConnection::SocketHandler::SocketHandler(MDnsSendRecvSocketPair socket_pair,
-                                             MDnsConnection* connection)
-    : send_socket_(std::move(socket_pair.first)),
-      recv_socket_(std::move(socket_pair.second)),
+MDnsConnection::SocketHandler::SocketHandler(
+    std::unique_ptr<DatagramServerSocket> socket,
+    MDnsConnection* connection)
+    : socket_(std::move(socket)),
       connection_(connection),
       response_(dns_protocol::kMaxMulticastSize),
-      send_in_progress_(false) {
-  DCHECK(send_socket_);
-  DCHECK(recv_socket_);
-}
+      send_in_progress_(false) {}
 
 MDnsConnection::SocketHandler::~SocketHandler() = default;
 
 int MDnsConnection::SocketHandler::Start() {
   IPEndPoint end_point;
-  int rv = recv_socket_->GetLocalAddress(&end_point);
+  int rv = socket_->GetLocalAddress(&end_point);
   if (rv != OK)
     return rv;
-  const AddressFamily af = end_point.GetFamily();
-#ifdef DEBUG
-  DCHECK(af == net::ADDRESS_FAMILY_IPV4 || af == net::ADDRESS_FAMILY_IPV6);
-  net::IPEndPoint send_socket_end_point;
-  DCHECK(send_socket_->GetLocalAddress(&send_socket_end_point));
-  DCHECK_EQ(af, send_socket_end_point.GetFamily());
-#endif
-  multicast_addr_ = dns_util::GetMdnsGroupEndPoint(af);
+  DCHECK(end_point.GetFamily() == ADDRESS_FAMILY_IPV4 ||
+         end_point.GetFamily() == ADDRESS_FAMILY_IPV6);
+  multicast_addr_ = dns_util::GetMdnsGroupEndPoint(end_point.GetFamily());
   return DoLoop(0);
 }
 
@@ -116,7 +80,7 @@
     if (rv > 0)
       connection_->OnDatagramReceived(&response_, recv_addr_, rv);
 
-    rv = recv_socket_->RecvFrom(
+    rv = socket_->RecvFrom(
         response_.io_buffer(), response_.io_buffer_size(), &recv_addr_,
         base::Bind(&MDnsConnection::SocketHandler::OnDatagramReceived,
                    base::Unretained(this)));
@@ -142,11 +106,11 @@
     send_queue_.push(std::make_pair(buffer, size));
     return;
   }
-  int rv =
-      send_socket_->Write(buffer.get(), size,
-                          base::Bind(&MDnsConnection::SocketHandler::SendDone,
-                                     base::Unretained(this)),
-                          kTrafficAnnotation);
+  int rv = socket_->SendTo(buffer.get(),
+                           size,
+                           multicast_addr_,
+                           base::Bind(&MDnsConnection::SocketHandler::SendDone,
+                                      base::Unretained(this)));
   if (rv == ERR_IO_PENDING) {
     send_in_progress_ = true;
   } else if (rv < OK) {
@@ -173,12 +137,12 @@
 MDnsConnection::~MDnsConnection() = default;
 
 bool MDnsConnection::Init(MDnsSocketFactory* socket_factory) {
-  std::vector<MDnsSendRecvSocketPair> socket_pairs;
-  socket_factory->CreateSocketPairs(&socket_pairs);
+  std::vector<std::unique_ptr<DatagramServerSocket>> sockets;
+  socket_factory->CreateSockets(&sockets);
 
-  for (auto& send_recv_sockets : socket_pairs) {
+  for (std::unique_ptr<DatagramServerSocket>& socket : sockets) {
     socket_handlers_.push_back(std::make_unique<MDnsConnection::SocketHandler>(
-        std::move(send_recv_sockets), this));
+        std::move(socket), this));
   }
 
   // All unbound sockets need to be bound before processing untrusted input.
diff --git a/net/dns/mdns_client_impl.h b/net/dns/mdns_client_impl.h
index 4a3ac88..5627193 100644
--- a/net/dns/mdns_client_impl.h
+++ b/net/dns/mdns_client_impl.h
@@ -23,9 +23,7 @@
 #include "net/base/net_export.h"
 #include "net/dns/mdns_cache.h"
 #include "net/dns/mdns_client.h"
-#include "net/socket/datagram_client_socket.h"
 #include "net/socket/datagram_server_socket.h"
-#include "net/socket/udp_client_socket.h"
 #include "net/socket/udp_server_socket.h"
 #include "net/socket/udp_socket.h"
 
@@ -44,8 +42,8 @@
   explicit MDnsSocketFactoryImpl(NetLog* net_log) : net_log_(net_log) {}
   ~MDnsSocketFactoryImpl() override {}
 
-  void CreateSocketPairs(
-      std::vector<MDnsSendRecvSocketPair>* socket_pairs) override;
+  void CreateSockets(
+      std::vector<std::unique_ptr<DatagramServerSocket>>* sockets) override;
 
  private:
   NetLog* const net_log_;
@@ -75,7 +73,7 @@
  private:
   class SocketHandler {
    public:
-    SocketHandler(MDnsSendRecvSocketPair socket_pair,
+    SocketHandler(std::unique_ptr<DatagramServerSocket> socket,
                   MDnsConnection* connection);
     ~SocketHandler();
 
@@ -89,8 +87,7 @@
     // Callback for when sending a query has finished.
     void SendDone(int rv);
 
-    std::unique_ptr<DatagramClientSocket> send_socket_;
-    std::unique_ptr<DatagramServerSocket> recv_socket_;
+    std::unique_ptr<DatagramServerSocket> socket_;
     MDnsConnection* connection_;
     IPEndPoint recv_addr_;
     DnsResponse response_;
diff --git a/net/dns/mdns_client_unittest.cc b/net/dns/mdns_client_unittest.cc
index 6a8f032..9b95172d 100644
--- a/net/dns/mdns_client_unittest.cc
+++ b/net/dns/mdns_client_unittest.cc
@@ -31,7 +31,6 @@
 #include "testing/gtest/include/gtest/gtest.h"
 
 using ::testing::_;
-using ::testing::DoAll;
 using ::testing::Exactly;
 using ::testing::IgnoreResult;
 using ::testing::Invoke;
@@ -45,12 +44,6 @@
 
 namespace {
 
-ACTION_TEMPLATE(MoveArgPointee,
-                HAS_1_TEMPLATE_PARAMS(int, k),
-                AND_1_VALUE_PARAMS(out)) {
-  *out = std::move(*::testing::get<k>(args));
-}
-
 const uint8_t kSamplePacket1[] = {
     // Header
     0x00, 0x00,  // ID is zeroed out
@@ -1110,18 +1103,18 @@
 // This is a simplifying assumption based on the way the code works now.
 class SimpleMockSocketFactory : public MDnsSocketFactory {
  public:
-  void CreateSocketPairs(
-      std::vector<MDnsSendRecvSocketPair>* socket_pairs) override {
-    socket_pairs->clear();
-    socket_pairs->swap(socket_pairs_);
+  void CreateSockets(
+      std::vector<std::unique_ptr<DatagramServerSocket>>* sockets) override {
+    sockets->clear();
+    sockets->swap(sockets_);
   }
 
-  void PushSocketPair(MDnsSendRecvSocketPair socket_pair) {
-    socket_pairs_.push_back(std::move(socket_pair));
+  void PushSocket(std::unique_ptr<DatagramServerSocket> socket) {
+    sockets_.push_back(std::move(socket));
   }
 
  private:
-  std::vector<MDnsSendRecvSocketPair> socket_pairs_;
+  std::vector<std::unique_ptr<DatagramServerSocket>> sockets_;
 };
 
 class MockMDnsConnectionDelegate : public MDnsConnection::Delegate {
@@ -1143,20 +1136,10 @@
  protected:
   // Follow successful connection initialization.
   void SetUp() override {
-    auto send_socket_ipv4 = std::make_unique<MockMDnsDatagramClientSocket>();
-    auto recv_socket_ipv4 =
-        std::make_unique<MockMDnsDatagramServerSocket>(ADDRESS_FAMILY_IPV4);
-    auto send_socket_ipv6 = std::make_unique<MockMDnsDatagramClientSocket>();
-    auto recv_socket_ipv6 =
-        std::make_unique<MockMDnsDatagramServerSocket>(ADDRESS_FAMILY_IPV6);
-    send_socket_ipv4_ = send_socket_ipv4.get();
-    recv_socket_ipv4_ = recv_socket_ipv4.get();
-    send_socket_ipv6_ = send_socket_ipv6.get();
-    recv_socket_ipv6_ = recv_socket_ipv6.get();
-    factory_.PushSocketPair(std::make_pair(std::move(send_socket_ipv4),
-                                           std::move(recv_socket_ipv4)));
-    factory_.PushSocketPair(std::make_pair(std::move(send_socket_ipv6),
-                                           std::move(recv_socket_ipv6)));
+    socket_ipv4_ = new MockMDnsDatagramServerSocket(ADDRESS_FAMILY_IPV4);
+    socket_ipv6_ = new MockMDnsDatagramServerSocket(ADDRESS_FAMILY_IPV6);
+    factory_.PushSocket(base::WrapUnique(socket_ipv6_));
+    factory_.PushSocket(base::WrapUnique(socket_ipv4_));
     sample_packet_ = MakeString(kSamplePacket1, sizeof(kSamplePacket1));
     sample_buffer_ = base::MakeRefCounted<StringIOBuffer>(sample_packet_);
   }
@@ -1167,10 +1150,8 @@
 
   StrictMock<MockMDnsConnectionDelegate> delegate_;
 
-  MockMDnsDatagramClientSocket* send_socket_ipv4_;
-  MockMDnsDatagramServerSocket* recv_socket_ipv4_;
-  MockMDnsDatagramClientSocket* send_socket_ipv6_;
-  MockMDnsDatagramServerSocket* recv_socket_ipv6_;
+  MockMDnsDatagramServerSocket* socket_ipv4_;
+  MockMDnsDatagramServerSocket* socket_ipv6_;
   SimpleMockSocketFactory factory_;
   MDnsConnection connection_;
   TestCompletionCallback callback_;
@@ -1179,12 +1160,12 @@
 };
 
 TEST_F(MDnsConnectionTest, ReceiveSynchronous) {
-  recv_socket_ipv6_->SetResponsePacket(sample_packet_);
-  EXPECT_CALL(*recv_socket_ipv4_, RecvFromInternal(_, _, _, _))
+  socket_ipv6_->SetResponsePacket(sample_packet_);
+  EXPECT_CALL(*socket_ipv4_, RecvFromInternal(_, _, _, _))
       .WillOnce(Return(ERR_IO_PENDING));
-  EXPECT_CALL(*recv_socket_ipv6_, RecvFromInternal(_, _, _, _))
-      .WillOnce(Invoke(recv_socket_ipv6_,
-                       &MockMDnsDatagramServerSocket::HandleRecvNow))
+  EXPECT_CALL(*socket_ipv6_, RecvFromInternal(_, _, _, _))
+      .WillOnce(
+          Invoke(socket_ipv6_, &MockMDnsDatagramServerSocket::HandleRecvNow))
       .WillOnce(Return(ERR_IO_PENDING));
 
   EXPECT_CALL(delegate_, HandlePacketInternal(sample_packet_));
@@ -1192,14 +1173,14 @@
 }
 
 TEST_F(MDnsConnectionTest, ReceiveAsynchronous) {
-  recv_socket_ipv6_->SetResponsePacket(sample_packet_);
+  socket_ipv6_->SetResponsePacket(sample_packet_);
 
-  EXPECT_CALL(*recv_socket_ipv4_, RecvFromInternal(_, _, _, _))
+  EXPECT_CALL(*socket_ipv4_, RecvFromInternal(_, _, _, _))
       .WillOnce(Return(ERR_IO_PENDING));
-  EXPECT_CALL(*recv_socket_ipv6_, RecvFromInternal(_, _, _, _))
+  EXPECT_CALL(*socket_ipv6_, RecvFromInternal(_, _, _, _))
       .Times(2)
-      .WillOnce(Invoke(recv_socket_ipv6_,
-                       &MockMDnsDatagramServerSocket::HandleRecvLater))
+      .WillOnce(
+          Invoke(socket_ipv6_, &MockMDnsDatagramServerSocket::HandleRecvLater))
       .WillOnce(Return(ERR_IO_PENDING));
 
   ASSERT_TRUE(InitConnection());
@@ -1210,17 +1191,17 @@
 }
 
 TEST_F(MDnsConnectionTest, Error) {
-  CompletionOnceCallback callback;
+  CompletionRepeatingCallback callback;
 
-  EXPECT_CALL(*recv_socket_ipv4_, RecvFromInternal(_, _, _, _))
+  EXPECT_CALL(*socket_ipv4_, RecvFromInternal(_, _, _, _))
       .WillOnce(Return(ERR_IO_PENDING));
-  EXPECT_CALL(*recv_socket_ipv6_, RecvFromInternal(_, _, _, _))
-      .WillOnce(DoAll(MoveArgPointee<3>(&callback), Return(ERR_IO_PENDING)));
+  EXPECT_CALL(*socket_ipv6_, RecvFromInternal(_, _, _, _))
+      .WillOnce(DoAll(SaveArg<3>(&callback), Return(ERR_IO_PENDING)));
 
   ASSERT_TRUE(InitConnection());
 
   EXPECT_CALL(delegate_, OnConnectionError(ERR_SOCKET_NOT_CONNECTED));
-  std::move(callback).Run(ERR_SOCKET_NOT_CONNECTED);
+  callback.Run(ERR_SOCKET_NOT_CONNECTED);
   base::RunLoop().RunUntilIdle();
 }
 
@@ -1228,24 +1209,28 @@
  protected:
   void SetUp() override {
     MDnsConnectionTest::SetUp();
-    EXPECT_CALL(*recv_socket_ipv4_, RecvFromInternal(_, _, _, _))
+    EXPECT_CALL(*socket_ipv4_, RecvFromInternal(_, _, _, _))
         .WillOnce(Return(ERR_IO_PENDING));
-    EXPECT_CALL(*recv_socket_ipv6_, RecvFromInternal(_, _, _, _))
+    EXPECT_CALL(*socket_ipv6_, RecvFromInternal(_, _, _, _))
         .WillOnce(Return(ERR_IO_PENDING));
     EXPECT_TRUE(InitConnection());
   }
 };
 
 TEST_F(MDnsConnectionSendTest, Send) {
-  EXPECT_CALL(*send_socket_ipv4_, WriteInternal(sample_packet_, _, _));
-  EXPECT_CALL(*send_socket_ipv6_, WriteInternal(sample_packet_, _, _));
+  EXPECT_CALL(*socket_ipv4_,
+              SendToInternal(sample_packet_, "224.0.0.251:5353", _));
+  EXPECT_CALL(*socket_ipv6_,
+              SendToInternal(sample_packet_, "[ff02::fb]:5353", _));
 
   connection_.Send(sample_buffer_, sample_packet_.size());
 }
 
 TEST_F(MDnsConnectionSendTest, SendError) {
-  EXPECT_CALL(*send_socket_ipv4_, WriteInternal(sample_packet_, _, _));
-  EXPECT_CALL(*send_socket_ipv6_, WriteInternal(sample_packet_, _, _))
+  EXPECT_CALL(*socket_ipv4_,
+              SendToInternal(sample_packet_, "224.0.0.251:5353", _));
+  EXPECT_CALL(*socket_ipv6_,
+              SendToInternal(sample_packet_, "[ff02::fb]:5353", _))
       .WillOnce(Return(ERR_SOCKET_NOT_CONNECTED));
 
   connection_.Send(sample_buffer_, sample_packet_.size());
@@ -1255,37 +1240,38 @@
 
 TEST_F(MDnsConnectionSendTest, SendQueued) {
   // Send data immediately.
-  EXPECT_CALL(*send_socket_ipv4_, WriteInternal(sample_packet_, _, _))
+  EXPECT_CALL(*socket_ipv4_,
+              SendToInternal(sample_packet_, "224.0.0.251:5353", _))
       .Times(2)
       .WillRepeatedly(Return(OK));
 
-  CompletionOnceCallback callback;
+  CompletionRepeatingCallback callback;
   // Delay sending data. Only the first call should be made.
-  EXPECT_CALL(*send_socket_ipv6_, WriteInternal(sample_packet_, _, _))
-      .WillOnce(DoAll(MoveArgPointee<1>(&callback), Return(ERR_IO_PENDING)));
+  EXPECT_CALL(*socket_ipv6_,
+              SendToInternal(sample_packet_, "[ff02::fb]:5353", _))
+      .WillOnce(DoAll(SaveArg<2>(&callback), Return(ERR_IO_PENDING)));
 
   connection_.Send(sample_buffer_, sample_packet_.size());
   connection_.Send(sample_buffer_, sample_packet_.size());
 
   // The second IPv6 packet is not sent yet.
-  EXPECT_CALL(*send_socket_ipv4_, WriteInternal(sample_packet_, _, _)).Times(0);
+  EXPECT_CALL(*socket_ipv4_,
+              SendToInternal(sample_packet_, "224.0.0.251:5353", _))
+      .Times(0);
   // Expect call for the second IPv6 packet.
-  EXPECT_CALL(*send_socket_ipv6_, WriteInternal(sample_packet_, _, _))
+  EXPECT_CALL(*socket_ipv6_,
+              SendToInternal(sample_packet_, "[ff02::fb]:5353", _))
       .WillOnce(Return(OK));
-  std::move(callback).Run(OK);
+  callback.Run(OK);
 }
 
-TEST(MDnsSocketTest, CreateSocketPair) {
+TEST(MDnsSocketTest, CreateSocket) {
   // Verifies that socket creation hasn't been broken.
   NetLog net_log;
-  MDnsSendRecvSocketPair socket_pair = CreateAndBindMDnsSocketPair(
-      AddressFamily::ADDRESS_FAMILY_IPV4, 1, &net_log);
-  const auto& send_socket = socket_pair.first;
-  const auto& recv_socket = socket_pair.second;
-  EXPECT_NE(nullptr, send_socket);
-  EXPECT_NE(nullptr, recv_socket);
-  send_socket->Close();
-  recv_socket->Close();
+  auto socket =
+      CreateAndBindMDnsSocket(AddressFamily::ADDRESS_FAMILY_IPV4, 1, &net_log);
+  EXPECT_TRUE(socket);
+  socket->Close();
 }
 
 }  // namespace net
diff --git a/net/dns/mock_mdns_socket_factory.cc b/net/dns/mock_mdns_socket_factory.cc
index d50b46b..5895e47d 100644
--- a/net/dns/mock_mdns_socket_factory.cc
+++ b/net/dns/mock_mdns_socket_factory.cc
@@ -14,8 +14,6 @@
 #include "base/threading/thread_task_runner_handle.h"
 #include "net/base/net_errors.h"
 #include "net/dns/public/util.h"
-#include "net/socket/socket_tag.h"
-#include "net/traffic_annotation/network_traffic_annotation.h"
 
 using testing::_;
 using testing::Invoke;
@@ -34,14 +32,15 @@
                                          const IPEndPoint& address,
                                          CompletionOnceCallback callback) {
   return SendToInternal(std::string(buf->data(), buf_len), address.ToString(),
-                        &callback);
+                        base::AdaptCallbackForRepeating(std::move(callback)));
 }
 
 int MockMDnsDatagramServerSocket::RecvFrom(IOBuffer* buffer,
                                            int size,
                                            IPEndPoint* address,
                                            CompletionOnceCallback callback) {
-  return RecvFromInternal(buffer, size, address, &callback);
+  return RecvFromInternal(buffer, size, address,
+                          base::AdaptCallbackForRepeating(std::move(callback)));
 }
 
 int MockMDnsDatagramServerSocket::GetLocalAddress(IPEndPoint* address) const {
@@ -58,7 +57,7 @@
     IOBuffer* buffer,
     int size,
     IPEndPoint* address,
-    CompletionOnceCallback* callback) {
+    CompletionRepeatingCallback callback) {
   int size_returned =
       std::min(response_packet_.size(), static_cast<size_t>(size));
   memcpy(buffer->data(), response_packet_.data(), size_returned);
@@ -69,53 +68,38 @@
     IOBuffer* buffer,
     int size,
     IPEndPoint* address,
-    CompletionOnceCallback* callback) {
+    CompletionRepeatingCallback callback) {
   int rv = HandleRecvNow(buffer, size, address, callback);
-  base::ThreadTaskRunnerHandle::Get()->PostTask(
-      FROM_HERE, base::BindOnce(std::move(*callback), rv));
+  base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE,
+                                                base::BindOnce(callback, rv));
   return ERR_IO_PENDING;
 }
 
-MockMDnsDatagramClientSocket::MockMDnsDatagramClientSocket() = default;
-
-MockMDnsDatagramClientSocket::~MockMDnsDatagramClientSocket() = default;
-
-int MockMDnsDatagramClientSocket::Write(
-    IOBuffer* buf,
-    int buf_len,
-    CompletionOnceCallback callback,
-    const NetworkTrafficAnnotationTag& traffic_annotation) {
-  return WriteInternal(std::string(buf->data(), buf_len), &callback,
-                       traffic_annotation);
-}
-
 MockMDnsSocketFactory::MockMDnsSocketFactory() = default;
 
 MockMDnsSocketFactory::~MockMDnsSocketFactory() = default;
 
-void MockMDnsSocketFactory::CreateSocketPairs(
-    std::vector<MDnsSendRecvSocketPair>* socket_pairs) {
-  CreateSocketPair(ADDRESS_FAMILY_IPV4, socket_pairs);
-  CreateSocketPair(ADDRESS_FAMILY_IPV6, socket_pairs);
+void MockMDnsSocketFactory::CreateSockets(
+    std::vector<std::unique_ptr<DatagramServerSocket>>* sockets) {
+  CreateSocket(ADDRESS_FAMILY_IPV4, sockets);
+  CreateSocket(ADDRESS_FAMILY_IPV6, sockets);
 }
 
-void MockMDnsSocketFactory::CreateSocketPair(
+void MockMDnsSocketFactory::CreateSocket(
     AddressFamily address_family,
-    std::vector<MDnsSendRecvSocketPair>* socket_pairs) {
-  auto new_send_socket =
-      std::make_unique<testing::NiceMock<MockMDnsDatagramClientSocket>>();
-  auto new_recv_socket =
-      std::make_unique<testing::NiceMock<MockMDnsDatagramServerSocket>>(
-          address_family);
+    std::vector<std::unique_ptr<DatagramServerSocket>>* sockets) {
+  std::unique_ptr<testing::NiceMock<MockMDnsDatagramServerSocket>> new_socket(
+      new testing::NiceMock<MockMDnsDatagramServerSocket>(address_family));
 
-  ON_CALL(*new_send_socket, WriteInternal(_, _, _))
-      .WillByDefault(Invoke(this, &MockMDnsSocketFactory::WriteInternal));
+  ON_CALL(*new_socket, SendToInternal(_, _, _))
+      .WillByDefault(Invoke(
+          this,
+          &MockMDnsSocketFactory::SendToInternal));
 
-  ON_CALL(*new_recv_socket, RecvFromInternal(_, _, _, _))
+  ON_CALL(*new_socket, RecvFromInternal(_, _, _, _))
       .WillByDefault(Invoke(this, &MockMDnsSocketFactory::RecvFromInternal));
 
-  socket_pairs->push_back(
-      std::make_pair(std::move(new_send_socket), std::move(new_recv_socket)));
+  sockets->push_back(std::move(new_socket));
 }
 
 void MockMDnsSocketFactory::SimulateReceive(const uint8_t* packet, int size) {
@@ -127,20 +111,20 @@
   base::ResetAndReturn(&recv_callback_).Run(size);
 }
 
-int MockMDnsSocketFactory::RecvFromInternal(IOBuffer* buffer,
-                                            int size,
-                                            IPEndPoint* address,
-                                            CompletionOnceCallback* callback) {
+int MockMDnsSocketFactory::RecvFromInternal(
+    IOBuffer* buffer,
+    int size,
+    IPEndPoint* address,
+    CompletionRepeatingCallback callback) {
   recv_buffer_ = buffer;
   recv_buffer_size_ = size;
-  recv_callback_ = std::move(*callback);
+  recv_callback_ = callback;
   return ERR_IO_PENDING;
 }
 
-int MockMDnsSocketFactory::WriteInternal(
-    const std::string& packet,
-    CompletionOnceCallback* callback,
-    const NetworkTrafficAnnotationTag& traffic_annotation) {
+int MockMDnsSocketFactory::SendToInternal(const std::string& packet,
+                                          const std::string& address,
+                                          CompletionOnceCallback callback) {
   OnSendTo(packet);
   return packet.size();
 }
diff --git a/net/dns/mock_mdns_socket_factory.h b/net/dns/mock_mdns_socket_factory.h
index 862058de..8f0d5b59 100644
--- a/net/dns/mock_mdns_socket_factory.h
+++ b/net/dns/mock_mdns_socket_factory.h
@@ -13,18 +13,13 @@
 
 #include "net/base/completion_once_callback.h"
 #include "net/base/completion_repeating_callback.h"
-#include "net/base/datagram_buffer.h"
-#include "net/base/network_change_notifier.h"
 #include "net/dns/mdns_client_impl.h"
 #include "net/log/net_log_with_source.h"
-#include "net/socket/datagram_client_socket.h"
 #include "testing/gmock/include/gmock/gmock.h"
 
 namespace net {
 
 class IPAddress;
-class SocketTag;
-struct NetworkTrafficAnnotationTag;
 
 class MockMDnsDatagramServerSocket : public DatagramServerSocket {
  public:
@@ -34,9 +29,8 @@
   // DatagramServerSocket implementation:
   MOCK_METHOD1(Listen, int(const IPEndPoint& address));
 
-  // GMock cannot handle move-only types like CompletionOnceCallback, so we use
-  // delegate methods RecvFromInternal and SendToInternal below as the
-  // respectively mocked methods for RecvFrom and SendTo.
+  // GMock cannot handle move-only types like CompletionOnceCallback, so it
+  // needs to be converted into the copyable type CompletionRepeatingCallback.
   int RecvFrom(IOBuffer* buffer,
                int size,
                IPEndPoint* address,
@@ -46,7 +40,7 @@
                int(IOBuffer* buffer,
                    int size,
                    IPEndPoint* address,
-                   CompletionOnceCallback* callback));
+                   CompletionRepeatingCallback callback));
 
   int SendTo(IOBuffer* buf,
              int buf_len,
@@ -56,7 +50,7 @@
   MOCK_METHOD3(SendToInternal,
                int(const std::string& packet,
                    const std::string address,
-                   CompletionOnceCallback* callback));
+                   CompletionRepeatingCallback callback));
 
   MOCK_METHOD1(SetReceiveBufferSize, int(int32_t size));
   MOCK_METHOD1(SetSendBufferSize, int(int32_t size));
@@ -93,108 +87,49 @@
   int HandleRecvNow(IOBuffer* buffer,
                     int size,
                     IPEndPoint* address,
-                    CompletionOnceCallback* callback);
+                    CompletionRepeatingCallback callback);
 
   int HandleRecvLater(IOBuffer* buffer,
                       int size,
                       IPEndPoint* address,
-                      CompletionOnceCallback* callback);
+                      CompletionRepeatingCallback callback);
 
  private:
   std::string response_packet_;
   IPEndPoint local_address_;
 };
 
-class MockMDnsDatagramClientSocket : public DatagramClientSocket {
- public:
-  MockMDnsDatagramClientSocket();
-  ~MockMDnsDatagramClientSocket() override;
-
-  // DatagramSocket implementation:
-  MOCK_METHOD0(Close, void());
-  MOCK_CONST_METHOD1(GetPeerAddress, int(IPEndPoint* address));
-  MOCK_CONST_METHOD1(GetLocalAddress, int(IPEndPoint* address));
-  MOCK_METHOD0(UseNonBlockingIO, void());
-  MOCK_METHOD0(SetDoNotFragment, int());
-  MOCK_METHOD1(SetMsgConfirm, void(bool confirm));
-  MOCK_CONST_METHOD0(NetLog, const NetLogWithSource&());
-
-  // Socket implementation:
-  MOCK_METHOD3(Read, int(IOBuffer*, int, CompletionOnceCallback));
-  MOCK_METHOD3(ReadIfReady, int(IOBuffer*, int, CompletionOnceCallback));
-  MOCK_METHOD0(CancelReadIfReady, int());
-  // GMock cannot handle move-only types like CompletionOnceCallback, so we use
-  // a delegate method WriteInternal below as the mocked method for Write.
-  int Write(IOBuffer* buf,
-            int buf_len,
-            CompletionOnceCallback callback,
-            const NetworkTrafficAnnotationTag& traffic_annotation) override;
-  MOCK_METHOD3(WriteInternal,
-               int(const std::string,
-                   CompletionOnceCallback*,
-                   const NetworkTrafficAnnotationTag&));
-  MOCK_METHOD1(SetReceiveBufferSize, int(int32_t));
-  MOCK_METHOD1(SetSendBufferSize, int(int32_t));
-
-  // DatagramClientSocket implementation:
-  MOCK_METHOD1(Connect, int(const IPEndPoint& address));
-  MOCK_METHOD2(ConnectUsingNetwork,
-               int(NetworkChangeNotifier::NetworkHandle, const IPEndPoint&));
-  MOCK_METHOD1(ConnectUsingDefaultNetwork, int(const IPEndPoint&));
-
-  MOCK_CONST_METHOD0(GetBoundNetwork, NetworkChangeNotifier::NetworkHandle());
-  MOCK_METHOD1(ApplySocketTag, void(const SocketTag&));
-  MOCK_METHOD3(WriteAsync,
-               int(DatagramBuffers,
-                   CompletionOnceCallback,
-                   const NetworkTrafficAnnotationTag&));
-  MOCK_METHOD4(WriteAsync,
-               int(const char*,
-                   size_t,
-                   CompletionOnceCallback,
-                   const NetworkTrafficAnnotationTag&));
-  MOCK_METHOD0(GetUnwrittenBuffers, DatagramBuffers());
-  MOCK_METHOD1(SetWriteAsyncEnabled, void(bool));
-  MOCK_METHOD1(SetMaxPacketSize, void(size_t));
-  MOCK_METHOD0(WriteAsyncEnabled, bool());
-  MOCK_METHOD1(SetWriteMultiCoreEnabled, void(bool));
-  MOCK_METHOD1(SetSendmmsgEnabled, void(bool));
-  MOCK_METHOD1(SetWriteBatchingActive, void(bool));
-  MOCK_METHOD1(SetMulticastInterface, int(uint32_t));
-};
-
 class MockMDnsSocketFactory : public MDnsSocketFactory {
  public:
   MockMDnsSocketFactory();
   ~MockMDnsSocketFactory() override;
 
-  void CreateSocketPairs(
-      std::vector<MDnsSendRecvSocketPair>* socket_pairs) override;
+  void CreateSockets(
+      std::vector<std::unique_ptr<DatagramServerSocket>>* sockets) override;
 
   void SimulateReceive(const uint8_t* packet, int size);
 
   MOCK_METHOD1(OnSendTo, void(const std::string&));
 
  private:
-  // The following two methods are the default implementations of
-  // MockMDnsDatagramClientSocket::WriteInternal and
-  // MockMDnsDatagramServerSocket::RecvFromInternal respectively.
-  int WriteInternal(const std::string& packet,
-                    CompletionOnceCallback* callback,
-                    const NetworkTrafficAnnotationTag& traffic_annotation);
+  int SendToInternal(const std::string& packet,
+                     const std::string& address,
+                     CompletionOnceCallback callback);
+
   // The latest receive callback is always saved, since the MDnsConnection
   // does not care which socket a packet is received on.
   int RecvFromInternal(IOBuffer* buffer,
                        int size,
                        IPEndPoint* address,
-                       CompletionOnceCallback* callback);
+                       CompletionRepeatingCallback callback);
 
-  void CreateSocketPair(AddressFamily address_family,
-                        std::vector<MDnsSendRecvSocketPair>* socket_pairs);
+  void CreateSocket(
+      AddressFamily address_family,
+      std::vector<std::unique_ptr<DatagramServerSocket>>* sockets);
 
   scoped_refptr<IOBuffer> recv_buffer_;
   int recv_buffer_size_;
-  CompletionOnceCallback recv_callback_;
+  CompletionRepeatingCallback recv_callback_;
 };
 
 }  // namespace net
diff --git a/net/dns/public/util.cc b/net/dns/public/util.cc
index ef40bc4..69c911f 100644
--- a/net/dns/public/util.cc
+++ b/net/dns/public/util.cc
@@ -70,7 +70,12 @@
 }
 
 IPEndPoint GetMdnsReceiveEndPoint(AddressFamily address_family) {
-#if defined(OS_WIN) || defined(OS_FUCHSIA)
+// TODO(qingsi): MacOS should follow other POSIX platforms in the else-branch
+// after addressing crbug.com/899310. We have encountered a conflicting issue on
+// CrOS as described in crbug.com/931916, and the following is a temporary
+// mitigation to reconcile the two issues. Remove this after closing
+// crbug.com/899310.
+#if defined(OS_WIN) || defined(OS_FUCHSIA) || defined(OS_MACOSX)
   // With Windows, binding to a mulitcast group address is not allowed.
   // Multicast messages will be received appropriate to the multicast groups the
   // socket has joined. Sockets intending to receive multicast messages should
@@ -86,12 +91,12 @@
       NOTREACHED();
       return IPEndPoint();
   }
-#else   // !(defined(OS_WIN) || defined(OS_FUCHSIA))
+#else   // !(defined(OS_WIN) || defined(OS_FUCHSIA)) || defined(OS_MACOSX)
   // With POSIX, any socket can receive messages for multicast groups joined by
   // any socket on the system. Sockets intending to receive messages for a
   // specific multicast group should bind to that group address.
   return GetMdnsGroupEndPoint(address_family);
-#endif  // !(defined(OS_WIN) || defined(OS_FUCHSIA))
+#endif  // !(defined(OS_WIN) || defined(OS_FUCHSIA)) || defined(OS_MACOSX)
 }
 
 }  // namespace dns_util
diff --git a/net/dns/public/util.h b/net/dns/public/util.h
index f600d5b2..26b0aaa 100644
--- a/net/dns/public/util.h
+++ b/net/dns/public/util.h
@@ -25,13 +25,13 @@
 
 // Gets the endpoint for the multicast group a socket should join to receive
 // MDNS messages. Such sockets should also bind to the endpoint from
-// GetMdnsReceiveEndPoint().
+// GetMDnsReceiveEndPoint().
 //
 // This is also the endpoint messages should be sent to to send MDNS messages.
 NET_EXPORT IPEndPoint GetMdnsGroupEndPoint(AddressFamily address_family);
 
 // Gets the endpoint sockets should be bound to to receive MDNS messages. Such
-// sockets should also join the multicast group from GetMdnsGroupEndPoint().
+// sockets should also join the multicast group from GetMDnsGroupEndPoint().
 NET_EXPORT IPEndPoint GetMdnsReceiveEndPoint(AddressFamily address_family);
 
 }  // namespace dns_util
diff --git a/net/http/http_auth.cc b/net/http/http_auth.cc
index 54bc7812..68ae3b1f 100644
--- a/net/http/http_auth.cc
+++ b/net/http/http_auth.cc
@@ -10,6 +10,7 @@
 #include "base/strings/string_tokenizer.h"
 #include "base/strings/string_util.h"
 #include "net/base/net_errors.h"
+#include "net/dns/host_resolver.h"
 #include "net/http/http_auth_challenge_tokenizer.h"
 #include "net/http/http_auth_handler.h"
 #include "net/http/http_auth_handler_factory.h"
@@ -31,6 +32,7 @@
     const GURL& origin,
     const std::set<Scheme>& disabled_schemes,
     const NetLogWithSource& net_log,
+    HostResolver* host_resolver,
     std::unique_ptr<HttpAuthHandler>* handler) {
   DCHECK(http_auth_handler_factory);
   DCHECK(handler->get() == NULL);
@@ -43,7 +45,7 @@
   while (response_headers.EnumerateHeader(&iter, header_name, &cur_challenge)) {
     std::unique_ptr<HttpAuthHandler> cur;
     int rv = http_auth_handler_factory->CreateAuthHandlerFromString(
-        cur_challenge, target, ssl_info, origin, net_log, &cur);
+        cur_challenge, target, ssl_info, origin, net_log, host_resolver, &cur);
     if (rv != OK) {
       VLOG(1) << "Unable to create AuthHandler. Status: "
               << ErrorToString(rv) << " Challenge: " << cur_challenge;
diff --git a/net/http/http_auth.h b/net/http/http_auth.h
index e4419e1..14603e66 100644
--- a/net/http/http_auth.h
+++ b/net/http/http_auth.h
@@ -20,6 +20,7 @@
 class HttpAuthHandler;
 class HttpAuthHandlerFactory;
 class HttpResponseHeaders;
+class HostResolver;
 class NetLogWithSource;
 class SSLInfo;
 
@@ -147,6 +148,7 @@
       const GURL& origin,
       const std::set<Scheme>& disabled_schemes,
       const NetLogWithSource& net_log,
+      HostResolver* host_resolver,
       std::unique_ptr<HttpAuthHandler>* handler);
 
   // Handle a 401/407 response from a server/proxy after a previous
diff --git a/net/http/http_auth_controller.cc b/net/http/http_auth_controller.cc
index e682f26..12cc4f2 100644
--- a/net/http/http_auth_controller.cc
+++ b/net/http/http_auth_controller.cc
@@ -129,7 +129,8 @@
     HttpAuth::Target target,
     const GURL& auth_url,
     HttpAuthCache* http_auth_cache,
-    HttpAuthHandlerFactory* http_auth_handler_factory)
+    HttpAuthHandlerFactory* http_auth_handler_factory,
+    HostResolver* host_resolver)
     : target_(target),
       auth_url_(auth_url),
       auth_origin_(auth_url.GetOrigin()),
@@ -137,8 +138,8 @@
       embedded_identity_used_(false),
       default_credentials_used_(false),
       http_auth_cache_(http_auth_cache),
-      http_auth_handler_factory_(http_auth_handler_factory) {
-}
+      http_auth_handler_factory_(http_auth_handler_factory),
+      host_resolver_(host_resolver) {}
 
 HttpAuthController::~HttpAuthController() {
   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
@@ -193,11 +194,11 @@
 
   // Try to create a handler using the previous auth challenge.
   std::unique_ptr<HttpAuthHandler> handler_preemptive;
-  int rv_create = http_auth_handler_factory_->
-      CreatePreemptiveAuthHandlerFromString(entry->auth_challenge(), target_,
-                                            auth_origin_,
-                                            entry->IncrementNonceCount(),
-                                            net_log, &handler_preemptive);
+  int rv_create =
+      http_auth_handler_factory_->CreatePreemptiveAuthHandlerFromString(
+          entry->auth_challenge(), target_, auth_origin_,
+          entry->IncrementNonceCount(), net_log, host_resolver_,
+          &handler_preemptive);
   if (rv_create != OK)
     return false;
 
@@ -288,9 +289,9 @@
   do {
     if (!handler_.get() && can_send_auth) {
       // Find the best authentication challenge that we support.
-      HttpAuth::ChooseBestChallenge(http_auth_handler_factory_, *headers,
-                                    ssl_info, target_, auth_origin_,
-                                    disabled_schemes_, net_log, &handler_);
+      HttpAuth::ChooseBestChallenge(
+          http_auth_handler_factory_, *headers, ssl_info, target_, auth_origin_,
+          disabled_schemes_, net_log, host_resolver_, &handler_);
       if (handler_.get())
         HistogramAuthEvent(handler_.get(), AUTH_EVENT_START);
     }
diff --git a/net/http/http_auth_controller.h b/net/http/http_auth_controller.h
index 327b380..e71c3f93 100644
--- a/net/http/http_auth_controller.h
+++ b/net/http/http_auth_controller.h
@@ -24,6 +24,7 @@
 class HttpAuthHandlerFactory;
 class HttpAuthCache;
 class HttpRequestHeaders;
+class HostResolver;
 class NetLogWithSource;
 struct HttpRequestInfo;
 class SSLInfo;
@@ -46,7 +47,8 @@
   HttpAuthController(HttpAuth::Target target,
                      const GURL& auth_url,
                      HttpAuthCache* http_auth_cache,
-                     HttpAuthHandlerFactory* http_auth_handler_factory);
+                     HttpAuthHandlerFactory* http_auth_handler_factory,
+                     HostResolver* host_resolver);
 
   // Generate an authentication token for |target| if necessary. The return
   // value is a net error code. |OK| will be returned both in the case that
@@ -184,6 +186,7 @@
   // for the lifetime of this object.
   HttpAuthCache* const http_auth_cache_;
   HttpAuthHandlerFactory* const http_auth_handler_factory_;
+  HostResolver* const host_resolver_;
 
   std::set<HttpAuth::Scheme> disabled_schemes_;
 
diff --git a/net/http/http_auth_controller_unittest.cc b/net/http/http_auth_controller_unittest.cc
index 8c94fcf..b2c9036 100644
--- a/net/http/http_auth_controller_unittest.cc
+++ b/net/http/http_auth_controller_unittest.cc
@@ -10,6 +10,7 @@
 #include "base/test/scoped_task_environment.h"
 #include "net/base/net_errors.h"
 #include "net/base/test_completion_callback.h"
+#include "net/dns/mock_host_resolver.h"
 #include "net/http/http_auth_cache.h"
 #include "net/http/http_auth_challenge_tokenizer.h"
 #include "net/http/http_auth_handler_mock.h"
@@ -71,11 +72,11 @@
                                        handler_rv);
   auth_handler_factory.AddMockHandler(auth_handler, HttpAuth::AUTH_PROXY);
   auth_handler_factory.set_do_init_from_challenge(true);
+  auto host_resolver = std::make_unique<MockHostResolver>();
 
-  scoped_refptr<HttpAuthController> controller(
-      new HttpAuthController(HttpAuth::AUTH_PROXY,
-                             GURL("http://example.com"),
-                             &dummy_auth_cache, &auth_handler_factory));
+  scoped_refptr<HttpAuthController> controller(new HttpAuthController(
+      HttpAuth::AUTH_PROXY, GURL("http://example.com"), &dummy_auth_cache,
+      &auth_handler_factory, host_resolver.get()));
   SSLInfo null_ssl_info;
   ASSERT_EQ(OK, controller->HandleAuthChallenge(headers, null_ssl_info, false,
                                                 false, dummy_log));
@@ -217,10 +218,11 @@
       HttpAuth::AUTH_SERVER);
   auth_handler_factory.set_do_init_from_challenge(true);
 
-  scoped_refptr<HttpAuthController> controller(
-      new HttpAuthController(HttpAuth::AUTH_SERVER,
-                             GURL("http://example.com"),
-                             &dummy_auth_cache, &auth_handler_factory));
+  auto host_resolver = std::make_unique<MockHostResolver>();
+
+  scoped_refptr<HttpAuthController> controller(new HttpAuthController(
+      HttpAuth::AUTH_SERVER, GURL("http://example.com"), &dummy_auth_cache,
+      &auth_handler_factory, host_resolver.get()));
   SSLInfo null_ssl_info;
   ASSERT_EQ(OK, controller->HandleAuthChallenge(headers, null_ssl_info, false,
                                                 false, dummy_log));
diff --git a/net/http/http_auth_handler_basic.cc b/net/http/http_auth_handler_basic.cc
index bf84a12..cdec282 100644
--- a/net/http/http_auth_handler_basic.cc
+++ b/net/http/http_auth_handler_basic.cc
@@ -11,6 +11,7 @@
 #include "base/strings/utf_string_conversions.h"
 #include "net/base/net_errors.h"
 #include "net/base/net_string_util.h"
+#include "net/dns/host_resolver.h"
 #include "net/http/http_auth.h"
 #include "net/http/http_auth_challenge_tokenizer.h"
 #include "net/http/http_auth_scheme.h"
@@ -115,6 +116,7 @@
     CreateReason reason,
     int digest_nonce_count,
     const NetLogWithSource& net_log,
+    HostResolver* host_resolver,
     std::unique_ptr<HttpAuthHandler>* handler) {
   // TODO(cbentzel): Move towards model of parsing in the factory
   //                 method and only constructing when valid.
diff --git a/net/http/http_auth_handler_basic.h b/net/http/http_auth_handler_basic.h
index 02e3dd7..b42aec1 100644
--- a/net/http/http_auth_handler_basic.h
+++ b/net/http/http_auth_handler_basic.h
@@ -5,6 +5,7 @@
 #ifndef NET_HTTP_HTTP_AUTH_HANDLER_BASIC_H_
 #define NET_HTTP_HTTP_AUTH_HANDLER_BASIC_H_
 
+#include <memory>
 #include <string>
 
 #include "net/base/completion_once_callback.h"
@@ -29,6 +30,7 @@
                           CreateReason reason,
                           int digest_nonce_count,
                           const NetLogWithSource& net_log,
+                          HostResolver* host_resolver,
                           std::unique_ptr<HttpAuthHandler>* handler) override;
   };
 
diff --git a/net/http/http_auth_handler_basic_unittest.cc b/net/http/http_auth_handler_basic_unittest.cc
index 1f6ea6f..f2c80e6 100644
--- a/net/http/http_auth_handler_basic_unittest.cc
+++ b/net/http/http_auth_handler_basic_unittest.cc
@@ -12,6 +12,7 @@
 #include "base/strings/utf_string_conversions.h"
 #include "net/base/net_errors.h"
 #include "net/base/test_completion_callback.h"
+#include "net/dns/mock_host_resolver.h"
 #include "net/http/http_auth_challenge_tokenizer.h"
 #include "net/http/http_request_info.h"
 #include "net/log/net_log_with_source.h"
@@ -43,10 +44,11 @@
   for (size_t i = 0; i < base::size(tests); ++i) {
     std::string challenge = "Basic realm=\"Atlantis\"";
     SSLInfo null_ssl_info;
+    auto host_resolver = std::make_unique<MockHostResolver>();
     std::unique_ptr<HttpAuthHandler> basic;
     EXPECT_EQ(OK, factory.CreateAuthHandlerFromString(
                       challenge, HttpAuth::AUTH_SERVER, null_ssl_info, origin,
-                      NetLogWithSource(), &basic));
+                      NetLogWithSource(), host_resolver.get(), &basic));
     AuthCredentials credentials(base::ASCIIToUTF16(tests[i].username),
                                 base::ASCIIToUTF16(tests[i].password));
     HttpRequestInfo request_info;
@@ -97,10 +99,11 @@
   GURL origin("http://www.example.com");
   HttpAuthHandlerBasic::Factory factory;
   SSLInfo null_ssl_info;
+  auto host_resolver = std::make_unique<MockHostResolver>();
   std::unique_ptr<HttpAuthHandler> basic;
   EXPECT_EQ(OK, factory.CreateAuthHandlerFromString(
                     tests[0].challenge, HttpAuth::AUTH_SERVER, null_ssl_info,
-                    origin, NetLogWithSource(), &basic));
+                    origin, NetLogWithSource(), host_resolver.get(), &basic));
 
   for (size_t i = 0; i < base::size(tests); ++i) {
     std::string challenge(tests[i].challenge);
@@ -198,10 +201,11 @@
   for (size_t i = 0; i < base::size(tests); ++i) {
     std::string challenge = tests[i].challenge;
     SSLInfo null_ssl_info;
+    auto host_resolver = std::make_unique<MockHostResolver>();
     std::unique_ptr<HttpAuthHandler> basic;
     int rv = factory.CreateAuthHandlerFromString(
         challenge, HttpAuth::AUTH_SERVER, null_ssl_info, origin,
-        NetLogWithSource(), &basic);
+        NetLogWithSource(), host_resolver.get(), &basic);
     EXPECT_EQ(tests[i].expected_rv, rv);
     if (rv == OK)
       EXPECT_EQ(tests[i].expected_realm, basic->realm());
diff --git a/net/http/http_auth_handler_digest.cc b/net/http/http_auth_handler_digest.cc
index 50941f4..424340f 100644
--- a/net/http/http_auth_handler_digest.cc
+++ b/net/http/http_auth_handler_digest.cc
@@ -15,6 +15,7 @@
 #include "net/base/net_errors.h"
 #include "net/base/net_string_util.h"
 #include "net/base/url_util.h"
+#include "net/dns/host_resolver.h"
 #include "net/http/http_auth.h"
 #include "net/http/http_auth_challenge_tokenizer.h"
 #include "net/http/http_auth_scheme.h"
@@ -94,6 +95,7 @@
     CreateReason reason,
     int digest_nonce_count,
     const NetLogWithSource& net_log,
+    HostResolver* host_resolver,
     std::unique_ptr<HttpAuthHandler>* handler) {
   // TODO(cbentzel): Move towards model of parsing in the factory
   //                 method and only constructing when valid.
diff --git a/net/http/http_auth_handler_digest.h b/net/http/http_auth_handler_digest.h
index cfe6569d..90731b8 100644
--- a/net/http/http_auth_handler_digest.h
+++ b/net/http/http_auth_handler_digest.h
@@ -73,6 +73,7 @@
                           CreateReason reason,
                           int digest_nonce_count,
                           const NetLogWithSource& net_log,
+                          HostResolver* host_resolver,
                           std::unique_ptr<HttpAuthHandler>* handler) override;
 
    private:
diff --git a/net/http/http_auth_handler_digest_unittest.cc b/net/http/http_auth_handler_digest_unittest.cc
index 86097ff..4af18578 100644
--- a/net/http/http_auth_handler_digest_unittest.cc
+++ b/net/http/http_auth_handler_digest_unittest.cc
@@ -9,6 +9,7 @@
 #include "base/strings/utf_string_conversions.h"
 #include "net/base/net_errors.h"
 #include "net/base/test_completion_callback.h"
+#include "net/dns/mock_host_resolver.h"
 #include "net/http/http_auth_challenge_tokenizer.h"
 #include "net/http/http_auth_handler_digest.h"
 #include "net/http/http_request_info.h"
@@ -56,6 +57,7 @@
   HttpAuthHandlerDigest::NonceGenerator* nonce_generator =
       new HttpAuthHandlerDigest::FixedNonceGenerator("client_nonce");
   factory->set_nonce_generator(nonce_generator);
+  auto host_resolver = std::make_unique<MockHostResolver>();
   std::unique_ptr<HttpAuthHandler> handler;
 
   // Create a handler for a particular challenge.
@@ -63,7 +65,7 @@
   GURL url_origin(target == HttpAuth::AUTH_SERVER ? request_url : proxy_name);
   int rv_create = factory->CreateAuthHandlerFromString(
       challenge, target, null_ssl_info, url_origin.GetOrigin(),
-      NetLogWithSource(), &handler);
+      NetLogWithSource(), host_resolver.get(), &handler);
   if (rv_create != OK || handler.get() == NULL) {
     ADD_FAILURE() << "Unable to create auth handler.";
     return false;
@@ -361,10 +363,11 @@
       new HttpAuthHandlerDigest::Factory());
   for (size_t i = 0; i < base::size(tests); ++i) {
     SSLInfo null_ssl_info;
+    auto host_resolver = std::make_unique<MockHostResolver>();
     std::unique_ptr<HttpAuthHandler> handler;
     int rv = factory->CreateAuthHandlerFromString(
         tests[i].challenge, HttpAuth::AUTH_SERVER, null_ssl_info, origin,
-        NetLogWithSource(), &handler);
+        NetLogWithSource(), host_resolver.get(), &handler);
     if (tests[i].parsed_success) {
       EXPECT_THAT(rv, IsOk());
     } else {
@@ -525,10 +528,11 @@
       new HttpAuthHandlerDigest::Factory());
   for (size_t i = 0; i < base::size(tests); ++i) {
     SSLInfo null_ssl_info;
+    auto host_resolver = std::make_unique<MockHostResolver>();
     std::unique_ptr<HttpAuthHandler> handler;
     int rv = factory->CreateAuthHandlerFromString(
         tests[i].challenge, HttpAuth::AUTH_SERVER, null_ssl_info, origin,
-        NetLogWithSource(), &handler);
+        NetLogWithSource(), host_resolver.get(), &handler);
     EXPECT_THAT(rv, IsOk());
     ASSERT_TRUE(handler != NULL);
 
@@ -550,6 +554,7 @@
 TEST(HttpAuthHandlerDigest, HandleAnotherChallenge) {
   std::unique_ptr<HttpAuthHandlerDigest::Factory> factory(
       new HttpAuthHandlerDigest::Factory());
+  auto host_resolver = std::make_unique<MockHostResolver>();
   std::unique_ptr<HttpAuthHandler> handler;
   std::string default_challenge =
       "Digest realm=\"Oblivion\", nonce=\"nonce-value\"";
@@ -557,7 +562,7 @@
   SSLInfo null_ssl_info;
   int rv = factory->CreateAuthHandlerFromString(
       default_challenge, HttpAuth::AUTH_SERVER, null_ssl_info, origin,
-      NetLogWithSource(), &handler);
+      NetLogWithSource(), host_resolver.get(), &handler);
   EXPECT_THAT(rv, IsOk());
   ASSERT_TRUE(handler.get() != NULL);
   HttpAuthChallengeTokenizer tok_default(default_challenge.begin(),
diff --git a/net/http/http_auth_handler_factory.cc b/net/http/http_auth_handler_factory.cc
index 6b3dd0e..0e5610e 100644
--- a/net/http/http_auth_handler_factory.cc
+++ b/net/http/http_auth_handler_factory.cc
@@ -4,11 +4,14 @@
 
 #include "net/http/http_auth_handler_factory.h"
 
+#include <set>
+
 #include "base/memory/ptr_util.h"
 #include "base/stl_util.h"
 #include "base/strings/string_util.h"
 #include "build/build_config.h"
 #include "net/base/net_errors.h"
+#include "net/dns/host_resolver.h"
 #include "net/http/http_auth_challenge_tokenizer.h"
 #include "net/http/http_auth_filter.h"
 #include "net/http/http_auth_handler_basic.h"
@@ -31,10 +34,11 @@
     const SSLInfo& ssl_info,
     const GURL& origin,
     const NetLogWithSource& net_log,
+    HostResolver* host_resolver,
     std::unique_ptr<HttpAuthHandler>* handler) {
   HttpAuthChallengeTokenizer props(challenge.begin(), challenge.end());
   return CreateAuthHandler(&props, target, ssl_info, origin, CREATE_CHALLENGE,
-                           1, net_log, handler);
+                           1, net_log, host_resolver, handler);
 }
 
 int HttpAuthHandlerFactory::CreatePreemptiveAuthHandlerFromString(
@@ -43,12 +47,13 @@
     const GURL& origin,
     int digest_nonce_count,
     const NetLogWithSource& net_log,
+    HostResolver* host_resolver,
     std::unique_ptr<HttpAuthHandler>* handler) {
   HttpAuthChallengeTokenizer props(challenge.begin(), challenge.end());
   SSLInfo null_ssl_info;
   return CreateAuthHandler(&props, target, null_ssl_info, origin,
                            CREATE_PREEMPTIVE, digest_nonce_count, net_log,
-                           handler);
+                           host_resolver, handler);
 }
 
 namespace {
@@ -97,7 +102,6 @@
 // static
 std::unique_ptr<HttpAuthHandlerRegistryFactory>
 HttpAuthHandlerFactory::CreateDefault(
-    HostResolver* host_resolver,
     const HttpAuthPreferences* prefs
 #if defined(OS_CHROMEOS)
     ,
@@ -113,7 +117,7 @@
 ) {
   std::vector<std::string> auth_types(std::begin(kDefaultAuthSchemes),
                                       std::end(kDefaultAuthSchemes));
-  return HttpAuthHandlerRegistryFactory::Create(host_resolver, prefs, auth_types
+  return HttpAuthHandlerRegistryFactory::Create(prefs, auth_types
 #if defined(OS_POSIX) && !defined(OS_ANDROID) && !defined(OS_CHROMEOS)
                                                 ,
                                                 gssapi_library_name
@@ -132,7 +136,6 @@
 // static
 std::unique_ptr<HttpAuthHandlerRegistryFactory>
 HttpAuthHandlerRegistryFactory::Create(
-    HostResolver* host_resolver,
     const HttpAuthPreferences* prefs,
     const std::vector<std::string>& auth_schemes
 #if defined(OS_CHROMEOS)
@@ -173,7 +176,6 @@
 
 #if BUILDFLAG(USE_KERBEROS)
   if (base::ContainsKey(auth_schemes_set, kNegotiateAuthScheme)) {
-    DCHECK(host_resolver);
     HttpAuthHandlerNegotiate::Factory* negotiate_factory =
         new HttpAuthHandlerNegotiate::Factory(negotiate_auth_system_factory);
 #if defined(OS_WIN)
@@ -185,7 +187,6 @@
     negotiate_factory->set_library(std::make_unique<GSSAPISharedLibrary>(""));
     negotiate_factory->set_allow_gssapi_library_load(allow_gssapi_library_load);
 #endif
-    negotiate_factory->set_host_resolver(host_resolver);
     registry_factory->RegisterSchemeFactory(kNegotiateAuthScheme,
                                             negotiate_factory);
   }
@@ -208,6 +209,7 @@
     CreateReason reason,
     int digest_nonce_count,
     const NetLogWithSource& net_log,
+    HostResolver* host_resolver,
     std::unique_ptr<HttpAuthHandler>* handler) {
   std::string scheme = challenge->scheme();
   if (scheme.empty()) {
@@ -223,7 +225,7 @@
   DCHECK(it->second);
   return it->second->CreateAuthHandler(challenge, target, ssl_info, origin,
                                        reason, digest_nonce_count, net_log,
-                                       handler);
+                                       host_resolver, handler);
 }
 
 }  // namespace net
diff --git a/net/http/http_auth_handler_factory.h b/net/http/http_auth_handler_factory.h
index 0c51a98..c0e0659 100644
--- a/net/http/http_auth_handler_factory.h
+++ b/net/http/http_auth_handler_factory.h
@@ -85,6 +85,12 @@
   // NOTE: This will apply to ALL |origin| values if the filters are empty.
   //
   // |*challenge| should not be reused after a call to |CreateAuthHandler()|,
+  //
+  // |host_resolver| is used by the Negotiate authentication handler to perform
+  // CNAME lookups to generate a Kerberos SPN for the server. If the "negotiate"
+  // scheme is used and the factory was created with
+  // |negotiate_disable_cname_lookup| false, |host_resolver| must not be null,
+  // and it must remain valid for the lifetime of the created |handler|.
   virtual int CreateAuthHandler(HttpAuthChallengeTokenizer* challenge,
                                 HttpAuth::Target target,
                                 const SSLInfo& ssl_info,
@@ -92,6 +98,7 @@
                                 CreateReason create_reason,
                                 int digest_nonce_count,
                                 const NetLogWithSource& net_log,
+                                HostResolver* host_resolver,
                                 std::unique_ptr<HttpAuthHandler>* handler) = 0;
 
   // Creates an HTTP authentication handler based on the authentication
@@ -104,6 +111,7 @@
                                   const SSLInfo& ssl_info,
                                   const GURL& origin,
                                   const NetLogWithSource& net_log,
+                                  HostResolver* host_resolver,
                                   std::unique_ptr<HttpAuthHandler>* handler);
 
   // Creates an HTTP authentication handler based on the authentication
@@ -117,6 +125,7 @@
       const GURL& origin,
       int digest_nonce_count,
       const NetLogWithSource& net_log,
+      HostResolver* host_resolver,
       std::unique_ptr<HttpAuthHandler>* handler);
 
   // Factory callback to create the auth system used for Negotiate
@@ -129,16 +138,9 @@
   // responsible for deleting the factory.
   // The default factory supports Basic, Digest, NTLM, and Negotiate schemes.
   //
-  // |resolver| is used by the Negotiate authentication handler to perform
-  // CNAME lookups to generate a Kerberos SPN for the server. It must be
-  // non-NULL.  |resolver| must remain valid for the lifetime of the
-  // HttpAuthHandlerRegistryFactory and any HttpAuthHandlers created by said
-  // factory.
-  //
   // |negotiate_auth_system_factory| is used to override the default auth system
   // used by the Negotiate authentication handler.
   static std::unique_ptr<HttpAuthHandlerRegistryFactory> CreateDefault(
-      HostResolver* resolver,
       const HttpAuthPreferences* prefs = nullptr
 #if defined(OS_CHROMEOS)
       ,
@@ -193,11 +195,6 @@
 
   // Creates an HttpAuthHandlerRegistryFactory.
   //
-  // |host_resolver| is used by the Negotiate authentication handler to perform
-  // CNAME lookups to generate a Kerberos SPN for the server. If the "negotiate"
-  // scheme is used and |negotiate_disable_cname_lookup| is false,
-  // |host_resolver| must not be NULL.
-  //
   // |prefs| is a pointer to the (single) authentication preferences object.
   // That object tracks preference, and hence policy, updates relevant to HTTP
   // authentication, and provides the current values of the preferences.
@@ -208,7 +205,6 @@
   // |negotiate_auth_system_factory| is used to override the default auth system
   // used by the Negotiate authentication handler.
   static std::unique_ptr<HttpAuthHandlerRegistryFactory> Create(
-      HostResolver* host_resolver,
       const HttpAuthPreferences* prefs,
       const std::vector<std::string>& auth_schemes
 #if defined(OS_CHROMEOS)
@@ -227,6 +223,12 @@
 
   // Creates an auth handler by dispatching out to the registered factories
   // based on the first token in |challenge|.
+  //
+  // |host_resolver| is used by the Negotiate authentication handler to perform
+  // CNAME lookups to generate a Kerberos SPN for the server. If the "negotiate"
+  // scheme is used and the factory was created with
+  // |negotiate_disable_cname_lookup| false, |host_resolver| must not be null,
+  // and it must remain valid for the lifetime of the created |handler|.
   int CreateAuthHandler(HttpAuthChallengeTokenizer* challenge,
                         HttpAuth::Target target,
                         const SSLInfo& ssl_info,
@@ -234,6 +236,7 @@
                         CreateReason reason,
                         int digest_nonce_count,
                         const NetLogWithSource& net_log,
+                        HostResolver* host_resolver,
                         std::unique_ptr<HttpAuthHandler>* handler) override;
 
  private:
diff --git a/net/http/http_auth_handler_factory_unittest.cc b/net/http/http_auth_handler_factory_unittest.cc
index f371018..b6ae977 100644
--- a/net/http/http_auth_handler_factory_unittest.cc
+++ b/net/http/http_auth_handler_factory_unittest.cc
@@ -8,6 +8,7 @@
 
 #include "build/build_config.h"
 #include "net/base/net_errors.h"
+#include "net/dns/host_resolver.h"
 #include "net/dns/mock_host_resolver.h"
 #include "net/http/http_auth_handler.h"
 #include "net/http/http_auth_scheme.h"
@@ -40,6 +41,7 @@
                         CreateReason reason,
                         int nonce_count,
                         const NetLogWithSource& net_log,
+                        HostResolver* host_resolver,
                         std::unique_ptr<HttpAuthHandler>* handler) override {
     handler->reset();
     return return_code_;
@@ -67,55 +69,60 @@
   MockHttpAuthHandlerFactory* mock_factory_digest_replace =
       new MockHttpAuthHandlerFactory(kDigestReturnCodeReplace);
 
+  auto host_resovler = std::make_unique<MockHostResolver>();
   std::unique_ptr<HttpAuthHandler> handler;
 
   // No schemes should be supported in the beginning.
   EXPECT_EQ(ERR_UNSUPPORTED_AUTH_SCHEME,
             registry_factory.CreateAuthHandlerFromString(
                 "Basic", HttpAuth::AUTH_SERVER, null_ssl_info, gurl,
-                NetLogWithSource(), &handler));
+                NetLogWithSource(), host_resovler.get(), &handler));
 
   // Test what happens with a single scheme.
   registry_factory.RegisterSchemeFactory("Basic", mock_factory_basic);
-  EXPECT_EQ(kBasicReturnCode, registry_factory.CreateAuthHandlerFromString(
-                                  "Basic", HttpAuth::AUTH_SERVER, null_ssl_info,
-                                  gurl, NetLogWithSource(), &handler));
+  EXPECT_EQ(kBasicReturnCode,
+            registry_factory.CreateAuthHandlerFromString(
+                "Basic", HttpAuth::AUTH_SERVER, null_ssl_info, gurl,
+                NetLogWithSource(), host_resovler.get(), &handler));
   EXPECT_EQ(ERR_UNSUPPORTED_AUTH_SCHEME,
             registry_factory.CreateAuthHandlerFromString(
                 "Digest", HttpAuth::AUTH_SERVER, null_ssl_info, gurl,
-                NetLogWithSource(), &handler));
+                NetLogWithSource(), host_resovler.get(), &handler));
 
   // Test multiple schemes
   registry_factory.RegisterSchemeFactory("Digest", mock_factory_digest);
-  EXPECT_EQ(kBasicReturnCode, registry_factory.CreateAuthHandlerFromString(
-                                  "Basic", HttpAuth::AUTH_SERVER, null_ssl_info,
-                                  gurl, NetLogWithSource(), &handler));
+  EXPECT_EQ(kBasicReturnCode,
+            registry_factory.CreateAuthHandlerFromString(
+                "Basic", HttpAuth::AUTH_SERVER, null_ssl_info, gurl,
+                NetLogWithSource(), host_resovler.get(), &handler));
   EXPECT_EQ(kDigestReturnCode,
             registry_factory.CreateAuthHandlerFromString(
                 "Digest", HttpAuth::AUTH_SERVER, null_ssl_info, gurl,
-                NetLogWithSource(), &handler));
+                NetLogWithSource(), host_resovler.get(), &handler));
 
   // Test case-insensitivity
-  EXPECT_EQ(kBasicReturnCode, registry_factory.CreateAuthHandlerFromString(
-                                  "basic", HttpAuth::AUTH_SERVER, null_ssl_info,
-                                  gurl, NetLogWithSource(), &handler));
+  EXPECT_EQ(kBasicReturnCode,
+            registry_factory.CreateAuthHandlerFromString(
+                "basic", HttpAuth::AUTH_SERVER, null_ssl_info, gurl,
+                NetLogWithSource(), host_resovler.get(), &handler));
 
   // Test replacement of existing auth scheme
   registry_factory.RegisterSchemeFactory("Digest", mock_factory_digest_replace);
-  EXPECT_EQ(kBasicReturnCode, registry_factory.CreateAuthHandlerFromString(
-                                  "Basic", HttpAuth::AUTH_SERVER, null_ssl_info,
-                                  gurl, NetLogWithSource(), &handler));
+  EXPECT_EQ(kBasicReturnCode,
+            registry_factory.CreateAuthHandlerFromString(
+                "Basic", HttpAuth::AUTH_SERVER, null_ssl_info, gurl,
+                NetLogWithSource(), host_resovler.get(), &handler));
   EXPECT_EQ(kDigestReturnCodeReplace,
             registry_factory.CreateAuthHandlerFromString(
                 "Digest", HttpAuth::AUTH_SERVER, null_ssl_info, gurl,
-                NetLogWithSource(), &handler));
+                NetLogWithSource(), host_resovler.get(), &handler));
 }
 
 TEST(HttpAuthHandlerFactoryTest, DefaultFactory) {
   std::unique_ptr<HostResolver> host_resolver(new MockHostResolver());
   MockAllowHttpAuthPreferences http_auth_preferences;
   std::unique_ptr<HttpAuthHandlerRegistryFactory> http_auth_handler_factory(
-      HttpAuthHandlerFactory::CreateDefault(host_resolver.get()));
+      HttpAuthHandlerFactory::CreateDefault());
   http_auth_handler_factory->SetHttpAuthPreferences(kNegotiateAuthScheme,
                                                     &http_auth_preferences);
   GURL server_origin("http://www.example.com");
@@ -125,7 +132,7 @@
     std::unique_ptr<HttpAuthHandler> handler;
     int rv = http_auth_handler_factory->CreateAuthHandlerFromString(
         "Basic realm=\"FooBar\"", HttpAuth::AUTH_SERVER, null_ssl_info,
-        server_origin, NetLogWithSource(), &handler);
+        server_origin, NetLogWithSource(), host_resolver.get(), &handler);
     EXPECT_THAT(rv, IsOk());
     ASSERT_FALSE(handler.get() == NULL);
     EXPECT_EQ(HttpAuth::AUTH_SCHEME_BASIC, handler->auth_scheme());
@@ -138,7 +145,7 @@
     std::unique_ptr<HttpAuthHandler> handler;
     int rv = http_auth_handler_factory->CreateAuthHandlerFromString(
         "UNSUPPORTED realm=\"FooBar\"", HttpAuth::AUTH_SERVER, null_ssl_info,
-        server_origin, NetLogWithSource(), &handler);
+        server_origin, NetLogWithSource(), host_resolver.get(), &handler);
     EXPECT_THAT(rv, IsError(ERR_UNSUPPORTED_AUTH_SCHEME));
     EXPECT_TRUE(handler.get() == NULL);
   }
@@ -146,7 +153,8 @@
     std::unique_ptr<HttpAuthHandler> handler;
     int rv = http_auth_handler_factory->CreateAuthHandlerFromString(
         "Digest realm=\"FooBar\", nonce=\"xyz\"", HttpAuth::AUTH_PROXY,
-        null_ssl_info, proxy_origin, NetLogWithSource(), &handler);
+        null_ssl_info, proxy_origin, NetLogWithSource(), host_resolver.get(),
+        &handler);
     EXPECT_THAT(rv, IsOk());
     ASSERT_FALSE(handler.get() == NULL);
     EXPECT_EQ(HttpAuth::AUTH_SCHEME_DIGEST, handler->auth_scheme());
@@ -159,7 +167,7 @@
     std::unique_ptr<HttpAuthHandler> handler;
     int rv = http_auth_handler_factory->CreateAuthHandlerFromString(
         "NTLM", HttpAuth::AUTH_SERVER, null_ssl_info, server_origin,
-        NetLogWithSource(), &handler);
+        NetLogWithSource(), host_resolver.get(), &handler);
     EXPECT_THAT(rv, IsOk());
     ASSERT_FALSE(handler.get() == NULL);
     EXPECT_EQ(HttpAuth::AUTH_SCHEME_NTLM, handler->auth_scheme());
@@ -172,7 +180,7 @@
     std::unique_ptr<HttpAuthHandler> handler;
     int rv = http_auth_handler_factory->CreateAuthHandlerFromString(
         "Negotiate", HttpAuth::AUTH_SERVER, null_ssl_info, server_origin,
-        NetLogWithSource(), &handler);
+        NetLogWithSource(), host_resolver.get(), &handler);
 // Note the default factory doesn't support Kerberos on Android
 #if BUILDFLAG(USE_KERBEROS) && !defined(OS_ANDROID)
     EXPECT_THAT(rv, IsOk());
diff --git a/net/http/http_auth_handler_mock.cc b/net/http/http_auth_handler_mock.cc
index fa245cb..850f20a 100644
--- a/net/http/http_auth_handler_mock.cc
+++ b/net/http/http_auth_handler_mock.cc
@@ -14,6 +14,7 @@
 #include "base/strings/string_util.h"
 #include "base/threading/thread_task_runner_handle.h"
 #include "net/base/net_errors.h"
+#include "net/dns/host_resolver.h"
 #include "net/http/http_auth_challenge_tokenizer.h"
 #include "net/http/http_request_info.h"
 #include "testing/gmock/include/gmock/gmock.h"
@@ -166,6 +167,7 @@
     CreateReason reason,
     int nonce_count,
     const NetLogWithSource& net_log,
+    HostResolver* host_resolver,
     std::unique_ptr<HttpAuthHandler>* handler) {
   if (handlers_[target].empty())
     return ERR_UNEXPECTED;
diff --git a/net/http/http_auth_handler_mock.h b/net/http/http_auth_handler_mock.h
index f208309..ffbadb5 100644
--- a/net/http/http_auth_handler_mock.h
+++ b/net/http/http_auth_handler_mock.h
@@ -50,6 +50,7 @@
                           CreateReason reason,
                           int nonce_count,
                           const NetLogWithSource& net_log,
+                          HostResolver* host_resolver,
                           std::unique_ptr<HttpAuthHandler>* handler) override;
 
    private:
diff --git a/net/http/http_auth_handler_negotiate.cc b/net/http/http_auth_handler_negotiate.cc
index 1356fdb9..7540fae 100644
--- a/net/http/http_auth_handler_negotiate.cc
+++ b/net/http/http_auth_handler_negotiate.cc
@@ -17,6 +17,7 @@
 #include "net/base/host_port_pair.h"
 #include "net/base/net_errors.h"
 #include "net/cert/x509_util.h"
+#include "net/dns/host_resolver.h"
 #include "net/http/http_auth_filter.h"
 #include "net/http/http_auth_preferences.h"
 #include "net/log/net_log_capture_mode.h"
@@ -74,11 +75,6 @@
 
 HttpAuthHandlerNegotiate::Factory::~Factory() = default;
 
-void HttpAuthHandlerNegotiate::Factory::set_host_resolver(
-    HostResolver* resolver) {
-  resolver_ = resolver;
-}
-
 #if !defined(OS_ANDROID) && defined(OS_POSIX)
 const std::string& HttpAuthHandlerNegotiate::Factory::GetLibraryNameForTesting()
     const {
@@ -94,6 +90,7 @@
     CreateReason reason,
     int digest_nonce_count,
     const NetLogWithSource& net_log,
+    HostResolver* host_resolver,
     std::unique_ptr<HttpAuthHandler>* handler) {
 #if defined(OS_WIN)
   if (is_unsupported_ || reason == CREATE_PREEMPTIVE)
@@ -111,7 +108,7 @@
   std::unique_ptr<HttpAuthHandler> tmp_handler(new HttpAuthHandlerNegotiate(
       CreateAuthSystem(auth_library_.get(), max_token_length_,
                        http_auth_preferences(), negotiate_auth_system_factory_),
-      http_auth_preferences(), resolver_));
+      http_auth_preferences(), host_resolver));
 #elif defined(OS_ANDROID)
   if (is_unsupported_ || !http_auth_preferences() ||
       http_auth_preferences()->AuthAndroidNegotiateAccountType().empty() ||
@@ -121,7 +118,7 @@
   //                 method and only constructing when valid.
   std::unique_ptr<HttpAuthHandler> tmp_handler(new HttpAuthHandlerNegotiate(
       CreateAuthSystem(http_auth_preferences(), negotiate_auth_system_factory_),
-      http_auth_preferences(), resolver_));
+      http_auth_preferences(), host_resolver));
 #elif defined(OS_POSIX)
   if (is_unsupported_ || !allow_gssapi_library_load_)
     return ERR_UNSUPPORTED_AUTH_SCHEME;
@@ -134,7 +131,7 @@
   std::unique_ptr<HttpAuthHandler> tmp_handler(new HttpAuthHandlerNegotiate(
       CreateAuthSystem(auth_library_.get(), http_auth_preferences(),
                        negotiate_auth_system_factory_),
-      http_auth_preferences(), resolver_));
+      http_auth_preferences(), host_resolver));
 #endif
   if (!tmp_handler->InitFromChallenge(challenge, target, ssl_info, origin,
                                       net_log))
diff --git a/net/http/http_auth_handler_negotiate.h b/net/http/http_auth_handler_negotiate.h
index a1f7fa8..3e90a33 100644
--- a/net/http/http_auth_handler_negotiate.h
+++ b/net/http/http_auth_handler_negotiate.h
@@ -44,11 +44,9 @@
 
   class NET_EXPORT_PRIVATE Factory : public HttpAuthHandlerFactory {
    public:
-    Factory(NegotiateAuthSystemFactory negotiate_auth_system_factory);
+    explicit Factory(NegotiateAuthSystemFactory negotiate_auth_system_factory);
     ~Factory() override;
 
-    void set_host_resolver(HostResolver* host_resolver);
-
 #if !defined(OS_ANDROID)
     // Sets the system library to use, thereby assuming ownership of
     // |auth_library|.
@@ -76,11 +74,11 @@
                           CreateReason reason,
                           int digest_nonce_count,
                           const NetLogWithSource& net_log,
+                          HostResolver* host_resolver,
                           std::unique_ptr<HttpAuthHandler>* handler) override;
 
    private:
     NegotiateAuthSystemFactory negotiate_auth_system_factory_;
-    HostResolver* resolver_ = nullptr;
 #if defined(OS_WIN)
     ULONG max_token_length_ = 0;
 #endif
diff --git a/net/http/http_auth_handler_negotiate_unittest.cc b/net/http/http_auth_handler_negotiate_unittest.cc
index 7e1dc868..841e6a5 100644
--- a/net/http/http_auth_handler_negotiate_unittest.cc
+++ b/net/http/http_auth_handler_negotiate_unittest.cc
@@ -69,7 +69,6 @@
 #if defined(OS_WIN) || (defined(OS_POSIX) && !defined(OS_ANDROID))
     factory_->set_library(base::WrapUnique(auth_library_));
 #endif
-    factory_->set_host_resolver(resolver_.get());
   }
 
 #if defined(OS_ANDROID)
@@ -226,7 +225,7 @@
     SSLInfo null_ssl_info;
     int rv = factory_->CreateAuthHandlerFromString(
         "Negotiate", HttpAuth::AUTH_SERVER, null_ssl_info, gurl,
-        NetLogWithSource(), &generic_handler);
+        NetLogWithSource(), resolver_.get(), &generic_handler);
     if (rv != OK)
       return rv;
     HttpAuthHandlerNegotiate* negotiate_handler =
@@ -437,7 +436,6 @@
                               -> std::unique_ptr<HttpNegotiateAuthSystem> {
         return std::make_unique<TestAuthSystem>();
       }));
-  negotiate_factory->set_host_resolver(resolver());
   negotiate_factory->set_http_auth_preferences(http_auth_preferences());
 #if !defined(OS_ANDROID)
   auto auth_library = std::make_unique<MockAuthLibrary>();
@@ -449,7 +447,7 @@
   std::unique_ptr<HttpAuthHandler> handler;
   EXPECT_EQ(OK, negotiate_factory->CreateAuthHandlerFromString(
                     "Negotiate", HttpAuth::AUTH_SERVER, SSLInfo(), gurl,
-                    NetLogWithSource(), &handler));
+                    NetLogWithSource(), resolver(), &handler));
   EXPECT_TRUE(handler);
 
   TestCompletionCallback callback;
diff --git a/net/http/http_auth_handler_ntlm.h b/net/http/http_auth_handler_ntlm.h
index 92dad89e..2d9f0f9 100644
--- a/net/http/http_auth_handler_ntlm.h
+++ b/net/http/http_auth_handler_ntlm.h
@@ -27,6 +27,7 @@
 #include "net/ntlm/ntlm_client.h"
 #endif
 
+#include <memory>
 #include <string>
 #include <vector>
 
@@ -56,6 +57,7 @@
                           CreateReason reason,
                           int digest_nonce_count,
                           const NetLogWithSource& net_log,
+                          HostResolver* host_resolver,
                           std::unique_ptr<HttpAuthHandler>* handler) override;
 #if defined(NTLM_SSPI)
     // Set the SSPILibrary to use. Typically the only callers which need to use
@@ -66,6 +68,7 @@
       sspi_library_.reset(sspi_library);
     }
 #endif  // defined(NTLM_SSPI)
+
    private:
 #if defined(NTLM_SSPI)
     ULONG max_token_length_;
diff --git a/net/http/http_auth_handler_ntlm_portable.cc b/net/http/http_auth_handler_ntlm_portable.cc
index cc350d8..1b13f78 100644
--- a/net/http/http_auth_handler_ntlm_portable.cc
+++ b/net/http/http_auth_handler_ntlm_portable.cc
@@ -8,6 +8,7 @@
 #include "base/time/time.h"
 #include "net/base/net_errors.h"
 #include "net/base/network_interfaces.h"
+#include "net/dns/host_resolver.h"
 #include "net/http/http_auth_preferences.h"
 
 namespace net {
@@ -119,6 +120,7 @@
     CreateReason reason,
     int digest_nonce_count,
     const NetLogWithSource& net_log,
+    HostResolver* host_resolver,
     std::unique_ptr<HttpAuthHandler>* handler) {
   if (reason == CREATE_PREEMPTIVE)
     return ERR_UNSUPPORTED_AUTH_SCHEME;
diff --git a/net/http/http_auth_handler_ntlm_portable_unittest.cc b/net/http/http_auth_handler_ntlm_portable_unittest.cc
index 5614330b..ff42aff 100644
--- a/net/http/http_auth_handler_ntlm_portable_unittest.cc
+++ b/net/http/http_auth_handler_ntlm_portable_unittest.cc
@@ -52,7 +52,7 @@
 
     return factory_->CreateAuthHandlerFromString(
         "NTLM", HttpAuth::AUTH_SERVER, null_ssl_info, gurl, NetLogWithSource(),
-        &auth_handler_);
+        nullptr, &auth_handler_);
   }
 
   std::string CreateNtlmAuthHeader(base::span<const uint8_t> buffer) {
diff --git a/net/http/http_auth_handler_ntlm_win.cc b/net/http/http_auth_handler_ntlm_win.cc
index 70f8d317..a92b013 100644
--- a/net/http/http_auth_handler_ntlm_win.cc
+++ b/net/http/http_auth_handler_ntlm_win.cc
@@ -11,6 +11,7 @@
 
 #include "base/strings/string_util.h"
 #include "net/base/net_errors.h"
+#include "net/dns/host_resolver.h"
 #include "net/http/http_auth_preferences.h"
 #include "net/http/http_auth_sspi_win.h"
 
@@ -55,6 +56,7 @@
     CreateReason reason,
     int digest_nonce_count,
     const NetLogWithSource& net_log,
+    HostResolver* host_resolver,
     std::unique_ptr<HttpAuthHandler>* handler) {
   if (is_unsupported_ || reason == CREATE_PREEMPTIVE)
     return ERR_UNSUPPORTED_AUTH_SCHEME;
diff --git a/net/http/http_auth_unittest.cc b/net/http/http_auth_unittest.cc
index 43a7514..c4a0c32 100644
--- a/net/http/http_auth_unittest.cc
+++ b/net/http/http_auth_unittest.cc
@@ -127,7 +127,7 @@
   MockAllowHttpAuthPreferences http_auth_preferences;
   std::unique_ptr<HostResolver> host_resolver(new MockHostResolver());
   std::unique_ptr<HttpAuthHandlerRegistryFactory> http_auth_handler_factory(
-      HttpAuthHandlerFactory::CreateDefault(host_resolver.get()));
+      HttpAuthHandlerFactory::CreateDefault());
   http_auth_handler_factory->SetHttpAuthPreferences(kNegotiateAuthScheme,
                                                     &http_auth_preferences);
 
@@ -143,7 +143,7 @@
     HttpAuth::ChooseBestChallenge(http_auth_handler_factory.get(), *headers,
                                   null_ssl_info, HttpAuth::AUTH_SERVER, origin,
                                   disabled_schemes, NetLogWithSource(),
-                                  &handler);
+                                  host_resolver.get(), &handler);
 
     if (handler.get()) {
       EXPECT_EQ(tests[i].challenge_scheme, handler->auth_scheme());
diff --git a/net/http/http_network_session.cc b/net/http/http_network_session.cc
index a8fd266..2cf4002 100644
--- a/net/http/http_network_session.cc
+++ b/net/http/http_network_session.cc
@@ -18,6 +18,7 @@
 #include "base/trace_event/memory_dump_request_args.h"
 #include "base/trace_event/process_memory_dump.h"
 #include "base/values.h"
+#include "net/dns/host_resolver.h"
 #include "net/http/http_auth_handler_factory.h"
 #include "net/http/http_response_body_drainer.h"
 #include "net/http/http_stream_factory.h"
@@ -190,6 +191,7 @@
       http_server_properties_(context.http_server_properties),
       cert_verifier_(context.cert_verifier),
       http_auth_handler_factory_(context.http_auth_handler_factory),
+      host_resolver_(context.host_resolver),
 #if BUILDFLAG(ENABLE_REPORTING)
       reporting_service_(context.reporting_service),
       network_error_logging_service_(context.network_error_logging_service),
@@ -311,23 +313,10 @@
   response_drainers_.erase(drainer);
 }
 
-TransportClientSocketPool* HttpNetworkSession::GetTransportSocketPool(
-    SocketPoolType pool_type) {
-  return GetSocketPoolManager(pool_type)->GetTransportSocketPool();
-}
-
-TransportClientSocketPool* HttpNetworkSession::GetSocketPoolForSOCKSProxy(
+TransportClientSocketPool* HttpNetworkSession::GetSocketPool(
     SocketPoolType pool_type,
-    const ProxyServer& socks_proxy) {
-  return GetSocketPoolManager(pool_type)->GetSocketPoolForSOCKSProxy(
-      socks_proxy);
-}
-
-TransportClientSocketPool* HttpNetworkSession::GetSocketPoolForHTTPLikeProxy(
-    SocketPoolType pool_type,
-    const ProxyServer& http_proxy) {
-  return GetSocketPoolManager(pool_type)->GetSocketPoolForHTTPLikeProxy(
-      http_proxy);
+    const ProxyServer& proxy_server) {
+  return GetSocketPoolManager(pool_type)->GetSocketPool(proxy_server);
 }
 
 std::unique_ptr<base::Value> HttpNetworkSession::SocketPoolInfoToValue() const {
diff --git a/net/http/http_network_session.h b/net/http/http_network_session.h
index 1de39d9..3b0bc86a 100644
--- a/net/http/http_network_session.h
+++ b/net/http/http_network_session.h
@@ -26,7 +26,6 @@
 #include "net/base/host_mapping_rules.h"
 #include "net/base/host_port_pair.h"
 #include "net/base/net_export.h"
-#include "net/dns/host_resolver.h"
 #include "net/http/http_auth_cache.h"
 #include "net/http/http_stream_factory.h"
 #include "net/net_buildflags.h"
@@ -296,14 +295,11 @@
   // Removes the drainer from the session. Does not dispose of it.
   void RemoveResponseDrainer(HttpResponseBodyDrainer* drainer);
 
-  TransportClientSocketPool* GetTransportSocketPool(SocketPoolType pool_type);
-  // Currently only works for SOCKS proxies.
-  TransportClientSocketPool* GetSocketPoolForSOCKSProxy(
-      SocketPoolType pool_type,
-      const ProxyServer& socks_proxy);
-  TransportClientSocketPool* GetSocketPoolForHTTPLikeProxy(
-      SocketPoolType pool_type,
-      const ProxyServer& http_proxy);
+  // Returns the socket pool of the given type for use with the specified
+  // ProxyServer. Use ProxyServer::Direct() to get the pool for use with direct
+  // connections.
+  TransportClientSocketPool* GetSocketPool(SocketPoolType pool_type,
+                                           const ProxyServer& proxy_server);
 
   CertVerifier* cert_verifier() { return cert_verifier_; }
   ProxyResolutionService* proxy_resolution_service() {
@@ -327,6 +323,7 @@
   NetLog* net_log() {
     return net_log_;
   }
+  HostResolver* host_resolver() { return host_resolver_; }
 #if BUILDFLAG(ENABLE_REPORTING)
   ReportingService* reporting_service() const { return reporting_service_; }
   NetworkErrorLoggingService* network_error_logging_service() const {
@@ -393,6 +390,7 @@
   HttpServerProperties* const http_server_properties_;
   CertVerifier* const cert_verifier_;
   HttpAuthHandlerFactory* const http_auth_handler_factory_;
+  HostResolver* const host_resolver_;
 
 #if BUILDFLAG(ENABLE_REPORTING)
   ReportingService* const reporting_service_;
diff --git a/net/http/http_network_transaction.cc b/net/http/http_network_transaction.cc
index 50a99c15..359d488 100644
--- a/net/http/http_network_transaction.cc
+++ b/net/http/http_network_transaction.cc
@@ -947,11 +947,9 @@
     return OK;
   HttpAuth::Target target = HttpAuth::AUTH_PROXY;
   if (!auth_controllers_[target].get())
-    auth_controllers_[target] =
-        new HttpAuthController(target,
-                               AuthURL(target),
-                               session_->http_auth_cache(),
-                               session_->http_auth_handler_factory());
+    auth_controllers_[target] = new HttpAuthController(
+        target, AuthURL(target), session_->http_auth_cache(),
+        session_->http_auth_handler_factory(), session_->host_resolver());
   return auth_controllers_[target]->MaybeGenerateAuthToken(request_,
                                                            io_callback_,
                                                            net_log_);
@@ -968,11 +966,9 @@
   next_state_ = STATE_GENERATE_SERVER_AUTH_TOKEN_COMPLETE;
   HttpAuth::Target target = HttpAuth::AUTH_SERVER;
   if (!auth_controllers_[target].get()) {
-    auth_controllers_[target] =
-        new HttpAuthController(target,
-                               AuthURL(target),
-                               session_->http_auth_cache(),
-                               session_->http_auth_handler_factory());
+    auth_controllers_[target] = new HttpAuthController(
+        target, AuthURL(target), session_->http_auth_cache(),
+        session_->http_auth_handler_factory(), session_->host_resolver());
     if (request_->load_flags & LOAD_DO_NOT_USE_EMBEDDED_IDENTITY)
       auth_controllers_[target]->DisableEmbeddedIdentity();
   }
diff --git a/net/http/http_network_transaction_unittest.cc b/net/http/http_network_transaction_unittest.cc
index 4f637a8b..06726c2 100644
--- a/net/http/http_network_transaction_unittest.cc
+++ b/net/http/http_network_transaction_unittest.cc
@@ -152,12 +152,16 @@
     "Alt-Svc: h2=\"mail.example.org:443\"\r\n";
 
 int GetIdleSocketCountInTransportSocketPool(HttpNetworkSession* session) {
-  return session->GetTransportSocketPool(HttpNetworkSession::NORMAL_SOCKET_POOL)
+  return session
+      ->GetSocketPool(HttpNetworkSession::NORMAL_SOCKET_POOL,
+                      ProxyServer::Direct())
       ->IdleSocketCount();
 }
 
 bool IsTransportSocketPoolStalled(HttpNetworkSession* session) {
-  return session->GetTransportSocketPool(HttpNetworkSession::NORMAL_SOCKET_POOL)
+  return session
+      ->GetSocketPool(HttpNetworkSession::NORMAL_SOCKET_POOL,
+                      ProxyServer::Direct())
       ->IsStalled();
 }
 
@@ -5108,7 +5112,7 @@
     // pass.
     EXPECT_EQ(test_case.expected_idle_socks4_sockets,
               session
-                  ->GetSocketPoolForSOCKSProxy(
+                  ->GetSocketPool(
                       HttpNetworkSession::NORMAL_SOCKET_POOL,
                       ProxyServer(ProxyServer::SCHEME_SOCKS4,
                                   SameProxyWithDifferentSchemesProxyResolver::
@@ -5116,7 +5120,7 @@
                   ->IdleSocketCount());
     EXPECT_EQ(test_case.expected_idle_socks5_sockets,
               session
-                  ->GetSocketPoolForSOCKSProxy(
+                  ->GetSocketPool(
                       HttpNetworkSession::NORMAL_SOCKET_POOL,
                       ProxyServer(ProxyServer::SCHEME_SOCKS5,
                                   SameProxyWithDifferentSchemesProxyResolver::
@@ -5124,7 +5128,7 @@
                   ->IdleSocketCount());
     EXPECT_EQ(test_case.expected_idle_http_sockets,
               session
-                  ->GetSocketPoolForHTTPLikeProxy(
+                  ->GetSocketPool(
                       HttpNetworkSession::NORMAL_SOCKET_POOL,
                       ProxyServer(ProxyServer::SCHEME_HTTP,
                                   SameProxyWithDifferentSchemesProxyResolver::
@@ -5132,7 +5136,7 @@
                   ->IdleSocketCount());
     EXPECT_EQ(test_case.expected_idle_https_sockets,
               session
-                  ->GetSocketPoolForHTTPLikeProxy(
+                  ->GetSocketPool(
                       HttpNetworkSession::NORMAL_SOCKET_POOL,
                       ProxyServer(ProxyServer::SCHEME_HTTPS,
                                   SameProxyWithDifferentSchemesProxyResolver::
@@ -5140,7 +5144,7 @@
                   ->IdleSocketCount());
     EXPECT_EQ(test_case.expected_idle_trusted_https_sockets,
               session
-                  ->GetSocketPoolForHTTPLikeProxy(
+                  ->GetSocketPool(
                       HttpNetworkSession::NORMAL_SOCKET_POOL,
                       ProxyServer(ProxyServer::SCHEME_HTTPS,
                                   SameProxyWithDifferentSchemesProxyResolver::
@@ -11242,7 +11246,8 @@
     CaptureGroupNameTransportSocketPool* transport_conn_pool =
         new CaptureGroupNameTransportSocketPool(nullptr, nullptr);
     auto mock_pool_manager = std::make_unique<MockClientSocketPoolManager>();
-    mock_pool_manager->SetTransportSocketPool(transport_conn_pool);
+    mock_pool_manager->SetSocketPool(ProxyServer::Direct(),
+                                     base::WrapUnique(transport_conn_pool));
     peer.SetClientSocketPoolManager(std::move(mock_pool_manager));
 
     EXPECT_EQ(ERR_IO_PENDING,
@@ -11291,8 +11296,8 @@
     CaptureGroupNameTransportSocketPool* http_proxy_pool =
         new CaptureGroupNameTransportSocketPool(NULL, NULL);
     auto mock_pool_manager = std::make_unique<MockClientSocketPoolManager>();
-    mock_pool_manager->SetSocketPoolForHTTPProxy(
-        proxy_server, base::WrapUnique(http_proxy_pool));
+    mock_pool_manager->SetSocketPool(proxy_server,
+                                     base::WrapUnique(http_proxy_pool));
     peer.SetClientSocketPoolManager(std::move(mock_pool_manager));
 
     EXPECT_EQ(ERR_IO_PENDING,
@@ -11354,8 +11359,8 @@
     CaptureGroupNameTransportSocketPool* socks_conn_pool =
         new CaptureGroupNameTransportSocketPool(NULL, NULL);
     auto mock_pool_manager = std::make_unique<MockClientSocketPoolManager>();
-    mock_pool_manager->SetSocketPoolForProxy(proxy_server,
-                                             base::WrapUnique(socks_conn_pool));
+    mock_pool_manager->SetSocketPool(proxy_server,
+                                     base::WrapUnique(socks_conn_pool));
     peer.SetClientSocketPoolManager(std::move(mock_pool_manager));
 
     HttpNetworkTransaction trans(DEFAULT_PRIORITY, session.get());
@@ -14380,7 +14385,8 @@
       nullptr /* socket_performance_watcher_factory */,
       nullptr /* network_quality_estimator */, session_deps_.net_log);
   auto mock_pool_manager = std::make_unique<MockClientSocketPoolManager>();
-  mock_pool_manager->SetTransportSocketPool(transport_pool);
+  mock_pool_manager->SetSocketPool(ProxyServer::Direct(),
+                                   base::WrapUnique(transport_pool));
   session_peer.SetClientSocketPoolManager(std::move(mock_pool_manager));
 
   HttpNetworkTransaction trans(DEFAULT_PRIORITY, session.get());
@@ -19334,7 +19340,7 @@
   // Insecure request does not generate a report
   histograms.ExpectBucketCount(
       NetworkErrorLoggingService::kRequestOutcomeHistogram,
-      NetworkErrorLoggingService::RequestOutcome::DISCARDED_INSECURE_ORIGIN, 1);
+      NetworkErrorLoggingService::RequestOutcome::kDiscardedInsecureOrigin, 1);
 
   EXPECT_EQ(1u, network_error_logging_service()->errors().size());
 }
@@ -19370,7 +19376,7 @@
   // policy for the origin.
   histograms.ExpectBucketCount(
       NetworkErrorLoggingService::kRequestOutcomeHistogram,
-      NetworkErrorLoggingService::RequestOutcome::DISCARDED_INSECURE_ORIGIN, 1);
+      NetworkErrorLoggingService::RequestOutcome::kDiscardedInsecureOrigin, 1);
 
   EXPECT_EQ(1u, network_error_logging_service()->errors().size());
 }
diff --git a/net/http/http_proxy_client_socket.cc b/net/http/http_proxy_client_socket.cc
index 68349676..710f958 100644
--- a/net/http/http_proxy_client_socket.cc
+++ b/net/http/http_proxy_client_socket.cc
@@ -22,7 +22,6 @@
 #include "net/http/http_stream_parser.h"
 #include "net/log/net_log.h"
 #include "net/log/net_log_event_type.h"
-#include "net/socket/client_socket_handle.h"
 #include "net/socket/stream_socket.h"
 #include "url/gurl.h"
 
@@ -31,7 +30,7 @@
 const int HttpProxyClientSocket::kDrainBodyBufferSize;
 
 HttpProxyClientSocket::HttpProxyClientSocket(
-    std::unique_ptr<StreamSocket> stream_socket,
+    std::unique_ptr<StreamSocket> socket,
     const std::string& user_agent,
     const HostPortPair& endpoint,
     const ProxyServer& proxy_server,
@@ -45,8 +44,7 @@
     : io_callback_(base::BindRepeating(&HttpProxyClientSocket::OnIOComplete,
                                        base::Unretained(this))),
       next_state_(STATE_NONE),
-      stream_socket_(std::move(stream_socket)),
-      socket_(stream_socket_.get()),
+      socket_(std::move(socket)),
       is_reused_(false),
       endpoint_(endpoint),
       auth_(http_auth_controller),
@@ -66,42 +64,6 @@
                                      user_agent);
 }
 
-HttpProxyClientSocket::HttpProxyClientSocket(
-    std::unique_ptr<ClientSocketHandle> client_socket_handle,
-    const std::string& user_agent,
-    const HostPortPair& endpoint,
-    const ProxyServer& proxy_server,
-    HttpAuthController* http_auth_controller,
-    bool tunnel,
-    bool using_spdy,
-    NextProto negotiated_protocol,
-    ProxyDelegate* proxy_delegate,
-    bool is_https_proxy,
-    const NetworkTrafficAnnotationTag& traffic_annotation)
-    : io_callback_(base::BindRepeating(&HttpProxyClientSocket::OnIOComplete,
-                                       base::Unretained(this))),
-      next_state_(STATE_NONE),
-      client_socket_handle_(std::move(client_socket_handle)),
-      socket_(client_socket_handle_->socket()),
-      is_reused_(client_socket_handle_->is_reused()),
-      endpoint_(endpoint),
-      auth_(http_auth_controller),
-      tunnel_(tunnel),
-      using_spdy_(using_spdy),
-      negotiated_protocol_(negotiated_protocol),
-      is_https_proxy_(is_https_proxy),
-      proxy_server_(proxy_server),
-      proxy_delegate_(proxy_delegate),
-      traffic_annotation_(traffic_annotation),
-      net_log_(socket_->NetLog()) {
-  // Synthesize the bits of a request that are actually used.
-  request_.url = GURL("https://" + endpoint.ToString());
-  request_.method = "CONNECT";
-  if (!user_agent.empty())
-    request_.extra_headers.SetHeader(HttpRequestHeaders::kUserAgent,
-                                     user_agent);
-}
-
 HttpProxyClientSocket::~HttpProxyClientSocket() {
   Disconnect();
 }
@@ -437,8 +399,8 @@
   }
 
   parser_buf_ = base::MakeRefCounted<GrowableIOBuffer>();
-  http_stream_parser_.reset(new HttpStreamParser(socket_, is_reused_, &request_,
-                                                 parser_buf_.get(), net_log_));
+  http_stream_parser_.reset(new HttpStreamParser(
+      socket_.get(), is_reused_, &request_, parser_buf_.get(), net_log_));
   return http_stream_parser_->SendRequest(request_line_, request_headers_,
                                           traffic_annotation_, &response_,
                                           io_callback_);
@@ -503,9 +465,7 @@
         return ERR_TUNNEL_CONNECTION_FAILED;
 
       http_stream_parser_.reset();
-      client_socket_handle_.reset();
-      stream_socket_.reset();
-      socket_ = nullptr;
+      socket_.reset();
       is_reused_ = false;
       return ERR_HTTPS_PROXY_TUNNEL_RESPONSE_REDIRECT;
 
diff --git a/net/http/http_proxy_client_socket.h b/net/http/http_proxy_client_socket.h
index 2585e9a0..7509bfd 100644
--- a/net/http/http_proxy_client_socket.h
+++ b/net/http/http_proxy_client_socket.h
@@ -29,7 +29,6 @@
 
 namespace net {
 
-class ClientSocketHandle;
 class GrowableIOBuffer;
 class HttpStreamParser;
 class IOBuffer;
@@ -38,11 +37,10 @@
 
 class NET_EXPORT_PRIVATE HttpProxyClientSocket : public ProxyClientSocket {
  public:
-  // Takes ownership of |stream_socket|, which should already be connected
-  // by the time Connect() is called. |stream_socket_| is assumed to be a freash
-  // socket. If tunnel is true then on Connect() this socket will establish an
-  // Http tunnel.
-  HttpProxyClientSocket(std::unique_ptr<StreamSocket> stream_socket,
+  // Takes ownership of |socket|, which should already be connected by the time
+  // Connect() is called. |socket| is assumed to be a freash socket. If tunnel
+  // is true then on Connect() this socket will establish an Http tunnel.
+  HttpProxyClientSocket(std::unique_ptr<StreamSocket> socket,
                         const std::string& user_agent,
                         const HostPortPair& endpoint,
                         const ProxyServer& proxy_server,
@@ -54,21 +52,6 @@
                         bool is_https_proxy,
                         const NetworkTrafficAnnotationTag& traffic_annotation);
 
-  // Same as above, but takes a ClientSocketHandle instead.
-  // TODO(mmenke): Remove in favor of above constructor.
-  HttpProxyClientSocket(
-      std::unique_ptr<ClientSocketHandle> client_socket_handle,
-      const std::string& user_agent,
-      const HostPortPair& endpoint,
-      const ProxyServer& proxy_server,
-      HttpAuthController* http_auth_controller,
-      bool tunnel,
-      bool using_spdy,
-      NextProto negotiated_protocol,
-      ProxyDelegate* proxy_delegate,
-      bool is_https_proxy,
-      const NetworkTrafficAnnotationTag& traffic_annotation);
-
   // On destruction Disconnect() is called.
   ~HttpProxyClientSocket() override;
 
@@ -163,12 +146,7 @@
   std::unique_ptr<HttpStreamParser> http_stream_parser_;
   scoped_refptr<IOBuffer> drain_buf_;
 
-  // One of these two stores the underlying socket, depending on which
-  // constructor was used.
-  std::unique_ptr<ClientSocketHandle> client_socket_handle_;
-  std::unique_ptr<StreamSocket> stream_socket_;
-  // The underlying socket.
-  StreamSocket* socket_;
+  std::unique_ptr<StreamSocket> socket_;
 
   // Whether or not |socket_| has been previously used. Once auth credentials
   // are sent, set to true.
diff --git a/net/http/http_proxy_client_socket_fuzzer.cc b/net/http/http_proxy_client_socket_fuzzer.cc
index af12e96..cbe9090 100644
--- a/net/http/http_proxy_client_socket_fuzzer.cc
+++ b/net/http/http_proxy_client_socket_fuzzer.cc
@@ -23,7 +23,6 @@
 #include "net/http/http_auth_handler_factory.h"
 #include "net/http/http_auth_scheme.h"
 #include "net/log/test_net_log.h"
-#include "net/socket/client_socket_handle.h"
 #include "net/socket/fuzzed_socket.h"
 #include "net/socket/next_proto.h"
 #include "net/traffic_annotation/network_traffic_annotation_test_helper.h"
@@ -44,10 +43,6 @@
       new net::FuzzedSocket(&data_provider, &test_net_log));
   CHECK_EQ(net::OK, fuzzed_socket->Connect(callback.callback()));
 
-  std::unique_ptr<net::ClientSocketHandle> socket_handle(
-      new net::ClientSocketHandle());
-  socket_handle->SetSocket(std::move(fuzzed_socket));
-
   // Create auth handler supporting basic and digest schemes.  Other schemes can
   // make system calls, which doesn't seem like a great idea.
   net::HttpAuthCache auth_cache;
@@ -60,12 +55,12 @@
   scoped_refptr<net::HttpAuthController> auth_controller(
       new net::HttpAuthController(net::HttpAuth::AUTH_PROXY,
                                   GURL("http://proxy:42/"), &auth_cache,
-                                  &auth_handler_factory));
+                                  &auth_handler_factory, nullptr));
   // Determine if the HttpProxyClientSocket should be told the underlying socket
   // is HTTPS.
   bool is_https_proxy = data_provider.ConsumeBool();
   net::HttpProxyClientSocket socket(
-      std::move(socket_handle), "Bond/007", net::HostPortPair("foo", 80),
+      std::move(fuzzed_socket), "Bond/007", net::HostPortPair("foo", 80),
       net::ProxyServer(net::ProxyServer::SCHEME_HTTP,
                        net::HostPortPair("proxy", 42)),
       auth_controller.get(), true /* tunnel */, false /* using_spdy */,
diff --git a/net/http/http_proxy_client_socket_wrapper.cc b/net/http/http_proxy_client_socket_wrapper.cc
index d950c6a4..2829cb4a 100644
--- a/net/http/http_proxy_client_socket_wrapper.cc
+++ b/net/http/http_proxy_client_socket_wrapper.cc
@@ -80,7 +80,8 @@
                        GURL((ssl_params_.get() ? "https://" : "http://") +
                             GetDestination().ToString()),
                        http_auth_cache,
-                       http_auth_handler_factory)
+                       http_auth_handler_factory,
+                       common_connect_job_params_.host_resolver)
                  : nullptr),
       net_log_(NetLogWithSource::Make(
           net_log.net_log(),
diff --git a/net/http/http_proxy_client_socket_wrapper_unittest.cc b/net/http/http_proxy_client_socket_wrapper_unittest.cc
deleted file mode 100644
index 6173eba..0000000
--- a/net/http/http_proxy_client_socket_wrapper_unittest.cc
+++ /dev/null
@@ -1,404 +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 "net/http/http_proxy_client_socket_wrapper.h"
-
-#include <cstdio>
-#include <memory>
-
-#include "build/build_config.h"
-#include "net/cert/ct_policy_enforcer.h"
-#include "net/cert/do_nothing_ct_verifier.h"
-#include "net/cert/mock_cert_verifier.h"
-#include "net/dns/mock_host_resolver.h"
-#include "net/http/http_auth_cache.h"
-#include "net/http/http_auth_handler_factory.h"
-#include "net/http/http_proxy_connect_job.h"
-#include "net/http/http_server_properties_impl.h"
-#include "net/http/transport_security_state.h"
-#include "net/quic/mock_crypto_client_stream_factory.h"
-#include "net/quic/mock_quic_data.h"
-#include "net/quic/quic_http_utils.h"
-#include "net/quic/quic_test_packet_maker.h"
-#include "net/socket/connect_job.h"
-#include "net/socket/socket_tag.h"
-#include "net/socket/socket_test_util.h"
-#include "net/socket/socks_connect_job.h"
-#include "net/socket/ssl_connect_job.h"
-#include "net/socket/transport_connect_job.h"
-#include "net/ssl/channel_id_service.h"
-#include "net/ssl/default_channel_id_store.h"
-#include "net/test/cert_test_util.h"
-#include "net/test/gtest_util.h"
-#include "net/test/test_data_directory.h"
-#include "net/test/test_with_scoped_task_environment.h"
-#include "net/third_party/quic/core/quic_utils.h"
-#include "net/third_party/quic/core/quic_versions.h"
-#include "net/third_party/quic/test_tools/mock_clock.h"
-#include "net/third_party/quic/test_tools/mock_random.h"
-#include "net/traffic_annotation/network_traffic_annotation_test_helper.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-namespace net {
-
-namespace {
-
-const char kProxyHost[] = "proxy.example.org";
-const int kProxyPort = 6121;
-const char kOriginHost[] = "www.google.org";
-const int kOriginPort = 443;
-const char kUserAgent[] = "Mozilla/1.0";
-
-class MockSSLConfigService : public SSLConfigService {
- public:
-  MockSSLConfigService() = default;
-  ~MockSSLConfigService() override = default;
-
-  void GetSSLConfig(SSLConfig* config) override { *config = config_; }
-
-  bool CanShareConnectionWithClientCerts(
-      const std::string& hostname) const override {
-    return false;
-  }
-
- private:
-  SSLConfig config_;
-};
-
-}  // namespace
-
-namespace test {
-
-class HttpProxyClientSocketWrapperTest
-    : public ::testing::TestWithParam<
-          std::tuple<quic::QuicTransportVersion, bool>>,
-      public WithScopedTaskEnvironment {
- protected:
-  static const bool kFin = true;
-  static const bool kIncludeVersion = true;
-  static const bool kSendFeedback = true;
-
-  HttpProxyClientSocketWrapperTest()
-      : proxy_host_port_(kProxyHost, kProxyPort),
-        endpoint_host_port_(kOriginHost, kOriginPort),
-        ssl_config_service_(new MockSSLConfigService()),
-        cert_verifier_(new MockCertVerifier()),
-        channel_id_service_(
-            new ChannelIDService(new DefaultChannelIDStore(nullptr))),
-        cert_transparency_verifier_(new DoNothingCTVerifier()),
-        random_generator_(0),
-        quic_version_(std::get<0>(GetParam())),
-        client_data_stream_id1_(
-            quic::QuicUtils::GetHeadersStreamId(quic_version_) +
-            quic::QuicUtils::StreamIdDelta(quic_version_)),
-        client_headers_include_h2_stream_dependency_(std::get<1>(GetParam())),
-        client_maker_(
-            quic_version_,
-            quic::QuicUtils::CreateRandomConnectionId(&random_generator_),
-            &clock_,
-            kProxyHost,
-            quic::Perspective::IS_CLIENT,
-            client_headers_include_h2_stream_dependency_),
-        server_maker_(
-            quic_version_,
-            quic::QuicUtils::CreateRandomConnectionId(&random_generator_),
-            &clock_,
-            kProxyHost,
-            quic::Perspective::IS_SERVER,
-            false),
-        header_stream_offset_(0),
-        response_offset_(0),
-        store_server_configs_in_properties_(false),
-        idle_connection_timeout_seconds_(kIdleConnectionTimeoutSeconds),
-        reduced_ping_timeout_seconds_(quic::kPingTimeoutSecs),
-        allow_server_migration_(false),
-        race_cert_verification_(false),
-        estimate_initial_rtt_(false),
-        quic_stream_factory_(nullptr),
-        privacy_mode_(PRIVACY_MODE_DISABLED),
-        http_auth_handler_factory_(
-            HttpAuthHandlerFactory::CreateDefault(&host_resolver_)),
-        client_socket_wrapper_(nullptr) {
-    clock_.AdvanceTime(
-        quic::QuicTime::Delta::FromSeconds(1));  // why is this here???
-  }
-
-  void Initialize() {
-    DCHECK(!quic_stream_factory_);
-    quic_stream_factory_.reset(new QuicStreamFactory(
-        net_log_.net_log(), &host_resolver_, ssl_config_service_.get(),
-        &socket_factory_, &http_server_properties_, cert_verifier_.get(),
-        &ct_policy_enforcer_, &transport_security_state_,
-        cert_transparency_verifier_.get(),
-        /*SocketPerformanceWatcherFactory=*/nullptr,
-        &crypto_client_stream_factory_, &random_generator_, &clock_,
-        quic::kDefaultMaxPacketSize, /*user_agent_id=*/kUserAgent,
-        store_server_configs_in_properties_,
-        /*close_sessions_on_ip_change=*/true,
-        /*goaway_sessions_on_ip_change=*/false,
-        /*mark_quic_broken_when_network_blackholes=*/false,
-        idle_connection_timeout_seconds_, reduced_ping_timeout_seconds_,
-        /*retransmittable_on_wire_timeout_milliseconds=*/
-        kDefaultRetransmittableOnWireTimeoutMillisecs,
-        /*max_time_before_crypto_handshake_seconds=*/
-        quic::kMaxTimeForCryptoHandshakeSecs,
-        /*max_idle_time_before_crypto_handshake_seconds=*/
-        quic::kInitialIdleTimeoutSecs,
-        /*migrate_sessions_on_network_change_v2=*/false,
-        /*migrate_sessions_early_v2=*/false,
-        /*retry_on_alternate_network_before_handshake=*/false,
-        /*migrate_idle_sessions=*/false,
-        base::TimeDelta::FromSeconds(kDefaultIdleSessionMigrationPeriodSeconds),
-        base::TimeDelta::FromSeconds(kMaxTimeOnNonDefaultNetworkSecs),
-        kMaxMigrationsToNonDefaultNetworkOnWriteError,
-        kMaxMigrationsToNonDefaultNetworkOnPathDegrading,
-        allow_server_migration_, /*race_stale_dns_on_connection=*/false,
-        /*go_away_on_path_degrading=*/false, race_cert_verification_,
-        estimate_initial_rtt_, client_headers_include_h2_stream_dependency_,
-        connection_options_, client_connection_options_,
-        /*enable_socket_recv_optimization=*/false));
-  }
-
-  void PopulateConnectRequestIR(spdy::SpdyHeaderBlock* block) {
-    (*block)[":method"] = "CONNECT";
-    (*block)[":authority"] = endpoint_host_port_.ToString();
-    (*block)["user-agent"] = kUserAgent;
-  }
-
-  std::unique_ptr<quic::QuicReceivedPacket> ConstructSettingsPacket(
-      uint64_t packet_number) {
-    return client_maker_.MakeInitialSettingsPacket(packet_number,
-                                                   &header_stream_offset_);
-  }
-
-  std::unique_ptr<quic::QuicReceivedPacket> ConstructConnectRequestPacket(
-      uint64_t packet_number,
-      RequestPriority priority) {
-    spdy::SpdyHeaderBlock block;
-    PopulateConnectRequestIR(&block);
-    return client_maker_.MakeRequestHeadersPacket(
-        packet_number, client_data_stream_id1_, kIncludeVersion, !kFin,
-        ConvertRequestPriorityToQuicPriority(priority), std::move(block), 0,
-        nullptr, &header_stream_offset_);
-  }
-
-  std::unique_ptr<quic::QuicReceivedPacket> ConstructServerConnectReplyPacket(
-      uint64_t packet_number,
-      bool fin) {
-    spdy::SpdyHeaderBlock block;
-    block[":status"] = "200";
-
-    return server_maker_.MakeResponseHeadersPacket(
-        packet_number, client_data_stream_id1_, !kIncludeVersion, fin,
-        std::move(block), nullptr, &response_offset_);
-  }
-
-  std::unique_ptr<quic::QuicReceivedPacket> ConstructAckAndRstPacket(
-      uint64_t packet_number,
-      quic::QuicRstStreamErrorCode error_code,
-      uint64_t largest_received,
-      uint64_t smallest_received,
-      uint64_t least_unacked) {
-    return client_maker_.MakeAckAndRstPacket(
-        packet_number, !kIncludeVersion, client_data_stream_id1_, error_code,
-        largest_received, smallest_received, least_unacked, kSendFeedback);
-  }
-
-  static ProofVerifyDetailsChromium DefaultProofVerifyDetails() {
-    // Load a certificate that is valid for *.example.org
-    scoped_refptr<X509Certificate> test_cert(
-        ImportCertFromFile(GetTestCertsDirectory(), "wildcard.pem"));
-    EXPECT_TRUE(test_cert.get());
-    ProofVerifyDetailsChromium verify_details;
-    verify_details.cert_verify_result.verified_cert = test_cert;
-    verify_details.cert_verify_result.is_issued_by_known_root = true;
-    return verify_details;
-  }
-
-  HostPortPair proxy_host_port_;
-  HostPortPair endpoint_host_port_;
-
-  quic::MockClock clock_;
-  MockQuicData mock_quic_data_;
-
-  // QuicStreamFactory environment
-  NetLogWithSource net_log_;
-  MockHostResolver host_resolver_;
-  std::unique_ptr<SSLConfigService> ssl_config_service_;
-  MockTaggingClientSocketFactory socket_factory_;
-  HttpServerPropertiesImpl http_server_properties_;
-  std::unique_ptr<MockCertVerifier> cert_verifier_;
-  DefaultCTPolicyEnforcer ct_policy_enforcer_;
-  std::unique_ptr<ChannelIDService> channel_id_service_;
-  TransportSecurityState transport_security_state_;
-  std::unique_ptr<DoNothingCTVerifier> cert_transparency_verifier_;
-  MockCryptoClientStreamFactory crypto_client_stream_factory_;
-  quic::test::MockRandom random_generator_;
-
-  const quic::QuicTransportVersion quic_version_;
-  const quic::QuicStreamId client_data_stream_id1_;
-  const bool client_headers_include_h2_stream_dependency_;
-  QuicTestPacketMaker client_maker_;
-  QuicTestPacketMaker server_maker_;
-  quic::QuicStreamOffset header_stream_offset_;
-  quic::QuicStreamOffset response_offset_;
-
-  // Variables to configure QuicStreamFactory.
-  bool store_server_configs_in_properties_;
-  int idle_connection_timeout_seconds_;
-  int reduced_ping_timeout_seconds_;
-  bool allow_server_migration_;
-  bool race_cert_verification_;
-  bool estimate_initial_rtt_;
-  quic::QuicTagVector connection_options_;
-  quic::QuicTagVector client_connection_options_;
-
-  std::unique_ptr<QuicStreamFactory> quic_stream_factory_;
-
-  // HttpProxyClientSocketWrapper environment
-  PrivacyMode privacy_mode_;
-  HttpAuthCache http_auth_cache_;
-  std::unique_ptr<HttpAuthHandlerRegistryFactory> http_auth_handler_factory_;
-
-  std::unique_ptr<HttpProxyClientSocketWrapper> client_socket_wrapper_;
-};
-
-TEST_P(HttpProxyClientSocketWrapperTest, QuicProxy) {
-  Initialize();
-  ProofVerifyDetailsChromium verify_details = DefaultProofVerifyDetails();
-  crypto_client_stream_factory_.AddProofVerifyDetails(&verify_details);
-
-  mock_quic_data_.AddWrite(SYNCHRONOUS, ConstructSettingsPacket(1));
-  mock_quic_data_.AddWrite(SYNCHRONOUS,
-                           ConstructConnectRequestPacket(2, HIGHEST));
-  mock_quic_data_.AddRead(ASYNC, ConstructServerConnectReplyPacket(1, !kFin));
-  mock_quic_data_.AddRead(SYNCHRONOUS, ERR_IO_PENDING);
-  mock_quic_data_.AddWrite(
-      SYNCHRONOUS,
-      ConstructAckAndRstPacket(3, quic::QUIC_STREAM_CANCELLED, 1, 1, 1));
-  mock_quic_data_.AddWrite(
-      SYNCHRONOUS, client_maker_.MakeAckAndConnectionClosePacket(
-                       4, false, quic::QuicTime::Delta::FromMilliseconds(0), 1,
-                       1, 1, quic::QUIC_CONNECTION_CANCELLED, "net error"));
-  mock_quic_data_.AddSocketDataToFactory(&socket_factory_);
-
-  scoped_refptr<TransportSocketParams> transport_params =
-      new TransportSocketParams(proxy_host_port_, false,
-                                OnHostResolutionCallback());
-
-  scoped_refptr<SSLSocketParams> ssl_params =
-      new SSLSocketParams(transport_params, nullptr, nullptr, proxy_host_port_,
-                          SSLConfig(), privacy_mode_);
-  transport_params = nullptr;
-
-  client_socket_wrapper_ = std::make_unique<HttpProxyClientSocketWrapper>(
-      HttpProxyClientSocketWrapper::OnProxyAuthChallengeCallback(),
-      /*request_priority=*/DEFAULT_PRIORITY,
-      /*connect_timeout_duration=*/base::TimeDelta::FromHours(1),
-      /*proxy_negotiation_timeout_duration=*/base::TimeDelta::FromHours(1),
-      CommonConnectJobParams("group_name",
-                             /*socket_tag=*/SocketTag(),
-                             /*client_socket_factory=*/nullptr,
-                             /*host_resolver=*/nullptr,
-                             /*proxy_delegate=*/nullptr,
-                             SSLClientSocketContext(), SSLClientSocketContext(),
-                             /*socket_performance_watcher_factory=*/nullptr,
-                             /*network_quality_estimator=*/nullptr,
-                             net_log_.net_log(),
-                             /*websocket_endpoint_lock_manager=*/nullptr),
-      /*transport_params=*/nullptr, ssl_params, quic_version_, kUserAgent,
-      endpoint_host_port_, &http_auth_cache_, http_auth_handler_factory_.get(),
-      /*spdy_session_pool=*/nullptr, quic_stream_factory_.get(),
-      /*is_trusted_proxy=*/false, /*tunnel=*/true, TRAFFIC_ANNOTATION_FOR_TESTS,
-      net_log_);
-
-  TestCompletionCallback callback;
-  client_socket_wrapper_->Connect(callback.callback());
-  EXPECT_EQ(DEFAULT_PRIORITY, host_resolver_.request_priority(1));
-
-  client_socket_wrapper_->SetPriority(HIGHEST);
-
-  // Expect connect request packet with HIGHEST priority, not DEFAULT_PRIORITY.
-  EXPECT_THAT(callback.WaitForResult(), IsOk());
-
-  client_socket_wrapper_.reset();
-  EXPECT_TRUE(mock_quic_data_.AllReadDataConsumed());
-}
-
-// Test that the SocketTag is appropriately applied to the underlying socket
-// for QUIC proxies.
-#if defined(OS_ANDROID)
-TEST_P(HttpProxyClientSocketWrapperTest, QuicProxySocketTag) {
-  Initialize();
-  ProofVerifyDetailsChromium verify_details = DefaultProofVerifyDetails();
-  crypto_client_stream_factory_.AddProofVerifyDetails(&verify_details);
-
-  mock_quic_data_.AddWrite(SYNCHRONOUS, ConstructSettingsPacket(1));
-  mock_quic_data_.AddWrite(SYNCHRONOUS,
-                           ConstructConnectRequestPacket(2, DEFAULT_PRIORITY));
-  mock_quic_data_.AddRead(ASYNC, ConstructServerConnectReplyPacket(1, !kFin));
-  mock_quic_data_.AddRead(SYNCHRONOUS, ERR_IO_PENDING);
-  mock_quic_data_.AddWrite(
-      SYNCHRONOUS,
-      ConstructAckAndRstPacket(3, quic::QUIC_STREAM_CANCELLED, 1, 1, 1));
-  mock_quic_data_.AddWrite(
-      SYNCHRONOUS, client_maker_.MakeAckAndConnectionClosePacket(
-                       4, false, quic::QuicTime::Delta::FromMilliseconds(0), 1,
-                       1, 1, quic::QUIC_CONNECTION_CANCELLED, "net error"));
-  mock_quic_data_.AddSocketDataToFactory(&socket_factory_);
-
-  scoped_refptr<TransportSocketParams> transport_params =
-      new TransportSocketParams(proxy_host_port_, false,
-                                OnHostResolutionCallback());
-
-  scoped_refptr<SSLSocketParams> ssl_params =
-      new SSLSocketParams(transport_params, nullptr, nullptr, proxy_host_port_,
-                          SSLConfig(), privacy_mode_);
-  transport_params = nullptr;
-  SocketTag tag(getuid(), 0x87654321);
-
-  client_socket_wrapper_ = std::make_unique<HttpProxyClientSocketWrapper>(
-      HttpProxyClientSocketWrapper::OnProxyAuthChallengeCallback(),
-      /*request_priority=*/DEFAULT_PRIORITY,
-      /*connect_timeout_duration=*/base::TimeDelta::FromHours(1),
-      /*proxy_negotiation_timeout_duration=*/base::TimeDelta::FromHours(1),
-      CommonConnectJobParams(
-          /*group_name=*/"group_name",
-          /*socket_tag=*/tag,
-          /*client_socket_factory=*/nullptr,
-          /*host_resolver=*/nullptr,
-          /*proxy_delegate=*/nullptr, SSLClientSocketContext(),
-          SSLClientSocketContext(),
-          /*socket_performance_watcher_factory=*/nullptr,
-          /*network_quality_estimator=*/nullptr, net_log_.net_log(),
-          /*websocket_endpoint_lock_manager=*/nullptr),
-      /*transport_params=*/nullptr, ssl_params, quic_version_, kUserAgent,
-      endpoint_host_port_, &http_auth_cache_, http_auth_handler_factory_.get(),
-      /*spdy_session_pool=*/nullptr, quic_stream_factory_.get(),
-      /*is_trusted_proxy=*/false, /*tunnel=*/true, TRAFFIC_ANNOTATION_FOR_TESTS,
-      net_log_);
-
-  TestCompletionCallback callback;
-  client_socket_wrapper_->Connect(callback.callback());
-
-  EXPECT_THAT(callback.WaitForResult(), IsOk());
-
-  EXPECT_EQ(socket_factory_.GetLastProducedUDPSocket()->tag(), tag);
-  EXPECT_TRUE(socket_factory_.GetLastProducedUDPSocket()
-                  ->tagged_before_data_transferred());
-
-  client_socket_wrapper_.reset();
-  EXPECT_TRUE(mock_quic_data_.AllReadDataConsumed());
-}
-#endif
-
-INSTANTIATE_TEST_SUITE_P(
-    VersionIncludeStreamDependencySequence,
-    HttpProxyClientSocketWrapperTest,
-    ::testing::Combine(
-        ::testing::ValuesIn(quic::AllSupportedTransportVersions()),
-        ::testing::Bool()));
-
-}  // namespace test
-}  // namespace net
diff --git a/net/http/http_stream_factory_unittest.cc b/net/http/http_stream_factory_unittest.cc
index 844b5a3..7161374 100644
--- a/net/http/http_stream_factory_unittest.cc
+++ b/net/http/http_stream_factory_unittest.cc
@@ -498,14 +498,19 @@
     std::unique_ptr<HttpNetworkSession> session(
         SpdySessionDependencies::SpdyCreateSession(&session_deps));
     HttpNetworkSessionPeer peer(session.get());
+    std::unique_ptr<CapturePreconnectsTransportSocketPool>
+        owned_transport_conn_pool =
+            std::make_unique<CapturePreconnectsTransportSocketPool>(
+                session_deps.host_resolver.get(),
+                session_deps.cert_verifier.get(),
+                session_deps.transport_security_state.get(),
+                session_deps.cert_transparency_verifier.get(),
+                session_deps.ct_policy_enforcer.get());
     CapturePreconnectsTransportSocketPool* transport_conn_pool =
-        new CapturePreconnectsTransportSocketPool(
-            session_deps.host_resolver.get(), session_deps.cert_verifier.get(),
-            session_deps.transport_security_state.get(),
-            session_deps.cert_transparency_verifier.get(),
-            session_deps.ct_policy_enforcer.get());
+        owned_transport_conn_pool.get();
     auto mock_pool_manager = std::make_unique<MockClientSocketPoolManager>();
-    mock_pool_manager->SetTransportSocketPool(transport_conn_pool);
+    mock_pool_manager->SetSocketPool(ProxyServer::Direct(),
+                                     std::move(owned_transport_conn_pool));
     peer.SetClientSocketPoolManager(std::move(mock_pool_manager));
     PreconnectHelper(kTests[i], session.get());
     EXPECT_EQ(kTests[i].num_streams, transport_conn_pool->last_num_streams());
@@ -529,8 +534,8 @@
             session_deps.cert_transparency_verifier.get(),
             session_deps.ct_policy_enforcer.get());
     auto mock_pool_manager = std::make_unique<MockClientSocketPoolManager>();
-    mock_pool_manager->SetSocketPoolForHTTPProxy(
-        proxy_server, base::WrapUnique(http_proxy_pool));
+    mock_pool_manager->SetSocketPool(proxy_server,
+                                     base::WrapUnique(http_proxy_pool));
     peer.SetClientSocketPoolManager(std::move(mock_pool_manager));
     PreconnectHelper(kTests[i], session.get());
     EXPECT_EQ(kTests[i].num_streams, http_proxy_pool->last_num_streams());
@@ -555,8 +560,8 @@
             session_deps.cert_transparency_verifier.get(),
             session_deps.ct_policy_enforcer.get());
     auto mock_pool_manager = std::make_unique<MockClientSocketPoolManager>();
-    mock_pool_manager->SetSocketPoolForProxy(
-        proxy_server, base::WrapUnique(socks_proxy_pool));
+    mock_pool_manager->SetSocketPool(proxy_server,
+                                     base::WrapUnique(socks_proxy_pool));
     peer.SetClientSocketPoolManager(std::move(mock_pool_manager));
     PreconnectHelper(kTests[i], session.get());
     EXPECT_EQ(kTests[i].num_streams, socks_proxy_pool->last_num_streams());
@@ -580,14 +585,19 @@
                        SpdySessionKey::IsProxySession::kFalse, SocketTag());
     ignore_result(CreateFakeSpdySession(session->spdy_session_pool(), key));
 
+    std::unique_ptr<CapturePreconnectsTransportSocketPool>
+        owned_transport_conn_pool =
+            std::make_unique<CapturePreconnectsTransportSocketPool>(
+                session_deps.host_resolver.get(),
+                session_deps.cert_verifier.get(),
+                session_deps.transport_security_state.get(),
+                session_deps.cert_transparency_verifier.get(),
+                session_deps.ct_policy_enforcer.get());
     CapturePreconnectsTransportSocketPool* transport_conn_pool =
-        new CapturePreconnectsTransportSocketPool(
-            session_deps.host_resolver.get(), session_deps.cert_verifier.get(),
-            session_deps.transport_security_state.get(),
-            session_deps.cert_transparency_verifier.get(),
-            session_deps.ct_policy_enforcer.get());
+        owned_transport_conn_pool.get();
     auto mock_pool_manager = std::make_unique<MockClientSocketPoolManager>();
-    mock_pool_manager->SetTransportSocketPool(transport_conn_pool);
+    mock_pool_manager->SetSocketPool(ProxyServer::Direct(),
+                                     std::move(owned_transport_conn_pool));
     peer.SetClientSocketPoolManager(std::move(mock_pool_manager));
     PreconnectHelper(kTests[i], session.get());
     // We shouldn't be preconnecting if we have an existing session, which is
@@ -608,14 +618,19 @@
   std::unique_ptr<HttpNetworkSession> session(
       SpdySessionDependencies::SpdyCreateSession(&session_deps));
   HttpNetworkSessionPeer peer(session.get());
+  std::unique_ptr<CapturePreconnectsTransportSocketPool>
+      owned_transport_conn_pool =
+          std::make_unique<CapturePreconnectsTransportSocketPool>(
+              session_deps.host_resolver.get(),
+              session_deps.cert_verifier.get(),
+              session_deps.transport_security_state.get(),
+              session_deps.cert_transparency_verifier.get(),
+              session_deps.ct_policy_enforcer.get());
   CapturePreconnectsTransportSocketPool* transport_conn_pool =
-      new CapturePreconnectsTransportSocketPool(
-          session_deps.host_resolver.get(), session_deps.cert_verifier.get(),
-          session_deps.transport_security_state.get(),
-          session_deps.cert_transparency_verifier.get(),
-          session_deps.ct_policy_enforcer.get());
+      owned_transport_conn_pool.get();
   auto mock_pool_manager = std::make_unique<MockClientSocketPoolManager>();
-  mock_pool_manager->SetTransportSocketPool(transport_conn_pool);
+  mock_pool_manager->SetSocketPool(ProxyServer::Direct(),
+                                   std::move(owned_transport_conn_pool));
   peer.SetClientSocketPoolManager(std::move(mock_pool_manager));
 
   PreconnectHelperForURL(1, GURL("http://www.google.com:7"), session.get());
@@ -1168,8 +1183,8 @@
             session_deps.cert_transparency_verifier.get(),
             session_deps.ct_policy_enforcer.get());
     auto mock_pool_manager = std::make_unique<MockClientSocketPoolManager>();
-    mock_pool_manager->SetSocketPoolForHTTPProxy(
-        proxy_server, base::WrapUnique(http_proxy_pool));
+    mock_pool_manager->SetSocketPool(proxy_server,
+                                     base::WrapUnique(http_proxy_pool));
     peer.SetClientSocketPoolManager(std::move(mock_pool_manager));
     PreconnectHelperForURL(num_streams, url, session.get());
     EXPECT_EQ(num_streams, http_proxy_pool->last_num_streams());
@@ -1223,8 +1238,8 @@
 
         auto mock_pool_manager =
             std::make_unique<MockClientSocketPoolManager>();
-        mock_pool_manager->SetSocketPoolForHTTPProxy(
-            proxy_server, base::WrapUnique(http_proxy_pool));
+        mock_pool_manager->SetSocketPool(proxy_server,
+                                         base::WrapUnique(http_proxy_pool));
         peer.SetClientSocketPoolManager(std::move(mock_pool_manager));
 
         HttpRequestInfo request;
@@ -1304,8 +1319,8 @@
           session_deps.ct_policy_enforcer.get());
 
   auto mock_pool_manager = std::make_unique<MockClientSocketPoolManager>();
-  mock_pool_manager->SetSocketPoolForHTTPProxy(
-      proxy_server, base::WrapUnique(http_proxy_pool));
+  mock_pool_manager->SetSocketPool(proxy_server,
+                                   base::WrapUnique(http_proxy_pool));
   peer.SetClientSocketPoolManager(std::move(mock_pool_manager));
 
   HttpRequestInfo request_privacy_mode_disabled;
@@ -1391,8 +1406,7 @@
 // Return count of distinct groups in given socket pool.
 int GetSocketPoolGroupCount(ClientSocketPool* pool) {
   int count = 0;
-  std::unique_ptr<base::DictionaryValue> dict(
-      pool->GetInfoAsValue("", "", false));
+  std::unique_ptr<base::DictionaryValue> dict(pool->GetInfoAsValue("", ""));
   EXPECT_TRUE(dict != nullptr);
   base::DictionaryValue* groups = nullptr;
   if (dict->GetDictionary("groups", &groups) && (groups != nullptr)) {
@@ -1414,8 +1428,7 @@
 // Return count of sockets handed out by a given socket pool.
 int GetHandedOutSocketCount(ClientSocketPool* pool) {
   int count = 0;
-  std::unique_ptr<base::DictionaryValue> dict(
-      pool->GetInfoAsValue("", "", false));
+  std::unique_ptr<base::DictionaryValue> dict(pool->GetInfoAsValue("", ""));
   EXPECT_TRUE(dict != nullptr);
   if (!dict->GetInteger("handed_out_socket_count", &count))
     return -1;
@@ -1458,8 +1471,8 @@
 
   std::unique_ptr<HttpNetworkSession> session(
       SpdySessionDependencies::SpdyCreateSession(&session_deps));
-  TransportClientSocketPool* ssl_pool =
-      session->GetTransportSocketPool(HttpNetworkSession::NORMAL_SOCKET_POOL);
+  TransportClientSocketPool* ssl_pool = session->GetSocketPool(
+      HttpNetworkSession::NORMAL_SOCKET_POOL, ProxyServer::Direct());
 
   EXPECT_EQ(GetSocketPoolGroupCount(ssl_pool), 0);
 
@@ -1564,8 +1577,9 @@
   EXPECT_TRUE(nullptr == waiter.websocket_stream());
 
   EXPECT_EQ(0, GetSpdySessionCount(session.get()));
-  EXPECT_EQ(1, GetSocketPoolGroupCount(session->GetTransportSocketPool(
-                   HttpNetworkSession::NORMAL_SOCKET_POOL)));
+  EXPECT_EQ(
+      1, GetSocketPoolGroupCount(session->GetSocketPool(
+             HttpNetworkSession::NORMAL_SOCKET_POOL, ProxyServer::Direct())));
   EXPECT_TRUE(waiter.used_proxy_info().is_direct());
 }
 
@@ -1658,8 +1672,9 @@
   EXPECT_TRUE(nullptr == waiter.websocket_stream());
 
   EXPECT_EQ(0, GetSpdySessionCount(session.get()));
-  EXPECT_EQ(1, GetSocketPoolGroupCount(session->GetTransportSocketPool(
-                   HttpNetworkSession::NORMAL_SOCKET_POOL)));
+  EXPECT_EQ(
+      1, GetSocketPoolGroupCount(session->GetSocketPool(
+             HttpNetworkSession::NORMAL_SOCKET_POOL, ProxyServer::Direct())));
   EXPECT_TRUE(waiter.used_proxy_info().is_direct());
 }
 
@@ -1696,17 +1711,18 @@
   EXPECT_TRUE(nullptr == waiter.websocket_stream());
 
   EXPECT_EQ(0, GetSpdySessionCount(session.get()));
-  EXPECT_EQ(0, GetSocketPoolGroupCount(session->GetTransportSocketPool(
-                   HttpNetworkSession::NORMAL_SOCKET_POOL)));
-  EXPECT_EQ(1, GetSocketPoolGroupCount(session->GetSocketPoolForHTTPLikeProxy(
+  EXPECT_EQ(
+      0, GetSocketPoolGroupCount(session->GetSocketPool(
+             HttpNetworkSession::NORMAL_SOCKET_POOL, ProxyServer::Direct())));
+  EXPECT_EQ(1, GetSocketPoolGroupCount(session->GetSocketPool(
                    HttpNetworkSession::NORMAL_SOCKET_POOL,
                    ProxyServer(ProxyServer::SCHEME_HTTP,
                                HostPortPair("myproxy", 8888)))));
-  EXPECT_EQ(0, GetSocketPoolGroupCount(session->GetSocketPoolForHTTPLikeProxy(
+  EXPECT_EQ(0, GetSocketPoolGroupCount(session->GetSocketPool(
                    HttpNetworkSession::NORMAL_SOCKET_POOL,
                    ProxyServer(ProxyServer::SCHEME_HTTPS,
                                HostPortPair("myproxy", 8888)))));
-  EXPECT_EQ(0, GetSocketPoolGroupCount(session->GetSocketPoolForHTTPLikeProxy(
+  EXPECT_EQ(0, GetSocketPoolGroupCount(session->GetSocketPool(
                    HttpNetworkSession::WEBSOCKET_SOCKET_POOL,
                    ProxyServer(ProxyServer::SCHEME_HTTP,
                                HostPortPair("myproxy", 8888)))));
@@ -1752,7 +1768,7 @@
        ++preconnect_request) {
     session->http_stream_factory()->PreconnectStreams(1, request_info);
     base::RunLoop().RunUntilIdle();
-    while (GetSocketPoolGroupCount(session->GetSocketPoolForHTTPLikeProxy(
+    while (GetSocketPoolGroupCount(session->GetSocketPool(
                HttpNetworkSession::NORMAL_SOCKET_POOL,
                ProxyServer(ProxyServer::SCHEME_HTTPS,
                            HostPortPair("myproxy.org", 443)))) == 0) {
@@ -1777,7 +1793,7 @@
   ASSERT_TRUE(nullptr != waiter.stream());
   EXPECT_TRUE(nullptr == waiter.websocket_stream());
 
-  EXPECT_EQ(1, GetSocketPoolGroupCount(session->GetSocketPoolForHTTPLikeProxy(
+  EXPECT_EQ(1, GetSocketPoolGroupCount(session->GetSocketPool(
                    HttpNetworkSession::NORMAL_SOCKET_POOL,
                    ProxyServer(ProxyServer::SCHEME_HTTPS,
                                HostPortPair("myproxy.org", 443)))));
@@ -1787,7 +1803,7 @@
        ++preconnect_request) {
     session->http_stream_factory()->PreconnectStreams(1, request_info);
     base::RunLoop().RunUntilIdle();
-    EXPECT_EQ(1, GetSocketPoolGroupCount(session->GetSocketPoolForHTTPLikeProxy(
+    EXPECT_EQ(1, GetSocketPoolGroupCount(session->GetSocketPool(
                      HttpNetworkSession::NORMAL_SOCKET_POOL,
                      ProxyServer(ProxyServer::SCHEME_HTTPS,
                                  HostPortPair("myproxy.org", 443)))));
@@ -1832,8 +1848,9 @@
   ASSERT_TRUE(nullptr != waiter.websocket_stream());
   EXPECT_EQ(MockWebSocketHandshakeStream::kStreamTypeBasic,
             waiter.websocket_stream()->type());
-  EXPECT_EQ(0, GetSocketPoolGroupCount(session->GetTransportSocketPool(
-                   HttpNetworkSession::NORMAL_SOCKET_POOL)));
+  EXPECT_EQ(
+      0, GetSocketPoolGroupCount(session->GetSocketPool(
+             HttpNetworkSession::NORMAL_SOCKET_POOL, ProxyServer::Direct())));
   EXPECT_TRUE(waiter.used_proxy_info().is_direct());
 }
 
@@ -1875,8 +1892,9 @@
   ASSERT_TRUE(nullptr != waiter.websocket_stream());
   EXPECT_EQ(MockWebSocketHandshakeStream::kStreamTypeBasic,
             waiter.websocket_stream()->type());
-  EXPECT_EQ(0, GetSocketPoolGroupCount(session->GetTransportSocketPool(
-                   HttpNetworkSession::NORMAL_SOCKET_POOL)));
+  EXPECT_EQ(
+      0, GetSocketPoolGroupCount(session->GetSocketPool(
+             HttpNetworkSession::NORMAL_SOCKET_POOL, ProxyServer::Direct())));
   EXPECT_TRUE(waiter.used_proxy_info().is_direct());
 }
 
@@ -1916,13 +1934,14 @@
   ASSERT_TRUE(nullptr != waiter.websocket_stream());
   EXPECT_EQ(MockWebSocketHandshakeStream::kStreamTypeBasic,
             waiter.websocket_stream()->type());
-  EXPECT_EQ(0, GetSocketPoolGroupCount(session->GetTransportSocketPool(
-                   HttpNetworkSession::WEBSOCKET_SOCKET_POOL)));
-  EXPECT_EQ(0, GetSocketPoolGroupCount(session->GetSocketPoolForHTTPLikeProxy(
+  EXPECT_EQ(0, GetSocketPoolGroupCount(session->GetSocketPool(
+                   HttpNetworkSession::WEBSOCKET_SOCKET_POOL,
+                   ProxyServer::Direct())));
+  EXPECT_EQ(0, GetSocketPoolGroupCount(session->GetSocketPool(
                    HttpNetworkSession::NORMAL_SOCKET_POOL,
                    ProxyServer(ProxyServer::SCHEME_HTTP,
                                HostPortPair("myproxy", 8888)))));
-  EXPECT_EQ(1, GetSocketPoolGroupCount(session->GetSocketPoolForHTTPLikeProxy(
+  EXPECT_EQ(1, GetSocketPoolGroupCount(session->GetSocketPool(
                    HttpNetworkSession::WEBSOCKET_SOCKET_POOL,
                    ProxyServer(ProxyServer::SCHEME_HTTP,
                                HostPortPair("myproxy", 8888)))));
@@ -1967,8 +1986,9 @@
   ASSERT_TRUE(nullptr != waiter.stream());
 
   EXPECT_EQ(1, GetSpdySessionCount(session.get()));
-  EXPECT_EQ(1, GetSocketPoolGroupCount(session->GetTransportSocketPool(
-                   HttpNetworkSession::NORMAL_SOCKET_POOL)));
+  EXPECT_EQ(
+      1, GetSocketPoolGroupCount(session->GetSocketPool(
+             HttpNetworkSession::NORMAL_SOCKET_POOL, ProxyServer::Direct())));
   EXPECT_TRUE(waiter.used_proxy_info().is_direct());
 }
 
@@ -2020,8 +2040,9 @@
   ASSERT_TRUE(nullptr != waiter.stream());
 
   EXPECT_EQ(1, GetSpdySessionCount(session.get()));
-  EXPECT_EQ(0, GetSocketPoolGroupCount(session->GetTransportSocketPool(
-                   HttpNetworkSession::NORMAL_SOCKET_POOL)));
+  EXPECT_EQ(
+      0, GetSocketPoolGroupCount(session->GetSocketPool(
+             HttpNetworkSession::NORMAL_SOCKET_POOL, ProxyServer::Direct())));
   EXPECT_FALSE(waiter.used_proxy_info().is_direct());
   EXPECT_TRUE(http_server_properties->GetSupportsSpdy(scheme_host_port));
 }
@@ -2071,7 +2092,8 @@
             ssl_params),
         MEDIUM, SocketTag(), ClientSocketPool::RespectLimits::ENABLED,
         callback.callback(), ClientSocketPool::ProxyAuthCallback(),
-        session->GetTransportSocketPool(HttpNetworkSession::NORMAL_SOCKET_POOL),
+        session->GetSocketPool(HttpNetworkSession::NORMAL_SOCKET_POOL,
+                               ProxyServer::Direct()),
         NetLogWithSource());
     rv = callback.GetResult(rv);
     handles.push_back(std::move(connection));
@@ -2079,10 +2101,11 @@
 
   // Releases handles now, and these sockets should go into the socket pool.
   handles.clear();
-  EXPECT_EQ(
-      kNumIdleSockets,
-      session->GetTransportSocketPool(HttpNetworkSession::NORMAL_SOCKET_POOL)
-          ->IdleSocketCount());
+  EXPECT_EQ(kNumIdleSockets,
+            session
+                ->GetSocketPool(HttpNetworkSession::NORMAL_SOCKET_POOL,
+                                ProxyServer::Direct())
+                ->IdleSocketCount());
 
   // Request two streams at once and make sure they use the same connection.
   HttpRequestInfo request_info;
@@ -2114,9 +2137,10 @@
   ASSERT_NE(waiter1.stream(), waiter2.stream());
 
   // Establishing the SpdySession will close idle H2 sockets.
-  EXPECT_EQ(
-      0, session->GetTransportSocketPool(HttpNetworkSession::NORMAL_SOCKET_POOL)
-             ->IdleSocketCount());
+  EXPECT_EQ(0, session
+                   ->GetSocketPool(HttpNetworkSession::NORMAL_SOCKET_POOL,
+                                   ProxyServer::Direct())
+                   ->IdleSocketCount());
   EXPECT_EQ(1, GetSpdySessionCount(session.get()));
 }
 
@@ -2176,9 +2200,10 @@
   ASSERT_NE(waiter1.stream(), waiter2.stream());
 
   // Establishing the SpdySession will close the extra H2 socket.
-  EXPECT_EQ(
-      0, session->GetTransportSocketPool(HttpNetworkSession::NORMAL_SOCKET_POOL)
-             ->IdleSocketCount());
+  EXPECT_EQ(0, session
+                   ->GetSocketPool(HttpNetworkSession::NORMAL_SOCKET_POOL,
+                                   ProxyServer::Direct())
+                   ->IdleSocketCount());
   EXPECT_EQ(1, GetSpdySessionCount(session.get()));
   EXPECT_TRUE(data0.AllReadDataConsumed());
   EXPECT_TRUE(data1.AllReadDataConsumed());
@@ -2220,8 +2245,9 @@
   EXPECT_FALSE(waiter.websocket_stream());
   ASSERT_FALSE(waiter.stream());
   ASSERT_TRUE(waiter.bidirectional_stream_impl());
-  EXPECT_EQ(1, GetSocketPoolGroupCount(session->GetTransportSocketPool(
-                   HttpNetworkSession::NORMAL_SOCKET_POOL)));
+  EXPECT_EQ(
+      1, GetSocketPoolGroupCount(session->GetSocketPool(
+             HttpNetworkSession::NORMAL_SOCKET_POOL, ProxyServer::Direct())));
   EXPECT_TRUE(waiter.used_proxy_info().is_direct());
 }
 
@@ -2428,8 +2454,9 @@
   EXPECT_THAT(stream_impl->ReadData(buffer.get(), 1), IsOk());
   EXPECT_EQ(kProtoQUIC, stream_impl->GetProtocol());
   EXPECT_EQ("200", delegate.response_headers().find(":status")->second);
-  EXPECT_EQ(0, GetSocketPoolGroupCount(session()->GetTransportSocketPool(
-                   HttpNetworkSession::NORMAL_SOCKET_POOL)));
+  EXPECT_EQ(
+      0, GetSocketPoolGroupCount(session()->GetSocketPool(
+             HttpNetworkSession::NORMAL_SOCKET_POOL, ProxyServer::Direct())));
   EXPECT_TRUE(waiter.used_proxy_info().is_direct());
 }
 
@@ -2557,8 +2584,9 @@
   EXPECT_EQ(kProtoQUIC, stream_impl->GetProtocol());
   EXPECT_EQ("200", delegate.response_headers().find(":status")->second);
   // There is no Http2 socket pool.
-  EXPECT_EQ(0, GetSocketPoolGroupCount(session()->GetTransportSocketPool(
-                   HttpNetworkSession::NORMAL_SOCKET_POOL)));
+  EXPECT_EQ(
+      0, GetSocketPoolGroupCount(session()->GetSocketPool(
+             HttpNetworkSession::NORMAL_SOCKET_POOL, ProxyServer::Direct())));
   EXPECT_TRUE(waiter.used_proxy_info().is_direct());
 }
 
@@ -2601,8 +2629,9 @@
   EXPECT_FALSE(waiter.websocket_stream());
   ASSERT_FALSE(waiter.stream());
   ASSERT_FALSE(waiter.bidirectional_stream_impl());
-  EXPECT_EQ(1, GetSocketPoolGroupCount(session->GetTransportSocketPool(
-                   HttpNetworkSession::NORMAL_SOCKET_POOL)));
+  EXPECT_EQ(
+      1, GetSocketPoolGroupCount(session->GetSocketPool(
+             HttpNetworkSession::NORMAL_SOCKET_POOL, ProxyServer::Direct())));
 }
 
 #if defined(OS_ANDROID)
@@ -2670,10 +2699,12 @@
   ASSERT_TRUE(nullptr != waiter1.stream());
 
   EXPECT_EQ(1, GetSpdySessionCount(session.get()));
-  EXPECT_EQ(1, GetSocketPoolGroupCount(session->GetTransportSocketPool(
-                   HttpNetworkSession::NORMAL_SOCKET_POOL)));
-  EXPECT_EQ(1, GetHandedOutSocketCount(session->GetTransportSocketPool(
-                   HttpNetworkSession::NORMAL_SOCKET_POOL)));
+  EXPECT_EQ(
+      1, GetSocketPoolGroupCount(session->GetSocketPool(
+             HttpNetworkSession::NORMAL_SOCKET_POOL, ProxyServer::Direct())));
+  EXPECT_EQ(
+      1, GetHandedOutSocketCount(session->GetSocketPool(
+             HttpNetworkSession::NORMAL_SOCKET_POOL, ProxyServer::Direct())));
   // Verify socket tagged appropriately.
   EXPECT_TRUE(tag1 == socket_factory->GetLastProducedTCPSocket()->tag());
   EXPECT_TRUE(
@@ -2693,10 +2724,12 @@
   ASSERT_TRUE(nullptr != waiter2.stream());
 
   EXPECT_EQ(2, GetSpdySessionCount(session.get()));
-  EXPECT_EQ(1, GetSocketPoolGroupCount(session->GetTransportSocketPool(
-                   HttpNetworkSession::NORMAL_SOCKET_POOL)));
-  EXPECT_EQ(2, GetHandedOutSocketCount(session->GetTransportSocketPool(
-                   HttpNetworkSession::NORMAL_SOCKET_POOL)));
+  EXPECT_EQ(
+      1, GetSocketPoolGroupCount(session->GetSocketPool(
+             HttpNetworkSession::NORMAL_SOCKET_POOL, ProxyServer::Direct())));
+  EXPECT_EQ(
+      2, GetHandedOutSocketCount(session->GetSocketPool(
+             HttpNetworkSession::NORMAL_SOCKET_POOL, ProxyServer::Direct())));
   // Verify socket tagged appropriately.
   EXPECT_TRUE(tag2 == socket_factory->GetLastProducedTCPSocket()->tag());
   EXPECT_TRUE(
@@ -2716,10 +2749,12 @@
   ASSERT_TRUE(nullptr != waiter3.stream());
 
   EXPECT_EQ(2, GetSpdySessionCount(session.get()));
-  EXPECT_EQ(1, GetSocketPoolGroupCount(session->GetTransportSocketPool(
-                   HttpNetworkSession::NORMAL_SOCKET_POOL)));
-  EXPECT_EQ(2, GetHandedOutSocketCount(session->GetTransportSocketPool(
-                   HttpNetworkSession::NORMAL_SOCKET_POOL)));
+  EXPECT_EQ(
+      1, GetSocketPoolGroupCount(session->GetSocketPool(
+             HttpNetworkSession::NORMAL_SOCKET_POOL, ProxyServer::Direct())));
+  EXPECT_EQ(
+      2, GetHandedOutSocketCount(session->GetSocketPool(
+             HttpNetworkSession::NORMAL_SOCKET_POOL, ProxyServer::Direct())));
 }
 
 // Verify HttpStreamFactory::Job passes socket tag along properly to QUIC
@@ -2924,10 +2959,12 @@
   ASSERT_TRUE(waiter1.stream());
 
   EXPECT_EQ(1, GetSpdySessionCount(session.get()));
-  EXPECT_EQ(1, GetSocketPoolGroupCount(session->GetTransportSocketPool(
-                   HttpNetworkSession::NORMAL_SOCKET_POOL)));
-  EXPECT_EQ(1, GetHandedOutSocketCount(session->GetTransportSocketPool(
-                   HttpNetworkSession::NORMAL_SOCKET_POOL)));
+  EXPECT_EQ(
+      1, GetSocketPoolGroupCount(session->GetSocketPool(
+             HttpNetworkSession::NORMAL_SOCKET_POOL, ProxyServer::Direct())));
+  EXPECT_EQ(
+      1, GetHandedOutSocketCount(session->GetSocketPool(
+             HttpNetworkSession::NORMAL_SOCKET_POOL, ProxyServer::Direct())));
   // Verify socket tagged appropriately.
   MockTaggingStreamSocket* socket = socket_factory->GetLastProducedTCPSocket();
   EXPECT_TRUE(tag1 == socket->tag());
@@ -2946,10 +2983,12 @@
   ASSERT_TRUE(waiter2.stream());
   // Verify still have just one session.
   EXPECT_EQ(1, GetSpdySessionCount(session.get()));
-  EXPECT_EQ(1, GetSocketPoolGroupCount(session->GetTransportSocketPool(
-                   HttpNetworkSession::NORMAL_SOCKET_POOL)));
-  EXPECT_EQ(1, GetHandedOutSocketCount(session->GetTransportSocketPool(
-                   HttpNetworkSession::NORMAL_SOCKET_POOL)));
+  EXPECT_EQ(
+      1, GetSocketPoolGroupCount(session->GetSocketPool(
+             HttpNetworkSession::NORMAL_SOCKET_POOL, ProxyServer::Direct())));
+  EXPECT_EQ(
+      1, GetHandedOutSocketCount(session->GetSocketPool(
+             HttpNetworkSession::NORMAL_SOCKET_POOL, ProxyServer::Direct())));
   // Verify no new sockets created.
   EXPECT_EQ(socket, socket_factory->GetLastProducedTCPSocket());
   // Verify socket tag changed.
@@ -2978,10 +3017,12 @@
   ASSERT_TRUE(waiter3.stream());
   // Verify still have just one session.
   EXPECT_EQ(1, GetSpdySessionCount(session.get()));
-  EXPECT_EQ(1, GetSocketPoolGroupCount(session->GetTransportSocketPool(
-                   HttpNetworkSession::NORMAL_SOCKET_POOL)));
-  EXPECT_EQ(1, GetHandedOutSocketCount(session->GetTransportSocketPool(
-                   HttpNetworkSession::NORMAL_SOCKET_POOL)));
+  EXPECT_EQ(
+      1, GetSocketPoolGroupCount(session->GetSocketPool(
+             HttpNetworkSession::NORMAL_SOCKET_POOL, ProxyServer::Direct())));
+  EXPECT_EQ(
+      1, GetHandedOutSocketCount(session->GetSocketPool(
+             HttpNetworkSession::NORMAL_SOCKET_POOL, ProxyServer::Direct())));
   // Verify no new sockets created.
   EXPECT_EQ(socket, socket_factory->GetLastProducedTCPSocket());
   // Verify socket tag changed.
@@ -3010,10 +3051,12 @@
   ASSERT_TRUE(waiter4.stream());
   // Verify we now have two sessions.
   EXPECT_EQ(2, GetSpdySessionCount(session.get()));
-  EXPECT_EQ(1, GetSocketPoolGroupCount(session->GetTransportSocketPool(
-                   HttpNetworkSession::NORMAL_SOCKET_POOL)));
-  EXPECT_EQ(2, GetHandedOutSocketCount(session->GetTransportSocketPool(
-                   HttpNetworkSession::NORMAL_SOCKET_POOL)));
+  EXPECT_EQ(
+      1, GetSocketPoolGroupCount(session->GetSocketPool(
+             HttpNetworkSession::NORMAL_SOCKET_POOL, ProxyServer::Direct())));
+  EXPECT_EQ(
+      2, GetHandedOutSocketCount(session->GetSocketPool(
+             HttpNetworkSession::NORMAL_SOCKET_POOL, ProxyServer::Direct())));
   // Verify a new socket was created.
   MockTaggingStreamSocket* socket2 = socket_factory->GetLastProducedTCPSocket();
   EXPECT_NE(socket, socket2);
@@ -3096,10 +3139,12 @@
 
   // Verify just one session created.
   EXPECT_EQ(1, GetSpdySessionCount(session.get()));
-  EXPECT_EQ(1, GetSocketPoolGroupCount(session->GetTransportSocketPool(
-                   HttpNetworkSession::NORMAL_SOCKET_POOL)));
-  EXPECT_EQ(1, GetHandedOutSocketCount(session->GetTransportSocketPool(
-                   HttpNetworkSession::NORMAL_SOCKET_POOL)));
+  EXPECT_EQ(
+      1, GetSocketPoolGroupCount(session->GetSocketPool(
+             HttpNetworkSession::NORMAL_SOCKET_POOL, ProxyServer::Direct())));
+  EXPECT_EQ(
+      1, GetHandedOutSocketCount(session->GetSocketPool(
+             HttpNetworkSession::NORMAL_SOCKET_POOL, ProxyServer::Direct())));
 
   // Open another session to same IP but with different privacy mode.
   StreamRequestWaiter waiter2;
@@ -3115,10 +3160,12 @@
 
   // Verify two sessions are now open.
   EXPECT_EQ(2, GetSpdySessionCount(session.get()));
-  EXPECT_EQ(2, GetSocketPoolGroupCount(session->GetTransportSocketPool(
-                   HttpNetworkSession::NORMAL_SOCKET_POOL)));
-  EXPECT_EQ(2, GetHandedOutSocketCount(session->GetTransportSocketPool(
-                   HttpNetworkSession::NORMAL_SOCKET_POOL)));
+  EXPECT_EQ(
+      2, GetSocketPoolGroupCount(session->GetSocketPool(
+             HttpNetworkSession::NORMAL_SOCKET_POOL, ProxyServer::Direct())));
+  EXPECT_EQ(
+      2, GetHandedOutSocketCount(session->GetSocketPool(
+             HttpNetworkSession::NORMAL_SOCKET_POOL, ProxyServer::Direct())));
 
   // Open a third session that IP aliases first session.
   StreamRequestWaiter waiter3;
@@ -3137,10 +3184,12 @@
   // created.  This will fail unless the session pool supports multiple
   // sessions aliasing a single IP.
   EXPECT_EQ(2, GetSpdySessionCount(session.get()));
-  EXPECT_EQ(2, GetSocketPoolGroupCount(session->GetTransportSocketPool(
-                   HttpNetworkSession::NORMAL_SOCKET_POOL)));
-  EXPECT_EQ(2, GetHandedOutSocketCount(session->GetTransportSocketPool(
-                   HttpNetworkSession::NORMAL_SOCKET_POOL)));
+  EXPECT_EQ(
+      2, GetSocketPoolGroupCount(session->GetSocketPool(
+             HttpNetworkSession::NORMAL_SOCKET_POOL, ProxyServer::Direct())));
+  EXPECT_EQ(
+      2, GetHandedOutSocketCount(session->GetSocketPool(
+             HttpNetworkSession::NORMAL_SOCKET_POOL, ProxyServer::Direct())));
 
   // Open a fourth session that IP aliases the second session.
   StreamRequestWaiter waiter4;
@@ -3158,10 +3207,12 @@
   // Verify the session pool reused the second session.  This will fail unless
   // the session pool supports multiple sessions aliasing a single IP.
   EXPECT_EQ(2, GetSpdySessionCount(session.get()));
-  EXPECT_EQ(2, GetSocketPoolGroupCount(session->GetTransportSocketPool(
-                   HttpNetworkSession::NORMAL_SOCKET_POOL)));
-  EXPECT_EQ(2, GetHandedOutSocketCount(session->GetTransportSocketPool(
-                   HttpNetworkSession::NORMAL_SOCKET_POOL)));
+  EXPECT_EQ(
+      2, GetSocketPoolGroupCount(session->GetSocketPool(
+             HttpNetworkSession::NORMAL_SOCKET_POOL, ProxyServer::Direct())));
+  EXPECT_EQ(
+      2, GetHandedOutSocketCount(session->GetSocketPool(
+             HttpNetworkSession::NORMAL_SOCKET_POOL, ProxyServer::Direct())));
 }
 
 }  // namespace
diff --git a/net/network_error_logging/network_error_logging_service.cc b/net/network_error_logging/network_error_logging_service.cc
index 7c0db74..78bc90a 100644
--- a/net/network_error_logging/network_error_logging_service.cc
+++ b/net/network_error_logging/network_error_logging_service.cc
@@ -169,8 +169,14 @@
 
 void RecordRequestOutcome(NetworkErrorLoggingService::RequestOutcome outcome) {
   UMA_HISTOGRAM_ENUMERATION(
-      NetworkErrorLoggingService::kRequestOutcomeHistogram, outcome,
-      NetworkErrorLoggingService::RequestOutcome::MAX);
+      NetworkErrorLoggingService::kRequestOutcomeHistogram, outcome);
+}
+
+void RecordSignedExchangeRequestOutcome(
+    NetworkErrorLoggingService::RequestOutcome outcome) {
+  UMA_HISTOGRAM_ENUMERATION(
+      NetworkErrorLoggingService::kSignedExchangeRequestOutcomeHistogram,
+      outcome);
 }
 
 class NetworkErrorLoggingServiceImpl : public NetworkErrorLoggingService {
@@ -226,7 +232,7 @@
       return;
 
     if (!reporting_service_) {
-      RecordRequestOutcome(RequestOutcome::DISCARDED_NO_REPORTING_SERVICE);
+      RecordRequestOutcome(RequestOutcome::kDiscardedNoReportingService);
       return;
     }
 
@@ -236,7 +242,7 @@
     auto report_origin = url::Origin::Create(details.uri);
     const OriginPolicy* policy = FindPolicyForOrigin(report_origin);
     if (!policy) {
-      RecordRequestOutcome(RequestOutcome::DISCARDED_NO_ORIGIN_POLICY);
+      RecordRequestOutcome(RequestOutcome::kDiscardedNoOriginPolicy);
       return;
     }
 
@@ -264,7 +270,7 @@
     // meaningful if it only includes reports that otherwise could have been
     // uploaded.
     if (details.reporting_upload_depth > kMaxNestedReportDepth) {
-      RecordRequestOutcome(RequestOutcome::DISCARDED_REPORTING_UPLOAD);
+      RecordRequestOutcome(RequestOutcome::kDiscardedReportingUpload);
       return;
     }
 
@@ -284,7 +290,7 @@
     // errors.
     if (phase_string != kDnsPhase &&
         IsMismatchingSubdomainReport(*policy, report_origin)) {
-      RecordRequestOutcome(RequestOutcome::DISCARDED_NON_DNS_SUBDOMAIN_REPORT);
+      RecordRequestOutcome(RequestOutcome::kDiscardedNonDNSSubdomainReport);
       return;
     }
 
@@ -293,8 +299,8 @@
         SampleAndReturnFraction(*policy, success);
     if (!sampling_fraction.has_value()) {
       RecordRequestOutcome(success
-                               ? RequestOutcome::DISCARDED_UNSAMPLED_SUCCESS
-                               : RequestOutcome::DISCARDED_UNSAMPLED_FAILURE);
+                               ? RequestOutcome::kDiscardedUnsampledSuccess
+                               : RequestOutcome::kDiscardedUnsampledFailure);
       return;
     }
 
@@ -307,7 +313,7 @@
         CreateReportBody(phase_string, type_string, sampling_fraction.value(),
                          details),
         details.reporting_upload_depth);
-    RecordRequestOutcome(RequestOutcome::QUEUED);
+    RecordRequestOutcome(RequestOutcome::kQueued);
   }
 
   void QueueSignedExchangeReport(
@@ -316,41 +322,51 @@
       return;
 
     if (!reporting_service_) {
-      RecordRequestOutcome(RequestOutcome::DISCARDED_NO_REPORTING_SERVICE);
+      RecordSignedExchangeRequestOutcome(
+          RequestOutcome::kDiscardedNoReportingService);
       return;
     }
     if (!details.outer_url.SchemeIsCryptographic()) {
-      RecordHeaderOutcome(HeaderOutcome::DISCARDED_INSECURE_ORIGIN);
+      RecordSignedExchangeRequestOutcome(
+          RequestOutcome::kDiscardedInsecureOrigin);
       return;
     }
     const auto report_origin = url::Origin::Create(details.outer_url);
     const OriginPolicy* policy = FindPolicyForOrigin(report_origin);
     if (!policy) {
-      RecordRequestOutcome(RequestOutcome::DISCARDED_NO_ORIGIN_POLICY);
+      RecordSignedExchangeRequestOutcome(
+          RequestOutcome::kDiscardedNoOriginPolicy);
       return;
     }
     if (IsMismatchingSubdomainReport(*policy, report_origin)) {
-      RecordRequestOutcome(RequestOutcome::DISCARDED_NON_DNS_SUBDOMAIN_REPORT);
+      RecordSignedExchangeRequestOutcome(
+          RequestOutcome::kDiscardedNonDNSSubdomainReport);
       return;
     }
     // Don't send the report when the IP addresses of the server and the policy
     // don’t match. This case is coverd by OnRequest() while processing the HTTP
     // response.
-    if (details.server_ip_address != policy->received_ip_address)
+    // This happens if the server has set the NEL policy previously, but doesn't
+    // set the NEL policy for the signed exchange response, and the IP address
+    // has changed due to DNS round robin.
+    if (details.server_ip_address != policy->received_ip_address) {
+      RecordSignedExchangeRequestOutcome(
+          RequestOutcome::kDiscardedIPAddressMismatch);
       return;
+    }
     const base::Optional<double> sampling_fraction =
         SampleAndReturnFraction(*policy, details.success);
     if (!sampling_fraction.has_value()) {
-      RecordRequestOutcome(details.success
-                               ? RequestOutcome::DISCARDED_UNSAMPLED_SUCCESS
-                               : RequestOutcome::DISCARDED_UNSAMPLED_FAILURE);
+      RecordSignedExchangeRequestOutcome(
+          details.success ? RequestOutcome::kDiscardedUnsampledSuccess
+                          : RequestOutcome::kDiscardedUnsampledFailure);
       return;
     }
     reporting_service_->QueueReport(
         details.outer_url, details.user_agent, policy->report_to, kReportType,
         CreateSignedExchangeReportBody(details, sampling_fraction.value()),
         0 /* depth */);
-    RecordRequestOutcome(RequestOutcome::QUEUED);
+    RecordSignedExchangeRequestOutcome(RequestOutcome::kQueued);
   }
 
   void RemoveBrowsingData(const base::RepeatingCallback<bool(const GURL&)>&
@@ -670,6 +686,10 @@
 const char NetworkErrorLoggingService::kRequestOutcomeHistogram[] =
     "Net.NetworkErrorLogging.RequestOutcome";
 
+const char
+    NetworkErrorLoggingService::kSignedExchangeRequestOutcomeHistogram[] =
+        "Net.NetworkErrorLogging.SignedExchangeRequestOutcome";
+
 // Allow NEL reports on regular requests, plus NEL reports on Reporting uploads
 // containing only regular requests, but do not allow NEL reports on Reporting
 // uploads containing Reporting uploads.
@@ -721,13 +741,12 @@
 // static
 void NetworkErrorLoggingService::
     RecordRequestDiscardedForNoNetworkErrorLoggingService() {
-  RecordRequestOutcome(
-      RequestOutcome::DISCARDED_NO_NETWORK_ERROR_LOGGING_SERVICE);
+  RecordRequestOutcome(RequestOutcome::kDiscardedNoNetworkErrorLoggingService);
 }
 
 // static
 void NetworkErrorLoggingService::RecordRequestDiscardedForInsecureOrigin() {
-  RecordRequestOutcome(RequestOutcome::DISCARDED_INSECURE_ORIGIN);
+  RecordRequestOutcome(RequestOutcome::kDiscardedInsecureOrigin);
 }
 
 // static
diff --git a/net/network_error_logging/network_error_logging_service.h b/net/network_error_logging/network_error_logging_service.h
index 1325367..812d8b9 100644
--- a/net/network_error_logging/network_error_logging_service.h
+++ b/net/network_error_logging/network_error_logging_service.h
@@ -120,6 +120,7 @@
 
   static const char kHeaderOutcomeHistogram[];
   static const char kRequestOutcomeHistogram[];
+  static const char kSignedExchangeRequestOutcomeHistogram[];
 
   enum class HeaderOutcome {
     DISCARDED_NO_NETWORK_ERROR_LOGGING_SERVICE = 0,
@@ -146,19 +147,20 @@
   };
 
   enum class RequestOutcome {
-    DISCARDED_NO_NETWORK_ERROR_LOGGING_SERVICE = 0,
+    kDiscardedNoNetworkErrorLoggingService = 0,
 
-    DISCARDED_NO_REPORTING_SERVICE = 1,
-    DISCARDED_INSECURE_ORIGIN = 2,
-    DISCARDED_NO_ORIGIN_POLICY = 3,
-    DISCARDED_UNMAPPED_ERROR = 4,
-    DISCARDED_REPORTING_UPLOAD = 5,
-    DISCARDED_UNSAMPLED_SUCCESS = 6,
-    DISCARDED_UNSAMPLED_FAILURE = 7,
-    QUEUED = 8,
-    DISCARDED_NON_DNS_SUBDOMAIN_REPORT = 9,
+    kDiscardedNoReportingService = 1,
+    kDiscardedInsecureOrigin = 2,
+    kDiscardedNoOriginPolicy = 3,
+    kDiscardedUnmappedError = 4,
+    kDiscardedReportingUpload = 5,
+    kDiscardedUnsampledSuccess = 6,
+    kDiscardedUnsampledFailure = 7,
+    kQueued = 8,
+    kDiscardedNonDNSSubdomainReport = 9,
+    kDiscardedIPAddressMismatch = 10,
 
-    MAX
+    kMaxValue = kDiscardedIPAddressMismatch
   };
 
   static void RecordHeaderDiscardedForNoNetworkErrorLoggingService();
diff --git a/net/proxy_resolution/pac_library_unittest.cc b/net/proxy_resolution/pac_library_unittest.cc
index c32e5b9..8a911e9 100644
--- a/net/proxy_resolution/pac_library_unittest.cc
+++ b/net/proxy_resolution/pac_library_unittest.cc
@@ -254,36 +254,13 @@
     return nullptr;
   }
   std::unique_ptr<SSLClientSocket> CreateSSLClientSocket(
-      std::unique_ptr<ClientSocketHandle> transport_socket,
+      std::unique_ptr<StreamSocket> stream_socket,
       const HostPortPair& host_and_port,
       const SSLConfig& ssl_config,
       const SSLClientSocketContext& context) override {
     ADD_FAILURE() << "Called CreateSSLClientSocket()";
     return nullptr;
   }
-  std::unique_ptr<SSLClientSocket> CreateSSLClientSocket(
-      std::unique_ptr<StreamSocket> nested_socket,
-      const HostPortPair& host_and_port,
-      const SSLConfig& ssl_config,
-      const SSLClientSocketContext& context) override {
-    ADD_FAILURE() << "Called CreateSSLClientSocket()";
-    return nullptr;
-  }
-  std::unique_ptr<ProxyClientSocket> CreateProxyClientSocket(
-      std::unique_ptr<ClientSocketHandle> transport_socket,
-      const std::string& user_agent,
-      const HostPortPair& endpoint,
-      const ProxyServer& proxy_server,
-      HttpAuthController* http_auth_controller,
-      bool tunnel,
-      bool using_spdy,
-      NextProto negotiated_protocol,
-      ProxyDelegate* proxy_delegate,
-      bool is_https_proxy,
-      const NetworkTrafficAnnotationTag& traffic_annotation) override {
-    ADD_FAILURE() << "Called CreateProxyClientSocket()";
-    return nullptr;
-  }
   std::unique_ptr<ProxyClientSocket> CreateProxyClientSocket(
       std::unique_ptr<StreamSocket> stream_socket,
       const std::string& user_agent,
diff --git a/net/proxy_resolution/proxy_config_service_android.cc b/net/proxy_resolution/proxy_config_service_android.cc
index 25680aa..4cf71546 100644
--- a/net/proxy_resolution/proxy_config_service_android.cc
+++ b/net/proxy_resolution/proxy_config_service_android.cc
@@ -271,16 +271,15 @@
 class ProxyConfigServiceAndroid::Delegate
     : public base::RefCountedThreadSafe<Delegate> {
  public:
-  Delegate(const scoped_refptr<base::SequencedTaskRunner>& network_task_runner,
+  Delegate(const scoped_refptr<base::SequencedTaskRunner>& main_task_runner,
            const scoped_refptr<base::SequencedTaskRunner>& jni_task_runner,
            const GetPropertyCallback& get_property_callback)
       : jni_delegate_(this),
-        network_task_runner_(network_task_runner),
+        main_task_runner_(main_task_runner),
         jni_task_runner_(jni_task_runner),
         get_property_callback_(get_property_callback),
         exclude_pac_url_(false),
-        has_proxy_override_(false) {
-  }
+        has_proxy_override_(false) {}
 
   void SetupJNI() {
     DCHECK(InJNISequence());
@@ -297,9 +296,9 @@
     DCHECK(InJNISequence());
     ProxyConfigWithAnnotation proxy_config;
     GetLatestProxyConfigInternal(get_property_callback_, &proxy_config);
-    network_task_runner_->PostTask(
-        FROM_HERE, base::BindOnce(&Delegate::SetNewConfigInNetworkSequence,
-                                  this, proxy_config));
+    main_task_runner_->PostTask(
+        FROM_HERE, base::BindOnce(&Delegate::SetNewConfigInMainSequence, this,
+                                  proxy_config));
   }
 
   void Shutdown() {
@@ -313,17 +312,17 @@
 
   // Called only in the network sequence.
   void AddObserver(Observer* observer) {
-    DCHECK(InNetworkSequence());
+    DCHECK(InMainSequence());
     observers_.AddObserver(observer);
   }
 
   void RemoveObserver(Observer* observer) {
-    DCHECK(InNetworkSequence());
+    DCHECK(InMainSequence());
     observers_.RemoveObserver(observer);
   }
 
   ConfigAvailability GetLatestProxyConfig(ProxyConfigWithAnnotation* config) {
-    DCHECK(InNetworkSequence());
+    DCHECK(InMainSequence());
     if (!config)
       return ProxyConfigService::CONFIG_UNSET;
     *config = proxy_config_;
@@ -338,9 +337,9 @@
 
     ProxyConfigWithAnnotation proxy_config;
     GetLatestProxyConfigInternal(get_property_callback_, &proxy_config);
-    network_task_runner_->PostTask(
-        FROM_HERE, base::BindOnce(&Delegate::SetNewConfigInNetworkSequence,
-                                  this, proxy_config));
+    main_task_runner_->PostTask(
+        FROM_HERE, base::BindOnce(&Delegate::SetNewConfigInMainSequence, this,
+                                  proxy_config));
   }
 
   // Called in the JNI sequence.
@@ -359,9 +358,9 @@
       CreateStaticProxyConfig(host, port, pac_url, exclusion_list,
           &proxy_config);
     }
-    network_task_runner_->PostTask(
-        FROM_HERE, base::BindOnce(&Delegate::SetNewConfigInNetworkSequence,
-                                  this, proxy_config));
+    main_task_runner_->PostTask(
+        FROM_HERE, base::BindOnce(&Delegate::SetNewConfigInMainSequence, this,
+                                  proxy_config));
   }
 
   void set_exclude_pac_url(bool enabled) {
@@ -384,9 +383,9 @@
       return result;
     }
 
-    network_task_runner_->PostTaskAndReply(
+    main_task_runner_->PostTaskAndReply(
         FROM_HERE,
-        base::BindOnce(&Delegate::SetNewConfigInNetworkSequence, this,
+        base::BindOnce(&Delegate::SetNewConfigInMainSequence, this,
                        proxy_config),
         std::move(callback));
 
@@ -403,9 +402,9 @@
 
     ProxyConfigWithAnnotation proxy_config;
     GetLatestProxyConfigInternal(get_property_callback_, &proxy_config);
-    network_task_runner_->PostTaskAndReply(
+    main_task_runner_->PostTaskAndReply(
         FROM_HERE,
-        base::BindOnce(&Delegate::SetNewConfigInNetworkSequence, this,
+        base::BindOnce(&Delegate::SetNewConfigInMainSequence, this,
                        proxy_config),
         std::move(callback));
     has_proxy_override_ = false;
@@ -455,9 +454,9 @@
   }
 
   // Called on the network sequence.
-  void SetNewConfigInNetworkSequence(
+  void SetNewConfigInMainSequence(
       const ProxyConfigWithAnnotation& proxy_config) {
-    DCHECK(InNetworkSequence());
+    DCHECK(InMainSequence());
     proxy_config_ = proxy_config;
     for (auto& observer : observers_) {
       observer.OnProxyConfigChanged(proxy_config,
@@ -469,15 +468,15 @@
     return jni_task_runner_->RunsTasksInCurrentSequence();
   }
 
-  bool InNetworkSequence() const {
-    return network_task_runner_->RunsTasksInCurrentSequence();
+  bool InMainSequence() const {
+    return main_task_runner_->RunsTasksInCurrentSequence();
   }
 
   ScopedJavaGlobalRef<jobject> java_proxy_change_listener_;
 
   JNIDelegateImpl jni_delegate_;
   base::ObserverList<Observer>::Unchecked observers_;
-  scoped_refptr<base::SequencedTaskRunner> network_task_runner_;
+  scoped_refptr<base::SequencedTaskRunner> main_task_runner_;
   scoped_refptr<base::SequencedTaskRunner> jni_task_runner_;
   GetPropertyCallback get_property_callback_;
   ProxyConfigWithAnnotation proxy_config_;
@@ -489,10 +488,11 @@
 };
 
 ProxyConfigServiceAndroid::ProxyConfigServiceAndroid(
-    const scoped_refptr<base::SequencedTaskRunner>& network_task_runner,
+    const scoped_refptr<base::SequencedTaskRunner>& main_task_runner,
     const scoped_refptr<base::SequencedTaskRunner>& jni_task_runner)
-    : delegate_(new Delegate(
-        network_task_runner, jni_task_runner, base::Bind(&GetJavaProperty))) {
+    : delegate_(new Delegate(main_task_runner,
+                             jni_task_runner,
+                             base::BindRepeating(&GetJavaProperty))) {
   delegate_->SetupJNI();
   delegate_->FetchInitialConfig();
 }
@@ -520,11 +520,12 @@
 }
 
 ProxyConfigServiceAndroid::ProxyConfigServiceAndroid(
-    const scoped_refptr<base::SequencedTaskRunner>& network_task_runner,
+    const scoped_refptr<base::SequencedTaskRunner>& main_task_runner,
     const scoped_refptr<base::SequencedTaskRunner>& jni_task_runner,
     GetPropertyCallback get_property_callback)
-    : delegate_(new Delegate(
-        network_task_runner, jni_task_runner, get_property_callback)) {
+    : delegate_(new Delegate(main_task_runner,
+                             jni_task_runner,
+                             get_property_callback)) {
   delegate_->SetupJNI();
   delegate_->FetchInitialConfig();
 }
diff --git a/net/proxy_resolution/proxy_config_service_android.h b/net/proxy_resolution/proxy_config_service_android.h
index 5a5a15f..c12c4f09 100644
--- a/net/proxy_resolution/proxy_config_service_android.h
+++ b/net/proxy_resolution/proxy_config_service_android.h
@@ -59,7 +59,7 @@
   };
 
   ProxyConfigServiceAndroid(
-      const scoped_refptr<base::SequencedTaskRunner>& network_task_runner,
+      const scoped_refptr<base::SequencedTaskRunner>& main_task_runner,
       const scoped_refptr<base::SequencedTaskRunner>& jni_task_runner);
 
   ~ProxyConfigServiceAndroid() override;
@@ -112,7 +112,7 @@
 
   // For tests.
   ProxyConfigServiceAndroid(
-      const scoped_refptr<base::SequencedTaskRunner>& network_task_runner,
+      const scoped_refptr<base::SequencedTaskRunner>& main_task_runner,
       const scoped_refptr<base::SequencedTaskRunner>& jni_task_runner,
       GetPropertyCallback get_property_callback);
 
diff --git a/net/quic/bidirectional_stream_quic_impl_unittest.cc b/net/quic/bidirectional_stream_quic_impl_unittest.cc
index 1cb6118a..b0a6ae8 100644
--- a/net/quic/bidirectional_stream_quic_impl_unittest.cc
+++ b/net/quic/bidirectional_stream_quic_impl_unittest.cc
@@ -872,7 +872,6 @@
   size_t spdy_request_headers_frame_length;
   quic::QuicStreamOffset header_stream_offset = 0;
   client_maker_.SetEncryptionLevel(quic::ENCRYPTION_ZERO_RTT);
-  client_maker_.SetLongHeaderType(quic::ZERO_RTT_PROTECTED);
   AddWrite(ConstructRequestHeadersPacketInner(
       1, GetNthClientInitiatedBidirectionalStreamId(0), kFin, DEFAULT_PRIORITY,
       &spdy_request_headers_frame_length, &header_stream_offset));
@@ -976,7 +975,6 @@
   SetRequest("GET", "/", DEFAULT_PRIORITY);
   quic::QuicStreamOffset offset = 0;
   client_maker_.SetEncryptionLevel(quic::ENCRYPTION_ZERO_RTT);
-  client_maker_.SetLongHeaderType(quic::ZERO_RTT_PROTECTED);
   AddWrite(ConstructRequestHeadersPacketInner(
       1, GetNthClientInitiatedBidirectionalStreamId(0), kFin, DEFAULT_PRIORITY,
       nullptr, &offset));
@@ -1810,7 +1808,6 @@
   size_t spdy_request_headers_frame_length;
   quic::QuicStreamOffset header_stream_offset = 0;
   client_maker_.SetEncryptionLevel(quic::ENCRYPTION_ZERO_RTT);
-  client_maker_.SetLongHeaderType(quic::ZERO_RTT_PROTECTED);
   AddWrite(ConstructRequestHeadersPacketInner(
       1, GetNthClientInitiatedBidirectionalStreamId(0), kFin, DEFAULT_PRIORITY,
       &spdy_request_headers_frame_length, &header_stream_offset));
@@ -1857,7 +1854,6 @@
   size_t spdy_request_headers_frame_length;
   quic::QuicStreamOffset header_stream_offset = 0;
   client_maker_.SetEncryptionLevel(quic::ENCRYPTION_ZERO_RTT);
-  client_maker_.SetLongHeaderType(quic::ZERO_RTT_PROTECTED);
   AddWrite(ConstructRequestHeadersPacketInner(
       1, GetNthClientInitiatedBidirectionalStreamId(0), kFin, DEFAULT_PRIORITY,
       &spdy_request_headers_frame_length, &header_stream_offset));
@@ -2332,7 +2328,6 @@
   SetRequest("GET", "/", DEFAULT_PRIORITY);
   size_t spdy_request_headers_frame_length;
   client_maker_.SetEncryptionLevel(quic::ENCRYPTION_ZERO_RTT);
-  client_maker_.SetLongHeaderType(quic::ZERO_RTT_PROTECTED);
   AddWrite(ConstructRequestHeadersPacket(1, kFin, DEFAULT_PRIORITY,
                                          &spdy_request_headers_frame_length));
   AddWrite(ConstructClientAckPacket(2, 3, 1, 2));  // Ack the data packet
diff --git a/net/quic/quic_end_to_end_unittest.cc b/net/quic/quic_end_to_end_unittest.cc
index beeb4ff..30adf6d 100644
--- a/net/quic/quic_end_to_end_unittest.cc
+++ b/net/quic/quic_end_to_end_unittest.cc
@@ -107,8 +107,7 @@
         cert_transparency_verifier_(new MultiLogCTVerifier()),
         ssl_config_service_(new SSLConfigServiceDefaults),
         proxy_resolution_service_(ProxyResolutionService::CreateDirect()),
-        auth_handler_factory_(
-            HttpAuthHandlerFactory::CreateDefault(&host_resolver_)),
+        auth_handler_factory_(HttpAuthHandlerFactory::CreateDefault()),
         strike_register_no_startup_period_(false) {
     request_.method = "GET";
     request_.url = GURL("https://test.example.com/");
diff --git a/net/quic/quic_flags_list.h b/net/quic/quic_flags_list.h
index c2ddbeb..174a004 100644
--- a/net/quic/quic_flags_list.h
+++ b/net/quic/quic_flags_list.h
@@ -6,6 +6,7 @@
 // inside a macro to generate values. The following line silences a
 // presubmit warning that would otherwise be triggered by this:
 // no-include-guard-because-multiply-included
+// NOLINT(build/header_guard)
 
 // This file contains the list of QUIC protocol flags.
 
@@ -220,12 +221,6 @@
           FLAGS_quic_reloadable_flag_quic_bbr_startup_rate_reduction,
           false)
 
-// If true, QuicDispatcher will not assume all blocked writers share the same
-// opinion about whether their packet writers are blocked.
-QUIC_FLAG(bool,
-          FLAGS_quic_restart_flag_quic_check_blocked_writer_for_blockage,
-          true)
-
 // If true, disconnected quic connection will not be added to dispatcher's write
 // blocked list.
 QUIC_FLAG(
@@ -238,16 +233,6 @@
 QUIC_FLAG(bool,
           FLAGS_quic_reloadable_flag_quic_set_transmission_type_for_next_frame,
           true)
-// If true, always send connection close/reset for IETF connections.
-QUIC_FLAG(bool,
-          FLAGS_quic_reloadable_flag_quic_always_reset_ietf_connections,
-          true)
-
-// When true, allows the AKD2 and AKD4 connection options to continue activating
-// ack decimation with reordering.
-QUIC_FLAG(bool,
-          FLAGS_quic_reloadable_flag_quic_keep_ack_decimation_reordering,
-          true)
 
 // If true, log leaf cert subject name into warning log.
 QUIC_FLAG(bool,
@@ -282,12 +267,6 @@
     FLAGS_quic_reloadable_flag_quic_simplify_build_connectivity_probing_packet,
     true)
 
-// If true, for QUIC v44+, long header packet type is determined by encryption
-// level.
-QUIC_FLAG(bool,
-          FLAGS_quic_reloadable_flag_quic_encryption_driven_header_type,
-          true)
-
 // If true, use one loss algorithm per encryption level.
 QUIC_FLAG(bool, FLAGS_quic_reloadable_flag_quic_use_uber_loss_algorithm, false)
 
@@ -303,3 +282,16 @@
     bool,
     FLAGS_quic_reloadable_flag_quic_faster_interval_add_in_sequence_buffer,
     false)
+
+// If true, GFE time wait list will send termination packets based on current
+// packet's encryption level.
+QUIC_FLAG(bool, FLAGS_quic_reloadable_flag_quic_fix_termination_packets, false)
+
+// If true, stop using AckBundling mode to send ACK, also deprecate ack_queued
+// from QuicConnection.
+QUIC_FLAG(bool,
+          FLAGS_quic_reloadable_flag_quic_deprecate_ack_bundling_mode,
+          false)
+
+// If true, enforce that QUIC CHLOs fit in one packet.
+QUIC_FLAG(bool, FLAGS_quic_enforce_single_packet_chlo, true)
diff --git a/net/quic/quic_http_stream_test.cc b/net/quic/quic_http_stream_test.cc
index e52c28e..3384fee 100644
--- a/net/quic/quic_http_stream_test.cc
+++ b/net/quic/quic_http_stream_test.cc
@@ -53,6 +53,7 @@
 #include "net/third_party/quic/core/crypto/quic_encrypter.h"
 #include "net/third_party/quic/core/http/spdy_utils.h"
 #include "net/third_party/quic/core/quic_connection.h"
+#include "net/third_party/quic/core/quic_utils.h"
 #include "net/third_party/quic/core/quic_write_blocked_list.h"
 #include "net/third_party/quic/core/tls_client_handshaker.h"
 #include "net/third_party/quic/platform/api/quic_string_piece.h"
@@ -1174,7 +1175,6 @@
   size_t spdy_request_headers_frame_length;
   quic::QuicStreamOffset header_stream_offset = 0;
   client_maker_.SetEncryptionLevel(quic::ENCRYPTION_ZERO_RTT);
-  client_maker_.SetLongHeaderType(quic::ZERO_RTT_PROTECTED);
   AddWrite(InnerConstructRequestHeadersPacket(
       1, GetNthClientInitiatedBidirectionalStreamId(0), kIncludeVersion, kFin,
       DEFAULT_PRIORITY, &spdy_request_headers_frame_length,
@@ -2318,9 +2318,10 @@
         DEFAULT_PRIORITY, &header_stream_offset));
   }
   AddWrite(ConstructClientRstStreamVaryMismatchAndRequestHeadersPacket(
-      client_packet_number++, stream_id_ + quic::test::NextStreamId(version_),
-      !kIncludeVersion, kFin, DEFAULT_PRIORITY, promise_id_,
-      &spdy_request_header_frame_length, &header_stream_offset));
+      client_packet_number++,
+      stream_id_ + quic::QuicUtils::StreamIdDelta(version_), !kIncludeVersion,
+      kFin, DEFAULT_PRIORITY, promise_id_, &spdy_request_header_frame_length,
+      &header_stream_offset));
   AddWrite(ConstructClientAckPacket(client_packet_number++, 3, 1, 2));
   AddWrite(ConstructClientRstStreamCancelledPacket(client_packet_number++));
 
@@ -2380,7 +2381,7 @@
   EXPECT_EQ(
       QuicHttpStreamPeer::GetQuicChromiumClientStream(promised_stream_.get())
           ->id(),
-      stream_id_ + quic::test::NextStreamId(version_));
+      stream_id_ + quic::QuicUtils::StreamIdDelta(version_));
 
   // After rendezvous failure, the push stream has been cancelled.
   EXPECT_EQ(session_->GetPromisedByUrl(promise_url_), nullptr);
@@ -2394,7 +2395,7 @@
   SetResponse("404 Not Found", string());
   size_t spdy_response_header_frame_length;
   ProcessPacket(InnerConstructResponseHeadersPacket(
-      3, stream_id_ + quic::test::NextStreamId(version_), kFin,
+      3, stream_id_ + quic::QuicUtils::StreamIdDelta(version_), kFin,
       &spdy_response_header_frame_length));
 
   base::RunLoop().RunUntilIdle();
diff --git a/net/quic/quic_network_transaction_unittest.cc b/net/quic/quic_network_transaction_unittest.cc
index 7ce82f69..fd626511 100644
--- a/net/quic/quic_network_transaction_unittest.cc
+++ b/net/quic/quic_network_transaction_unittest.cc
@@ -279,8 +279,7 @@
         cert_transparency_verifier_(new MultiLogCTVerifier()),
         ssl_config_service_(new SSLConfigServiceDefaults),
         proxy_resolution_service_(ProxyResolutionService::CreateDirect()),
-        auth_handler_factory_(
-            HttpAuthHandlerFactory::CreateDefault(&host_resolver_)),
+        auth_handler_factory_(HttpAuthHandlerFactory::CreateDefault()),
         ssl_data_(ASYNC, OK) {
     request_.method = "GET";
     std::string url("https://");
@@ -2254,7 +2253,6 @@
 
   std::string request_data;
   client_maker_.SetEncryptionLevel(quic::ENCRYPTION_ZERO_RTT);
-  client_maker_.SetLongHeaderType(quic::ZERO_RTT_PROTECTED);
 
   // The request will initially go out over QUIC.
   MockQuicData quic_data;
@@ -2375,7 +2373,6 @@
 
   std::string request_data;
   client_maker_.SetEncryptionLevel(quic::ENCRYPTION_ZERO_RTT);
-  client_maker_.SetLongHeaderType(quic::ZERO_RTT_PROTECTED);
 
   // The request will initially go out over QUIC.
   MockQuicData quic_data;
@@ -2510,7 +2507,6 @@
 
   std::string request_data;
   client_maker_.SetEncryptionLevel(quic::ENCRYPTION_ZERO_RTT);
-  client_maker_.SetLongHeaderType(quic::ZERO_RTT_PROTECTED);
 
   // The request will initially go out over QUIC.
   MockQuicData quic_data;
@@ -2643,7 +2639,6 @@
 
   std::string request_data;
   client_maker_.SetEncryptionLevel(quic::ENCRYPTION_ZERO_RTT);
-  client_maker_.SetLongHeaderType(quic::ZERO_RTT_PROTECTED);
   quic_data.AddWrite(SYNCHRONOUS,
                      client_maker_.MakeRequestHeadersPacketAndSaveData(
                          1, GetNthClientInitiatedBidirectionalStreamId(0), true,
@@ -2754,7 +2749,6 @@
 
   std::string request_data;
   client_maker_.SetEncryptionLevel(quic::ENCRYPTION_ZERO_RTT);
-  client_maker_.SetLongHeaderType(quic::ZERO_RTT_PROTECTED);
   quic_data.AddWrite(SYNCHRONOUS,
                      client_maker_.MakeRequestHeadersPacketAndSaveData(
                          1, GetNthClientInitiatedBidirectionalStreamId(0), true,
@@ -2873,7 +2867,6 @@
 
   std::string request_data;
   client_maker_.SetEncryptionLevel(quic::ENCRYPTION_ZERO_RTT);
-  client_maker_.SetLongHeaderType(quic::ZERO_RTT_PROTECTED);
   quic_data.AddWrite(SYNCHRONOUS,
                      client_maker_.MakeRequestHeadersPacketAndSaveData(
                          1, GetNthClientInitiatedBidirectionalStreamId(0), true,
@@ -2995,7 +2988,6 @@
   MockQuicData quic_data;
   quic::QuicStreamOffset header_stream_offset = 0;
   client_maker_.SetEncryptionLevel(quic::ENCRYPTION_ZERO_RTT);
-  client_maker_.SetLongHeaderType(quic::ZERO_RTT_PROTECTED);
   quic_data.AddWrite(
       SYNCHRONOUS,
       ConstructClientRequestHeadersPacket(
@@ -3068,7 +3060,6 @@
 
   std::string request_data;
   client_maker_.SetEncryptionLevel(quic::ENCRYPTION_ZERO_RTT);
-  client_maker_.SetLongHeaderType(quic::ZERO_RTT_PROTECTED);
   quic_data.AddWrite(SYNCHRONOUS,
                      client_maker_.MakeRequestHeadersPacketAndSaveData(
                          1, GetNthClientInitiatedBidirectionalStreamId(0), true,
@@ -3203,7 +3194,6 @@
 
   std::string request_data;
   client_maker_.SetEncryptionLevel(quic::ENCRYPTION_ZERO_RTT);
-  client_maker_.SetLongHeaderType(quic::ZERO_RTT_PROTECTED);
   quic_data.AddWrite(SYNCHRONOUS,
                      client_maker_.MakeRequestHeadersPacketAndSaveData(
                          1, GetNthClientInitiatedBidirectionalStreamId(0), true,
@@ -3342,7 +3332,6 @@
 
   std::string request_data;
   client_maker_.SetEncryptionLevel(quic::ENCRYPTION_ZERO_RTT);
-  client_maker_.SetLongHeaderType(quic::ZERO_RTT_PROTECTED);
   quic_data.AddWrite(SYNCHRONOUS,
                      client_maker_.MakeRequestHeadersPacketAndSaveData(
                          1, GetNthClientInitiatedBidirectionalStreamId(0), true,
@@ -3488,7 +3477,6 @@
 
   std::string request_data;
   client_maker_.SetEncryptionLevel(quic::ENCRYPTION_ZERO_RTT);
-  client_maker_.SetLongHeaderType(quic::ZERO_RTT_PROTECTED);
   quic_data.AddWrite(SYNCHRONOUS,
                      client_maker_.MakeRequestHeadersPacketAndSaveData(
                          1, GetNthClientInitiatedBidirectionalStreamId(0), true,
@@ -3632,7 +3620,6 @@
 
   std::string request_data;
   client_maker_.SetEncryptionLevel(quic::ENCRYPTION_ZERO_RTT);
-  client_maker_.SetLongHeaderType(quic::ZERO_RTT_PROTECTED);
   quic_data.AddWrite(SYNCHRONOUS,
                      client_maker_.MakeRequestHeadersPacketAndSaveData(
                          1, GetNthClientInitiatedBidirectionalStreamId(0), true,
@@ -3757,7 +3744,6 @@
   MockQuicData quic_data;
   quic::QuicStreamOffset header_stream_offset = 0;
   client_maker_.SetEncryptionLevel(quic::ENCRYPTION_ZERO_RTT);
-  client_maker_.SetLongHeaderType(quic::ZERO_RTT_PROTECTED);
   quic_data.AddWrite(
       SYNCHRONOUS,
       ConstructClientRequestHeadersPacket(
@@ -3849,7 +3835,6 @@
 
   std::string request_data;
   client_maker_.SetEncryptionLevel(quic::ENCRYPTION_ZERO_RTT);
-  client_maker_.SetLongHeaderType(quic::ZERO_RTT_PROTECTED);
   quic_data.AddWrite(SYNCHRONOUS,
                      client_maker_.MakeRequestHeadersPacketAndSaveData(
                          1, GetNthClientInitiatedBidirectionalStreamId(0), true,
@@ -4864,7 +4849,6 @@
   MockQuicData mock_quic_data;
   quic::QuicStreamOffset header_stream_offset = 0;
   client_maker_.SetEncryptionLevel(quic::ENCRYPTION_ZERO_RTT);
-  client_maker_.SetLongHeaderType(quic::ZERO_RTT_PROTECTED);
   mock_quic_data.AddWrite(
       SYNCHRONOUS,
       ConstructClientRequestHeadersPacket(
@@ -4901,7 +4885,6 @@
 TEST_P(QuicNetworkTransactionTest, ZeroRTTWithNoHttpRace) {
   MockQuicData mock_quic_data;
   client_maker_.SetEncryptionLevel(quic::ENCRYPTION_ZERO_RTT);
-  client_maker_.SetLongHeaderType(quic::ZERO_RTT_PROTECTED);
   mock_quic_data.AddWrite(
       SYNCHRONOUS, ConstructClientRequestHeadersPacket(
                        1, GetNthClientInitiatedBidirectionalStreamId(0), true,
@@ -5022,7 +5005,6 @@
   quic::QuicStreamOffset client_header_stream_offset = 0;
   quic::QuicStreamOffset server_header_stream_offset = 0;
   client_maker_.SetEncryptionLevel(quic::ENCRYPTION_ZERO_RTT);
-  client_maker_.SetLongHeaderType(quic::ZERO_RTT_PROTECTED);
   mock_quic_data.AddWrite(
       SYNCHRONOUS, ConstructClientRequestHeadersPacket(
                        1, GetNthClientInitiatedBidirectionalStreamId(0), true,
@@ -5116,7 +5098,6 @@
   quic::QuicStreamOffset client_header_stream_offset = 0;
   quic::QuicStreamOffset server_header_stream_offset = 0;
   client_maker_.SetEncryptionLevel(quic::ENCRYPTION_ZERO_RTT);
-  client_maker_.SetLongHeaderType(quic::ZERO_RTT_PROTECTED);
   mock_quic_data.AddWrite(
       SYNCHRONOUS, ConstructClientRequestHeadersPacket(
                        1, GetNthClientInitiatedBidirectionalStreamId(0), true,
@@ -5523,7 +5504,6 @@
   MockQuicData mock_quic_data;
   quic::QuicStreamOffset header_stream_offset = 0;
   client_maker_.SetEncryptionLevel(quic::ENCRYPTION_ZERO_RTT);
-  client_maker_.SetLongHeaderType(quic::ZERO_RTT_PROTECTED);
   mock_quic_data.AddWrite(
       SYNCHRONOUS,
       ConstructClientRequestHeadersPacket(
@@ -6471,7 +6451,7 @@
     storage_.set_ssl_config_service(
         std::make_unique<SSLConfigServiceDefaults>());
     storage_.set_http_auth_handler_factory(
-        HttpAuthHandlerFactory::CreateDefault(host_resolver()));
+        HttpAuthHandlerFactory::CreateDefault());
     storage_.set_http_server_properties(
         std::make_unique<HttpServerPropertiesImpl>());
     storage_.set_job_factory(std::make_unique<URLRequestJobFactoryImpl>());
@@ -6747,8 +6727,7 @@
         cert_transparency_verifier_(new MultiLogCTVerifier()),
         ssl_config_service_(new SSLConfigServiceDefaults),
         proxy_resolution_service_(ProxyResolutionService::CreateDirect()),
-        auth_handler_factory_(
-            HttpAuthHandlerFactory::CreateDefault(&host_resolver_)),
+        auth_handler_factory_(HttpAuthHandlerFactory::CreateDefault()),
         random_generator_(0),
         ssl_data_(ASYNC, OK) {}
 
diff --git a/net/quic/quic_proxy_client_socket_unittest.cc b/net/quic/quic_proxy_client_socket_unittest.cc
index 4e2aeac..662377d 100644
--- a/net/quic/quic_proxy_client_socket_unittest.cc
+++ b/net/quic/quic_proxy_client_socket_unittest.cc
@@ -5,7 +5,9 @@
 #include "net/quic/quic_proxy_client_socket.h"
 
 #include <memory>
+#include <tuple>
 #include <utility>
+#include <vector>
 
 #include "base/bind.h"
 #include "base/memory/ptr_util.h"
@@ -145,8 +147,7 @@
         proxy_host_port_(kProxyHost, kProxyPort),
         endpoint_host_port_(kOriginHost, kOriginPort),
         host_resolver_(new MockCachingHostResolver()),
-        http_auth_handler_factory_(
-            HttpAuthHandlerFactory::CreateDefault(host_resolver_.get())) {
+        http_auth_handler_factory_(HttpAuthHandlerFactory::CreateDefault()) {
     IPAddress ip(192, 0, 2, 33);
     peer_addr_ = IPEndPoint(ip, 443);
     clock_.AdvanceTime(quic::QuicTime::Delta::FromMilliseconds(20));
@@ -259,10 +260,10 @@
     sock_.reset(new QuicProxyClientSocket(
         std::move(stream_handle), std::move(session_handle_), user_agent_,
         endpoint_host_port_, net_log_.bound(),
-        new HttpAuthController(HttpAuth::AUTH_PROXY,
-                               GURL("https://" + proxy_host_port_.ToString()),
-                               &http_auth_cache_,
-                               http_auth_handler_factory_.get())));
+        new HttpAuthController(
+            HttpAuth::AUTH_PROXY,
+            GURL("https://" + proxy_host_port_.ToString()), &http_auth_cache_,
+            http_auth_handler_factory_.get(), host_resolver_.get())));
 
     session_->StartReading();
   }
diff --git a/net/quic/quic_test_packet_maker.cc b/net/quic/quic_test_packet_maker.cc
index 790bb70a..52ae592b 100644
--- a/net/quic/quic_test_packet_maker.cc
+++ b/net/quic/quic_test_packet_maker.cc
@@ -42,10 +42,7 @@
       spdy_response_framer_(spdy::SpdyFramer::ENABLE_COMPRESSION),
       perspective_(perspective),
       encryption_level_(quic::ENCRYPTION_FORWARD_SECURE),
-      long_header_type_(
-          FLAGS_quic_reloadable_flag_quic_encryption_driven_header_type
-              ? quic::INVALID_PACKET_TYPE
-              : quic::HANDSHAKE),
+      long_header_type_(quic::INVALID_PACKET_TYPE),
       client_headers_include_h2_stream_dependency_(
           client_headers_include_h2_stream_dependency &&
           version >= quic::QUIC_VERSION_43) {
@@ -64,9 +61,9 @@
                                                    bool include_version) {
   quic::QuicPacketHeader header;
   header.destination_connection_id = connection_id_;
-  header.destination_connection_id_length = GetDestinationConnectionIdLength();
+  header.destination_connection_id_included = HasDestinationConnectionId();
   header.source_connection_id = connection_id_;
-  header.source_connection_id_length = GetSourceConnectionIdLength();
+  header.source_connection_id_included = HasSourceConnectionId();
   header.reset_flag = false;
   header.version_flag = ShouldIncludeVersion(include_version);
   header.long_packet_type = long_header_type_;
@@ -118,9 +115,9 @@
     bool include_version) {
   quic::QuicPacketHeader header;
   header.destination_connection_id = connection_id_;
-  header.destination_connection_id_length = GetDestinationConnectionIdLength();
+  header.destination_connection_id_included = HasDestinationConnectionId();
   header.source_connection_id = connection_id_;
-  header.source_connection_id_length = GetSourceConnectionIdLength();
+  header.source_connection_id_included = HasSourceConnectionId();
   header.reset_flag = false;
   header.version_flag = ShouldIncludeVersion(include_version);
   header.long_packet_type = long_header_type_;
@@ -141,7 +138,6 @@
 std::unique_ptr<quic::QuicReceivedPacket>
 QuicTestPacketMaker::MakeDummyCHLOPacket(uint64_t packet_num) {
   SetEncryptionLevel(quic::ENCRYPTION_NONE);
-  SetLongHeaderType(quic::INITIAL);
   InitializeHeader(packet_num, /*include_version=*/true);
 
   quic::CryptoHandshakeMessage message =
@@ -182,9 +178,9 @@
                                           uint64_t least_unacked) {
   quic::QuicPacketHeader header;
   header.destination_connection_id = connection_id_;
-  header.destination_connection_id_length = GetDestinationConnectionIdLength();
+  header.destination_connection_id_included = HasDestinationConnectionId();
   header.source_connection_id = connection_id_;
-  header.source_connection_id_length = GetSourceConnectionIdLength();
+  header.source_connection_id_included = HasSourceConnectionId();
   header.reset_flag = false;
   header.version_flag = ShouldIncludeVersion(include_version);
   header.long_packet_type = long_header_type_;
@@ -236,9 +232,9 @@
     bool include_stop_sending_if_v99) {
   quic::QuicPacketHeader header;
   header.destination_connection_id = connection_id_;
-  header.destination_connection_id_length = GetDestinationConnectionIdLength();
+  header.destination_connection_id_included = HasDestinationConnectionId();
   header.source_connection_id = connection_id_;
-  header.source_connection_id_length = GetSourceConnectionIdLength();
+  header.source_connection_id_included = HasSourceConnectionId();
   header.reset_flag = false;
   header.version_flag = ShouldIncludeVersion(include_version);
   header.long_packet_type = long_header_type_;
@@ -274,9 +270,9 @@
                                                quic::QuicStreamId stream_id) {
   quic::QuicPacketHeader header;
   header.destination_connection_id = connection_id_;
-  header.destination_connection_id_length = GetDestinationConnectionIdLength();
+  header.destination_connection_id_included = HasDestinationConnectionId();
   header.source_connection_id = connection_id_;
-  header.source_connection_id_length = GetSourceConnectionIdLength();
+  header.source_connection_id_included = HasSourceConnectionId();
   header.reset_flag = false;
   header.version_flag = ShouldIncludeVersion(include_version);
   header.long_packet_type = long_header_type_;
@@ -301,9 +297,9 @@
                                            quic::QuicStreamId stream_id) {
   quic::QuicPacketHeader header;
   header.destination_connection_id = connection_id_;
-  header.destination_connection_id_length = GetDestinationConnectionIdLength();
+  header.destination_connection_id_included = HasDestinationConnectionId();
   header.source_connection_id = connection_id_;
-  header.source_connection_id_length = GetSourceConnectionIdLength();
+  header.source_connection_id_included = HasSourceConnectionId();
   header.reset_flag = false;
   header.version_flag = ShouldIncludeVersion(include_version);
   header.long_packet_type = long_header_type_;
@@ -399,9 +395,9 @@
     bool include_stop_sending_if_v99) {
   quic::QuicPacketHeader header;
   header.destination_connection_id = connection_id_;
-  header.destination_connection_id_length = GetDestinationConnectionIdLength();
+  header.destination_connection_id_included = HasDestinationConnectionId();
   header.source_connection_id = connection_id_;
-  header.source_connection_id_length = GetSourceConnectionIdLength();
+  header.source_connection_id_included = HasSourceConnectionId();
   header.reset_flag = false;
   header.version_flag = ShouldIncludeVersion(include_version);
   header.long_packet_type = long_header_type_;
@@ -458,9 +454,9 @@
     const std::string& quic_error_details) {
   quic::QuicPacketHeader header;
   header.destination_connection_id = connection_id_;
-  header.destination_connection_id_length = GetDestinationConnectionIdLength();
+  header.destination_connection_id_included = HasDestinationConnectionId();
   header.source_connection_id = connection_id_;
-  header.source_connection_id_length = GetSourceConnectionIdLength();
+  header.source_connection_id_included = HasSourceConnectionId();
   header.reset_flag = false;
   header.version_flag = ShouldIncludeVersion(include_version);
   header.long_packet_type = long_header_type_;
@@ -523,9 +519,9 @@
     const std::string& quic_error_details) {
   quic::QuicPacketHeader header;
   header.destination_connection_id = connection_id_;
-  header.destination_connection_id_length = GetDestinationConnectionIdLength();
+  header.destination_connection_id_included = HasDestinationConnectionId();
   header.source_connection_id = connection_id_;
-  header.source_connection_id_length = GetSourceConnectionIdLength();
+  header.source_connection_id_included = HasSourceConnectionId();
   header.reset_flag = false;
   header.version_flag = ShouldIncludeVersion(include_version);
   header.long_packet_type = long_header_type_;
@@ -571,9 +567,9 @@
     const std::string& quic_error_details) {
   quic::QuicPacketHeader header;
   header.destination_connection_id = connection_id_;
-  header.destination_connection_id_length = GetDestinationConnectionIdLength();
+  header.destination_connection_id_included = HasDestinationConnectionId();
   header.source_connection_id = connection_id_;
-  header.source_connection_id_length = GetSourceConnectionIdLength();
+  header.source_connection_id_included = HasSourceConnectionId();
   header.reset_flag = false;
   header.version_flag = ShouldIncludeVersion(include_version);
   header.long_packet_type = long_header_type_;
@@ -599,9 +595,9 @@
     std::string reason_phrase) {
   quic::QuicPacketHeader header;
   header.destination_connection_id = connection_id_;
-  header.destination_connection_id_length = GetDestinationConnectionIdLength();
+  header.destination_connection_id_included = HasDestinationConnectionId();
   header.source_connection_id = connection_id_;
-  header.source_connection_id_length = GetSourceConnectionIdLength();
+  header.source_connection_id_included = HasSourceConnectionId();
   header.reset_flag = false;
   header.version_flag = ShouldIncludeVersion(false);
   header.long_packet_type = long_header_type_;
@@ -666,9 +662,9 @@
     quic::QuicTime::Delta ack_delay_time) {
   quic::QuicPacketHeader header;
   header.destination_connection_id = connection_id_;
-  header.destination_connection_id_length = GetDestinationConnectionIdLength();
+  header.destination_connection_id_included = HasDestinationConnectionId();
   header.source_connection_id = connection_id_;
-  header.source_connection_id_length = GetSourceConnectionIdLength();
+  header.source_connection_id_included = HasSourceConnectionId();
   header.reset_flag = false;
   header.version_flag = ShouldIncludeVersion(false);
   header.long_packet_type = long_header_type_;
@@ -1212,9 +1208,9 @@
 void QuicTestPacketMaker::InitializeHeader(uint64_t packet_number,
                                            bool should_include_version) {
   header_.destination_connection_id = connection_id_;
-  header_.destination_connection_id_length = GetDestinationConnectionIdLength();
+  header_.destination_connection_id_included = HasDestinationConnectionId();
   header_.source_connection_id = connection_id_;
-  header_.source_connection_id_length = GetSourceConnectionIdLength();
+  header_.source_connection_id_included = HasSourceConnectionId();
   header_.reset_flag = false;
   header_.version_flag = ShouldIncludeVersion(should_include_version);
   header_.long_packet_type = long_header_type_;
@@ -1346,7 +1342,6 @@
 
 void QuicTestPacketMaker::SetEncryptionLevel(quic::EncryptionLevel level) {
   encryption_level_ = level;
-  if (FLAGS_quic_reloadable_flag_quic_encryption_driven_header_type) {
     switch (level) {
       case quic::ENCRYPTION_NONE:
         long_header_type_ = quic::INITIAL;
@@ -1361,16 +1356,6 @@
         QUIC_BUG << quic::QuicUtils::EncryptionLevelToString(level);
         long_header_type_ = quic::INVALID_PACKET_TYPE;
     }
-  }
-}
-
-void QuicTestPacketMaker::SetLongHeaderType(quic::QuicLongHeaderType type) {
-  if (FLAGS_quic_reloadable_flag_quic_encryption_driven_header_type) {
-    // TODO(fayang): Remove SetLongHeaderType when deprecating
-    // FLAGS_quic_reloadable_flag_quic_encryption_driven_header_type.
-    return;
-  }
-  long_header_type_ = type;
 }
 
 bool QuicTestPacketMaker::ShouldIncludeVersion(bool include_version) const {
@@ -1390,23 +1375,23 @@
   return quic::PACKET_1BYTE_PACKET_NUMBER;
 }
 
-quic::QuicConnectionIdLength
-QuicTestPacketMaker::GetDestinationConnectionIdLength() const {
+quic::QuicConnectionIdIncluded QuicTestPacketMaker::HasDestinationConnectionId()
+    const {
   if (perspective_ == quic::Perspective::IS_SERVER &&
       version_ > quic::QUIC_VERSION_43) {
-    return quic::PACKET_0BYTE_CONNECTION_ID;
+    return quic::CONNECTION_ID_ABSENT;
   }
-  return quic::PACKET_8BYTE_CONNECTION_ID;
+  return quic::CONNECTION_ID_PRESENT;
 }
 
-quic::QuicConnectionIdLength QuicTestPacketMaker::GetSourceConnectionIdLength()
+quic::QuicConnectionIdIncluded QuicTestPacketMaker::HasSourceConnectionId()
     const {
   if (perspective_ == quic::Perspective::IS_SERVER &&
       version_ > quic::QUIC_VERSION_43 &&
       encryption_level_ < quic::ENCRYPTION_FORWARD_SECURE) {
-    return quic::PACKET_8BYTE_CONNECTION_ID;
+    return quic::CONNECTION_ID_PRESENT;
   }
-  return quic::PACKET_0BYTE_CONNECTION_ID;
+  return quic::CONNECTION_ID_ABSENT;
 }
 
 }  // namespace test
diff --git a/net/quic/quic_test_packet_maker.h b/net/quic/quic_test_packet_maker.h
index 95d7b72..fe7bb39 100644
--- a/net/quic/quic_test_packet_maker.h
+++ b/net/quic/quic_test_packet_maker.h
@@ -369,8 +369,6 @@
 
   void SetEncryptionLevel(quic::EncryptionLevel level);
 
-  void SetLongHeaderType(quic::QuicLongHeaderType type);
-
   spdy::SpdyHeaderBlock GetRequestHeaders(const std::string& method,
                                           const std::string& scheme,
                                           const std::string& path);
@@ -407,9 +405,8 @@
 
   quic::QuicPacketNumberLength GetPacketNumberLength() const;
 
-  quic::QuicConnectionIdLength GetDestinationConnectionIdLength() const;
-
-  quic::QuicConnectionIdLength GetSourceConnectionIdLength() const;
+  quic::QuicConnectionIdIncluded HasDestinationConnectionId() const;
+  quic::QuicConnectionIdIncluded HasSourceConnectionId() const;
 
   quic::QuicTransportVersion version_;
   quic::QuicConnectionId connection_id_;
diff --git a/net/socket/client_socket_factory.cc b/net/socket/client_socket_factory.cc
index 1b5f7cf..c43fecc 100644
--- a/net/socket/client_socket_factory.cc
+++ b/net/socket/client_socket_factory.cc
@@ -9,7 +9,6 @@
 #include "base/lazy_instance.h"
 #include "build/build_config.h"
 #include "net/http/http_proxy_client_socket.h"
-#include "net/socket/client_socket_handle.h"
 #include "net/socket/ssl_client_socket_impl.h"
 #include "net/socket/tcp_client_socket.h"
 #include "net/socket/udp_client_socket.h"
@@ -45,21 +44,12 @@
   }
 
   std::unique_ptr<SSLClientSocket> CreateSSLClientSocket(
-      std::unique_ptr<ClientSocketHandle> transport_socket,
-      const HostPortPair& host_and_port,
-      const SSLConfig& ssl_config,
-      const SSLClientSocketContext& context) override {
-    return std::unique_ptr<SSLClientSocket>(new SSLClientSocketImpl(
-        std::move(transport_socket), host_and_port, ssl_config, context));
-  }
-
-  std::unique_ptr<SSLClientSocket> CreateSSLClientSocket(
-      std::unique_ptr<StreamSocket> nested_socket,
+      std::unique_ptr<StreamSocket> stream_socket,
       const HostPortPair& host_and_port,
       const SSLConfig& ssl_config,
       const SSLClientSocketContext& context) override {
     return std::make_unique<SSLClientSocketImpl>(
-        std::move(nested_socket), host_and_port, ssl_config, context);
+        std::move(stream_socket), host_and_port, ssl_config, context);
   }
 
   std::unique_ptr<ProxyClientSocket> CreateProxyClientSocket(
@@ -79,24 +69,6 @@
         http_auth_controller, tunnel, using_spdy, negotiated_protocol,
         proxy_delegate, is_https_proxy, traffic_annotation);
   }
-
-  std::unique_ptr<ProxyClientSocket> CreateProxyClientSocket(
-      std::unique_ptr<ClientSocketHandle> transport_socket,
-      const std::string& user_agent,
-      const HostPortPair& endpoint,
-      const ProxyServer& proxy_server,
-      HttpAuthController* http_auth_controller,
-      bool tunnel,
-      bool using_spdy,
-      NextProto negotiated_protocol,
-      ProxyDelegate* proxy_delegate,
-      bool is_https_proxy,
-      const NetworkTrafficAnnotationTag& traffic_annotation) override {
-    return std::make_unique<HttpProxyClientSocket>(
-        std::move(transport_socket), user_agent, endpoint, proxy_server,
-        http_auth_controller, tunnel, using_spdy, negotiated_protocol,
-        proxy_delegate, is_https_proxy, traffic_annotation);
-  }
 };
 
 static base::LazyInstance<DefaultClientSocketFactory>::Leaky
diff --git a/net/socket/client_socket_factory.h b/net/socket/client_socket_factory.h
index 6ac01e1..16982526 100644
--- a/net/socket/client_socket_factory.h
+++ b/net/socket/client_socket_factory.h
@@ -18,7 +18,6 @@
 namespace net {
 
 class AddressList;
-class ClientSocketHandle;
 class DatagramClientSocket;
 class HostPortPair;
 class NetLog;
@@ -50,39 +49,15 @@
       NetLog* net_log,
       const NetLogSource& source) = 0;
 
-  // It is allowed to pass in a |transport_socket| that is not obtained from a
-  // socket pool. The caller could create a ClientSocketHandle directly and call
-  // set_socket() on it to set a valid StreamSocket instance.
-  //
-  // TODO(mmenke): Remove this method in favor of the one below.
+  // It is allowed to pass in a StreamSocket that is not obtained from a
+  // socket pool. The caller could create a StreamSocket directly.
   virtual std::unique_ptr<SSLClientSocket> CreateSSLClientSocket(
-      std::unique_ptr<ClientSocketHandle> transport_socket,
-      const HostPortPair& host_and_port,
-      const SSLConfig& ssl_config,
-      const SSLClientSocketContext& context) = 0;
-  // Newer version of above function that does not sit on top of another socket
-  // pool.
-  virtual std::unique_ptr<SSLClientSocket> CreateSSLClientSocket(
-      std::unique_ptr<StreamSocket> nested_socket,
+      std::unique_ptr<StreamSocket> stream_socket,
       const HostPortPair& host_and_port,
       const SSLConfig& ssl_config,
       const SSLClientSocketContext& context) = 0;
 
   virtual std::unique_ptr<ProxyClientSocket> CreateProxyClientSocket(
-      std::unique_ptr<ClientSocketHandle> transport_socket,
-      const std::string& user_agent,
-      const HostPortPair& endpoint,
-      const ProxyServer& proxy_server,
-      HttpAuthController* http_auth_controller,
-      bool tunnel,
-      bool using_spdy,
-      NextProto negotiated_protocol,
-      ProxyDelegate* proxy_delegate,
-      bool is_https_proxy,
-      const NetworkTrafficAnnotationTag& traffic_annotation) = 0;
-  // Newer version of the above method.
-  // TODO(mmenke): Remove above method in favor of this one.
-  virtual std::unique_ptr<ProxyClientSocket> CreateProxyClientSocket(
       std::unique_ptr<StreamSocket> stream_socket,
       const std::string& user_agent,
       const HostPortPair& endpoint,
diff --git a/net/socket/client_socket_pool.h b/net/socket/client_socket_pool.h
index e182320..b824628 100644
--- a/net/socket/client_socket_pool.h
+++ b/net/socket/client_socket_pool.h
@@ -202,8 +202,7 @@
   // ClientSocketPools will be included.
   virtual std::unique_ptr<base::DictionaryValue> GetInfoAsValue(
       const std::string& name,
-      const std::string& type,
-      bool include_nested_pools) const = 0;
+      const std::string& type) const = 0;
 
   // Returns the maximum amount of time to wait before retrying a connect.
   static const int kMaxConnectRetryIntervalMs = 250;
diff --git a/net/socket/client_socket_pool_base.cc b/net/socket/client_socket_pool_base.cc
index e235a12..ad5e522 100644
--- a/net/socket/client_socket_pool_base.cc
+++ b/net/socket/client_socket_pool_base.cc
@@ -86,7 +86,6 @@
 }
 
 ClientSocketPoolBaseHelper::ClientSocketPoolBaseHelper(
-    HigherLayeredPool* pool,
     int max_sockets,
     int max_sockets_per_group,
     base::TimeDelta unused_idle_socket_timeout,
@@ -102,7 +101,6 @@
       connect_job_factory_(connect_job_factory),
       connect_backup_jobs_enabled_(false),
       pool_generation_number_(0),
-      pool_(pool),
       weak_factory_(this) {
   DCHECK_LE(0, max_sockets_per_group);
   DCHECK_LE(max_sockets_per_group, max_sockets);
@@ -122,11 +120,6 @@
   CHECK(higher_pools_.empty());
 
   NetworkChangeNotifier::RemoveIPAddressObserver(this);
-
-  // Remove from lower layer pools.
-  for (auto it = lower_pools_.begin(); it != lower_pools_.end(); ++it) {
-    (*it)->RemoveHigherLayeredPool(pool_);
-  }
 }
 
 ClientSocketPoolBaseHelper::CallbackResultPair::CallbackResultPair()
@@ -148,12 +141,6 @@
 ClientSocketPoolBaseHelper::CallbackResultPair::~CallbackResultPair() = default;
 
 bool ClientSocketPoolBaseHelper::IsStalled() const {
-  // If a lower layer pool is stalled, consider |this| stalled as well.
-  for (auto it = lower_pools_.begin(); it != lower_pools_.end(); ++it) {
-    if ((*it)->IsStalled())
-      return true;
-  }
-
   // If fewer than |max_sockets_| are in use, then clearly |this| is not
   // stalled.
   if ((handed_out_socket_count_ + connecting_socket_count_) < max_sockets_)
@@ -172,14 +159,6 @@
   return false;
 }
 
-void ClientSocketPoolBaseHelper::AddLowerLayeredPool(
-    LowerLayeredPool* lower_pool) {
-  DCHECK(pool_);
-  CHECK(!base::ContainsKey(lower_pools_, lower_pool));
-  lower_pools_.insert(lower_pool);
-  lower_pool->AddHigherLayeredPool(pool_);
-}
-
 void ClientSocketPoolBaseHelper::AddHigherLayeredPool(
     HigherLayeredPool* higher_pool) {
   CHECK(higher_pool);
@@ -799,16 +778,8 @@
     // If we have idle sockets, see if we can give one to the top-stalled group.
     std::string top_group_name;
     Group* top_group = NULL;
-    if (!FindTopStalledGroup(&top_group, &top_group_name)) {
-      // There may still be a stalled group in a lower level pool.
-      for (auto it = lower_pools_.begin(); it != lower_pools_.end(); ++it) {
-        if ((*it)->IsStalled()) {
-          CloseOneIdleSocket();
-          break;
-        }
-      }
+    if (!FindTopStalledGroup(&top_group, &top_group_name))
       return;
-    }
 
     if (ReachedMaxSocketsLimit()) {
       if (idle_socket_count() > 0) {
diff --git a/net/socket/client_socket_pool_base.h b/net/socket/client_socket_pool_base.h
index ca59a42..86b13e0 100644
--- a/net/socket/client_socket_pool_base.h
+++ b/net/socket/client_socket_pool_base.h
@@ -164,7 +164,6 @@
   };
 
   ClientSocketPoolBaseHelper(
-      HigherLayeredPool* pool,
       int max_sockets,
       int max_sockets_per_group,
       base::TimeDelta unused_idle_socket_timeout,
@@ -734,19 +733,10 @@
   // to the pool, we can make sure that they are discarded rather than reused.
   int pool_generation_number_;
 
-  // Used to add |this| as a higher layer pool on top of lower layer pools.  May
-  // be NULL if no lower layer pools will be added.
-  HigherLayeredPool* pool_;
-
   // Pools that create connections through |this|.  |this| will try to close
   // their idle sockets when it stalls.  Must be empty on destruction.
   std::set<HigherLayeredPool*> higher_pools_;
 
-  // Pools that this goes through.  Typically there's only one, but not always.
-  // |this| will check if they're stalled when it has a new idle socket.  |this|
-  // will remove itself from all lower layered pools on destruction.
-  std::set<LowerLayeredPool*> lower_pools_;
-
   base::WeakPtrFactory<ClientSocketPoolBaseHelper> weak_factory_;
 
   DISALLOW_COPY_AND_ASSIGN(ClientSocketPoolBaseHelper);
@@ -804,14 +794,12 @@
   // long to leave an unused idle socket open before closing it.
   // |used_idle_socket_timeout| specifies how long to leave a previously used
   // idle socket open before closing it.
-  ClientSocketPoolBase(HigherLayeredPool* self,
-                       int max_sockets,
+  ClientSocketPoolBase(int max_sockets,
                        int max_sockets_per_group,
                        base::TimeDelta unused_idle_socket_timeout,
                        base::TimeDelta used_idle_socket_timeout,
                        ConnectJobFactory* connect_job_factory)
-      : helper_(self,
-                max_sockets,
+      : helper_(max_sockets,
                 max_sockets_per_group,
                 unused_idle_socket_timeout,
                 used_idle_socket_timeout,
@@ -820,10 +808,6 @@
   virtual ~ClientSocketPoolBase() {}
 
   // These member functions simply forward to ClientSocketPoolBaseHelper.
-  void AddLowerLayeredPool(LowerLayeredPool* lower_pool) {
-    helper_.AddLowerLayeredPool(lower_pool);
-  }
-
   void AddHigherLayeredPool(HigherLayeredPool* higher_pool) {
     helper_.AddHigherLayeredPool(higher_pool);
   }
@@ -954,10 +938,6 @@
 
   bool CloseOneIdleSocket() { return helper_.CloseOneIdleSocket(); }
 
-  bool CloseOneIdleConnectionInHigherLayeredPool() {
-    return helper_.CloseOneIdleConnectionInHigherLayeredPool();
-  }
-
  private:
   // This adaptor class exists to bridge the
   // internal::ClientSocketPoolBaseHelper::ConnectJobFactory and
diff --git a/net/socket/client_socket_pool_base_unittest.cc b/net/socket/client_socket_pool_base_unittest.cc
index 16e52842..07af0295 100644
--- a/net/socket/client_socket_pool_base_unittest.cc
+++ b/net/socket/client_socket_pool_base_unittest.cc
@@ -235,7 +235,7 @@
   }
 
   std::unique_ptr<SSLClientSocket> CreateSSLClientSocket(
-      std::unique_ptr<ClientSocketHandle> transport_socket,
+      std::unique_ptr<StreamSocket> stream_socket,
       const HostPortPair& host_and_port,
       const SSLConfig& ssl_config,
       const SSLClientSocketContext& context) override {
@@ -243,31 +243,6 @@
     return std::unique_ptr<SSLClientSocket>();
   }
 
-  std::unique_ptr<SSLClientSocket> CreateSSLClientSocket(
-      std::unique_ptr<StreamSocket> nested_socket,
-      const HostPortPair& host_and_port,
-      const SSLConfig& ssl_config,
-      const SSLClientSocketContext& context) override {
-    NOTIMPLEMENTED();
-    return std::unique_ptr<SSLClientSocket>();
-  }
-
-  std::unique_ptr<ProxyClientSocket> CreateProxyClientSocket(
-      std::unique_ptr<ClientSocketHandle> transport_socket,
-      const std::string& user_agent,
-      const HostPortPair& endpoint,
-      const ProxyServer& proxy_server,
-      HttpAuthController* http_auth_controller,
-      bool tunnel,
-      bool using_spdy,
-      NextProto negotiated_protocol,
-      ProxyDelegate* proxy_delegate,
-      bool is_https_proxy,
-      const NetworkTrafficAnnotationTag& traffic_annotation) override {
-    NOTIMPLEMENTED();
-    return nullptr;
-  }
-
   std::unique_ptr<ProxyClientSocket> CreateProxyClientSocket(
       std::unique_ptr<StreamSocket> stream_socket,
       const std::string& user_agent,
@@ -616,8 +591,7 @@
       base::TimeDelta unused_idle_socket_timeout,
       base::TimeDelta used_idle_socket_timeout,
       TestClientSocketPoolBase::ConnectJobFactory* connect_job_factory)
-      : base_(NULL,
-              max_sockets,
+      : base_(max_sockets,
               max_sockets_per_group,
               unused_idle_socket_timeout,
               used_idle_socket_timeout,
@@ -699,8 +673,7 @@
 
   std::unique_ptr<base::DictionaryValue> GetInfoAsValue(
       const std::string& name,
-      const std::string& type,
-      bool include_nested_pools) const override {
+      const std::string& type) const override {
     return base_.GetInfoAsValue(name, type);
   }
 
@@ -737,10 +710,6 @@
 
   void EnableConnectBackupJobs() { base_.EnableConnectBackupJobs(); }
 
-  bool CloseOneIdleConnectionInHigherLayeredPool() {
-    return base_.CloseOneIdleConnectionInHigherLayeredPool();
-  }
-
  private:
   TestClientSocketPoolBase base_;
 
@@ -4383,29 +4352,6 @@
   bool can_release_connection_;
 };
 
-TEST_F(ClientSocketPoolBaseTest, FailToCloseIdleSocketsNotHeldByLayeredPool) {
-  CreatePool(kDefaultMaxSockets, kDefaultMaxSocketsPerGroup);
-  connect_job_factory_->set_job_type(TestConnectJob::kMockJob);
-
-  MockLayeredPool mock_layered_pool(pool_.get(), "foo");
-  EXPECT_THAT(mock_layered_pool.RequestSocket(pool_.get()), IsOk());
-  EXPECT_CALL(mock_layered_pool, CloseOneIdleConnection())
-      .WillOnce(Return(false));
-  EXPECT_FALSE(pool_->CloseOneIdleConnectionInHigherLayeredPool());
-}
-
-TEST_F(ClientSocketPoolBaseTest, ForciblyCloseIdleSocketsHeldByLayeredPool) {
-  CreatePool(kDefaultMaxSockets, kDefaultMaxSocketsPerGroup);
-  connect_job_factory_->set_job_type(TestConnectJob::kMockJob);
-
-  MockLayeredPool mock_layered_pool(pool_.get(), "foo");
-  EXPECT_THAT(mock_layered_pool.RequestSocket(pool_.get()), IsOk());
-  EXPECT_CALL(mock_layered_pool, CloseOneIdleConnection())
-      .WillOnce(Invoke(&mock_layered_pool,
-                       &MockLayeredPool::ReleaseOneConnection));
-  EXPECT_TRUE(pool_->CloseOneIdleConnectionInHigherLayeredPool());
-}
-
 // Tests the basic case of closing an idle socket in a higher layered pool when
 // a new request is issued and the lower layer pool is stalled.
 TEST_F(ClientSocketPoolBaseTest, CloseIdleSocketsHeldByLayeredPoolWhenNeeded) {
@@ -4428,6 +4374,31 @@
   EXPECT_THAT(callback.WaitForResult(), IsOk());
 }
 
+// Tests the case that trying to close an idle socket in a higher layered pool
+// fails.
+TEST_F(ClientSocketPoolBaseTest,
+       CloseIdleSocketsHeldByLayeredPoolWhenNeededFails) {
+  CreatePool(1, 1);
+  connect_job_factory_->set_job_type(TestConnectJob::kMockJob);
+
+  MockLayeredPool mock_layered_pool(pool_.get(), "foo");
+  mock_layered_pool.set_can_release_connection(false);
+  EXPECT_THAT(mock_layered_pool.RequestSocket(pool_.get()), IsOk());
+  EXPECT_CALL(mock_layered_pool, CloseOneIdleConnection())
+      .WillOnce(Invoke(&mock_layered_pool,
+                       &MockLayeredPool::ReleaseOneConnection));
+  ClientSocketHandle handle;
+  TestCompletionCallback callback;
+  EXPECT_EQ(
+      ERR_IO_PENDING,
+      handle.Init("a", params_, DEFAULT_PRIORITY, SocketTag(),
+                  ClientSocketPool::RespectLimits::ENABLED, callback.callback(),
+                  ClientSocketPool::ProxyAuthCallback(), pool_.get(),
+                  NetLogWithSource()));
+  base::RunLoop().RunUntilIdle();
+  EXPECT_FALSE(callback.have_result());
+}
+
 // Same as above, but the idle socket is in the same group as the stalled
 // socket, and closes the only other request in its group when closing requests
 // in higher layered pools.  This generally shouldn't happen, but it may be
diff --git a/net/socket/client_socket_pool_manager.cc b/net/socket/client_socket_pool_manager.cc
index a2c1ae88..b336789 100644
--- a/net/socket/client_socket_pool_manager.cc
+++ b/net/socket/client_socket_pool_manager.cc
@@ -10,7 +10,6 @@
 #include "base/stl_util.h"
 #include "base/strings/stringprintf.h"
 #include "net/base/load_flags.h"
-#include "net/base/proxy_server.h"
 #include "net/http/http_proxy_connect_job.h"
 #include "net/http/http_request_info.h"
 #include "net/http/http_stream_factory.h"
@@ -69,12 +68,12 @@
 
 // The meat of the implementation for the InitSocketHandleForHttpRequest,
 // InitSocketHandleForRawConnect and PreconnectSocketsForHttpRequest methods.
-int InitSocketPoolHelper(
+scoped_refptr<TransportClientSocketPool::SocketParams>
+CreateSocketParamsAndGetGroupName(
     ClientSocketPoolManager::SocketGroupType group_type,
     const HostPortPair& endpoint,
     const HttpRequestHeaders& request_extra_headers,
     int request_load_flags,
-    RequestPriority request_priority,
     HttpNetworkSession* session,
     const ProxyInfo& proxy_info,
     quic::QuicTransportVersion quic_version,
@@ -82,14 +81,8 @@
     const SSLConfig& ssl_config_for_proxy,
     bool force_tunnel,
     PrivacyMode privacy_mode,
-    const SocketTag& socket_tag,
-    const NetLogWithSource& net_log,
-    int num_preconnect_streams,
-    ClientSocketHandle* socket_handle,
-    HttpNetworkSession::SocketPoolType socket_pool_type,
     const OnHostResolutionCallback& resolution_callback,
-    CompletionOnceCallback callback,
-    const ClientSocketPool::ProxyAuthCallback& proxy_auth_callback) {
+    std::string* connection_group) {
   scoped_refptr<HttpProxySocketParams> http_proxy_params;
   scoped_refptr<SOCKSSocketParams> socks_params;
 
@@ -108,27 +101,22 @@
 
   // Build the string used to uniquely identify connections of this type.
   // Determine the host and port to connect to.
-  std::string connection_group = origin_host_port.ToString();
-  DCHECK(!connection_group.empty());
+  *connection_group = origin_host_port.ToString();
+  DCHECK(!connection_group->empty());
   if (group_type == ClientSocketPoolManager::FTP_GROUP) {
     // Combining FTP with forced SPDY over SSL would be a "path to madness".
     // Make sure we never do that.
     DCHECK(!using_ssl);
-    connection_group = "ftp/" + connection_group;
+    *connection_group = "ftp/" + *connection_group;
   }
   if (using_ssl) {
     std::string prefix = "ssl/";
     if (ssl_config_for_origin.version_interference_probe) {
       prefix += "version-interference-probe/";
     }
-    connection_group = prefix + connection_group;
+    *connection_group = prefix + *connection_group;
   }
 
-  ClientSocketPool::RespectLimits respect_limits =
-      ClientSocketPool::RespectLimits::ENABLED;
-  if ((request_load_flags & LOAD_IGNORE_LIMITS) != 0)
-    respect_limits = ClientSocketPool::RespectLimits::DISABLED;
-
   if (!proxy_info.is_direct()) {
     ProxyServer proxy_server = proxy_info.proxy_server();
     scoped_refptr<TransportSocketParams> proxy_tcp_params(
@@ -139,9 +127,9 @@
       // TODO(mmenke):  Would it be better to split these into two different
       //     socket pools?  And maybe socks4/socks5 as well?
       if (proxy_info.is_http()) {
-        connection_group = "http_proxy/" + connection_group;
+        *connection_group = "http_proxy/" + *connection_group;
       } else {
-        connection_group = "https_proxy/" + connection_group;
+        *connection_group = "https_proxy/" + *connection_group;
       }
 
       std::string user_agent;
@@ -177,8 +165,8 @@
         socks_version = '5';
       else
         socks_version = '4';
-      connection_group = base::StringPrintf(
-          "socks%c/%s", socks_version, connection_group.c_str());
+      *connection_group = base::StringPrintf("socks%c/%s", socks_version,
+                                             connection_group->c_str());
 
       socks_params = new SOCKSSocketParams(
           proxy_tcp_params, socks_version == '5', origin_host_port,
@@ -188,108 +176,85 @@
 
   // Change group name if privacy mode is enabled.
   if (privacy_mode == PRIVACY_MODE_ENABLED)
-    connection_group = "pm/" + connection_group;
+    *connection_group = "pm/" + *connection_group;
 
   // Deal with SSL - which layers on top of any given proxy.
   if (using_ssl) {
     scoped_refptr<TransportSocketParams> ssl_tcp_params;
     if (proxy_info.is_direct()) {
-      ssl_tcp_params = new TransportSocketParams(
+      ssl_tcp_params = base::MakeRefCounted<TransportSocketParams>(
           origin_host_port, disable_resolver_cache, resolution_callback);
     }
-    scoped_refptr<SSLSocketParams> ssl_params = new SSLSocketParams(
-        ssl_tcp_params, socks_params, http_proxy_params, origin_host_port,
-        ssl_config_for_origin, privacy_mode);
-    TransportClientSocketPool* ssl_pool = nullptr;
-    if (proxy_info.is_direct()) {
-      ssl_pool = session->GetTransportSocketPool(socket_pool_type);
-    } else if (proxy_info.is_socks()) {
-      ssl_pool = session->GetSocketPoolForSOCKSProxy(socket_pool_type,
-                                                     proxy_info.proxy_server());
-    } else {
-      ssl_pool = session->GetSocketPoolForHTTPLikeProxy(
-          socket_pool_type, proxy_info.proxy_server());
-    }
-
-    if (num_preconnect_streams) {
-      RequestSocketsForPool(
-          ssl_pool, connection_group,
-          TransportClientSocketPool::SocketParams::CreateFromSSLSocketParams(
-              ssl_params),
-          num_preconnect_streams, net_log);
-      return OK;
-    }
-
-    return socket_handle->Init(
-        connection_group,
-        TransportClientSocketPool::SocketParams::CreateFromSSLSocketParams(
-            ssl_params),
-        request_priority, socket_tag, respect_limits, std::move(callback),
-        proxy_auth_callback, ssl_pool, net_log);
+    scoped_refptr<SSLSocketParams> ssl_params =
+        base::MakeRefCounted<SSLSocketParams>(
+            ssl_tcp_params, socks_params, http_proxy_params, origin_host_port,
+            ssl_config_for_origin, privacy_mode);
+    return TransportClientSocketPool::SocketParams::CreateFromSSLSocketParams(
+        std::move(ssl_params));
   }
 
-  // Finally, get the connection started.
-
   if (proxy_info.is_http() || proxy_info.is_https()) {
-    TransportClientSocketPool* pool = session->GetSocketPoolForHTTPLikeProxy(
-        socket_pool_type, proxy_info.proxy_server());
-    if (num_preconnect_streams) {
-      RequestSocketsForPool(
-          pool, connection_group,
-          TransportClientSocketPool::SocketParams::
-              CreateFromHttpProxySocketParams(http_proxy_params),
-          num_preconnect_streams, net_log);
-      return OK;
-    }
-
-    return socket_handle->Init(
-        connection_group,
-        TransportClientSocketPool::SocketParams::
-            CreateFromHttpProxySocketParams(http_proxy_params),
-        request_priority, socket_tag, respect_limits, std::move(callback),
-        proxy_auth_callback, pool, net_log);
+    return TransportClientSocketPool::SocketParams::
+        CreateFromHttpProxySocketParams(std::move(http_proxy_params));
   }
 
   if (proxy_info.is_socks()) {
-    TransportClientSocketPool* pool = session->GetSocketPoolForSOCKSProxy(
-        socket_pool_type, proxy_info.proxy_server());
-    if (num_preconnect_streams) {
-      RequestSocketsForPool(
-          pool, connection_group,
-          TransportClientSocketPool::SocketParams::CreateFromSOCKSSocketParams(
-              socks_params),
-          num_preconnect_streams, net_log);
-      return OK;
-    }
-
-    return socket_handle->Init(
-        connection_group,
-        TransportClientSocketPool::SocketParams::CreateFromSOCKSSocketParams(
-            socks_params),
-        request_priority, socket_tag, respect_limits, std::move(callback),
-        proxy_auth_callback, pool, net_log);
+    return TransportClientSocketPool::SocketParams::CreateFromSOCKSSocketParams(
+        socks_params);
   }
 
   DCHECK(proxy_info.is_direct());
   scoped_refptr<TransportSocketParams> tcp_params = new TransportSocketParams(
       origin_host_port, disable_resolver_cache, resolution_callback);
+  return TransportClientSocketPool::SocketParams::
+      CreateFromTransportSocketParams(std::move(tcp_params));
+}
+
+int InitSocketPoolHelper(
+    ClientSocketPoolManager::SocketGroupType group_type,
+    const HostPortPair& endpoint,
+    const HttpRequestHeaders& request_extra_headers,
+    int request_load_flags,
+    RequestPriority request_priority,
+    HttpNetworkSession* session,
+    const ProxyInfo& proxy_info,
+    quic::QuicTransportVersion quic_version,
+    const SSLConfig& ssl_config_for_origin,
+    const SSLConfig& ssl_config_for_proxy,
+    bool force_tunnel,
+    PrivacyMode privacy_mode,
+    const SocketTag& socket_tag,
+    const NetLogWithSource& net_log,
+    int num_preconnect_streams,
+    ClientSocketHandle* socket_handle,
+    HttpNetworkSession::SocketPoolType socket_pool_type,
+    const OnHostResolutionCallback& resolution_callback,
+    CompletionOnceCallback callback,
+    const ClientSocketPool::ProxyAuthCallback& proxy_auth_callback) {
+  std::string connection_group;
+  scoped_refptr<TransportClientSocketPool::SocketParams> socket_params =
+      CreateSocketParamsAndGetGroupName(
+          group_type, endpoint, request_extra_headers, request_load_flags,
+          session, proxy_info, quic_version, ssl_config_for_origin,
+          ssl_config_for_proxy, force_tunnel, privacy_mode, resolution_callback,
+          &connection_group);
+
   TransportClientSocketPool* pool =
-      session->GetTransportSocketPool(socket_pool_type);
+      session->GetSocketPool(socket_pool_type, proxy_info.proxy_server());
+  ClientSocketPool::RespectLimits respect_limits =
+      ClientSocketPool::RespectLimits::ENABLED;
+  if ((request_load_flags & LOAD_IGNORE_LIMITS) != 0)
+    respect_limits = ClientSocketPool::RespectLimits::DISABLED;
+
   if (num_preconnect_streams) {
-    RequestSocketsForPool(
-        pool, connection_group,
-        TransportClientSocketPool::SocketParams::
-            CreateFromTransportSocketParams(std::move(tcp_params)),
-        num_preconnect_streams, net_log);
+    RequestSocketsForPool(pool, connection_group, std::move(socket_params),
+                          num_preconnect_streams, net_log);
     return OK;
   }
 
   return socket_handle->Init(
-      connection_group,
-      TransportClientSocketPool::SocketParams::CreateFromTransportSocketParams(
-          std::move(tcp_params)),
-      request_priority, socket_tag, respect_limits, std::move(callback),
-      proxy_auth_callback, pool, net_log);
+      connection_group, std::move(socket_params), request_priority, socket_tag,
+      respect_limits, std::move(callback), proxy_auth_callback, pool, net_log);
 }
 
 }  // namespace
diff --git a/net/socket/client_socket_pool_manager.h b/net/socket/client_socket_pool_manager.h
index ad411de..cb683d3 100644
--- a/net/socket/client_socket_pool_manager.h
+++ b/net/socket/client_socket_pool_manager.h
@@ -32,7 +32,6 @@
 class ClientSocketHandle;
 class HostPortPair;
 class HttpNetworkSession;
-class HttpProxyClientSocketPool;
 class HttpRequestHeaders;
 class NetLogWithSource;
 class ProxyInfo;
@@ -84,18 +83,12 @@
 
   virtual void FlushSocketPoolsWithError(int error) = 0;
   virtual void CloseIdleSockets() = 0;
-  // Returns the socket pool for direct HTTP and SSL connections.
-  virtual TransportClientSocketPool* GetTransportSocketPool() = 0;
-  // Returns the socket pool used for both SOCKS and SSL over SOCKS.
-  // TODO(https://crbug.com/929714): Merge this with
-  // GetSocketPoolForHTTPLikeProxy(), once GetSocketPoolForSSLWithProxy() and
-  // GetSocketPoolForHTTPLikeProxy() have been merged.
-  virtual TransportClientSocketPool* GetSocketPoolForSOCKSProxy(
-      const ProxyServer& socks_proxy) = 0;
-  // Returns the HttpProxyClientSocketPool for a ProxyServer that uses an
-  // "HTTP-like" scheme, as defined by ProxyServer::is_http_like().
-  virtual TransportClientSocketPool* GetSocketPoolForHTTPLikeProxy(
-      const ProxyServer& http_proxy) = 0;
+
+  // Returns the socket pool for the specified ProxyServer (Which may be
+  // ProxyServer::Direct()).
+  virtual TransportClientSocketPool* GetSocketPool(
+      const ProxyServer& proxy_server) = 0;
+
   // Creates a Value summary of the state of the socket pools.
   virtual std::unique_ptr<base::Value> SocketPoolInfoToValue() const = 0;
 
diff --git a/net/socket/client_socket_pool_manager_impl.cc b/net/socket/client_socket_pool_manager_impl.cc
index 02fc4b82..65ff148 100644
--- a/net/socket/client_socket_pool_manager_impl.cc
+++ b/net/socket/client_socket_pool_manager_impl.cc
@@ -22,22 +22,6 @@
 
 class SocketPerformanceWatcherFactory;
 
-namespace {
-
-// Appends information about all |socket_pools| to the end of |list|.
-template <class MapType>
-void AddSocketPoolsToList(base::ListValue* list,
-                          const MapType& socket_pools,
-                          const std::string& type,
-                          bool include_nested_pools) {
-  for (auto it = socket_pools.begin(); it != socket_pools.end(); it++) {
-    list->Append(it->second->GetInfoAsValue(it->first.ToURI(), type,
-                                            include_nested_pools));
-  }
-}
-
-}  // namespace
-
 ClientSocketPoolManagerImpl::ClientSocketPoolManagerImpl(
     NetLog* net_log,
     ClientSocketFactory* socket_factory,
@@ -69,31 +53,9 @@
       ssl_client_session_cache_privacy_mode_(
           ssl_client_session_cache_privacy_mode),
       ssl_config_service_(ssl_config_service),
+      websocket_endpoint_lock_manager_(websocket_endpoint_lock_manager),
       proxy_delegate_(proxy_delegate),
-      pool_type_(pool_type),
-      transport_socket_pool_(
-          pool_type == HttpNetworkSession::WEBSOCKET_SOCKET_POOL
-              ? std::make_unique<WebSocketTransportClientSocketPool>(
-                    max_sockets_per_pool(pool_type),
-                    max_sockets_per_group(pool_type),
-                    unused_idle_socket_timeout(pool_type),
-                    socket_factory_,
-                    host_resolver,
-                    proxy_delegate,
-                    cert_verifier,
-                    channel_id_service,
-                    transport_security_state,
-                    cert_transparency_verifier,
-                    ct_policy_enforcer,
-                    ssl_client_session_cache,
-                    ssl_client_session_cache_privacy_mode,
-                    ssl_config_service,
-                    network_quality_estimator,
-                    websocket_endpoint_lock_manager,
-                    net_log)
-              : CreateTransportSocketPool(
-                    ProxyServer::Direct(),
-                    true /* use_socket_performance_watcher_factory */)) {
+      pool_type_(pool_type) {
   CertDatabase::GetInstance()->AddObserver(this);
 }
 
@@ -103,82 +65,81 @@
 }
 
 void ClientSocketPoolManagerImpl::FlushSocketPoolsWithError(int error) {
-  for (const auto& it : http_proxy_socket_pools_) {
+  for (const auto& it : socket_pools_) {
     it.second->FlushWithError(error);
   }
-
-  for (const auto& it : proxy_socket_pools_) {
-    it.second->FlushWithError(error);
-  }
-
-  transport_socket_pool_->FlushWithError(error);
 }
 
 void ClientSocketPoolManagerImpl::CloseIdleSockets() {
-  for (const auto& it : http_proxy_socket_pools_) {
+  for (const auto& it : socket_pools_) {
     it.second->CloseIdleSockets();
   }
-
-  for (const auto& it : proxy_socket_pools_) {
-    it.second->CloseIdleSockets();
-  }
-
-  transport_socket_pool_->CloseIdleSockets();
 }
 
-TransportClientSocketPool*
-ClientSocketPoolManagerImpl::GetTransportSocketPool() {
-  return transport_socket_pool_.get();
-}
-
-TransportClientSocketPool*
-ClientSocketPoolManagerImpl::GetSocketPoolForSOCKSProxy(
+TransportClientSocketPool* ClientSocketPoolManagerImpl::GetSocketPool(
     const ProxyServer& proxy_server) {
-  DCHECK(proxy_server.is_socks());
-
-  TransportSocketPoolMap::const_iterator it =
-      proxy_socket_pools_.find(proxy_server);
-  if (it != proxy_socket_pools_.end())
+  TransportSocketPoolMap::const_iterator it = socket_pools_.find(proxy_server);
+  if (it != socket_pools_.end())
     return it->second.get();
 
-  std::pair<TransportSocketPoolMap::iterator, bool> ret =
-      proxy_socket_pools_.insert(std::make_pair(
-          proxy_server,
-          CreateTransportSocketPool(
-              proxy_server,
-              false /* use_socket_performance_watcher_factory */)));
+  int sockets_per_proxy_server;
+  int sockets_per_group;
+  if (proxy_server.is_direct()) {
+    sockets_per_proxy_server = max_sockets_per_pool(pool_type_);
+    sockets_per_group = max_sockets_per_group(pool_type_);
+  } else {
+    sockets_per_proxy_server = max_sockets_per_proxy_server(pool_type_);
+    sockets_per_group =
+        std::min(sockets_per_proxy_server, max_sockets_per_group(pool_type_));
+  }
 
-  return ret.first->second.get();
-}
+  std::unique_ptr<TransportClientSocketPool> new_pool;
 
-TransportClientSocketPool*
-ClientSocketPoolManagerImpl::GetSocketPoolForHTTPLikeProxy(
-    const ProxyServer& http_proxy) {
-  DCHECK(http_proxy.is_http_like());
-
-  TransportSocketPoolMap::const_iterator it =
-      http_proxy_socket_pools_.find(http_proxy);
-  if (it != http_proxy_socket_pools_.end())
-    return it->second.get();
+  // Use specialized WebSockets pool for WebSockets when no proxy is in use.
+  if (pool_type_ == HttpNetworkSession::WEBSOCKET_SOCKET_POOL &&
+      proxy_server.is_direct()) {
+    new_pool = std::make_unique<WebSocketTransportClientSocketPool>(
+        sockets_per_proxy_server, sockets_per_group,
+        unused_idle_socket_timeout(pool_type_), socket_factory_, host_resolver_,
+        proxy_delegate_, cert_verifier_, channel_id_service_,
+        transport_security_state_, cert_transparency_verifier_,
+        ct_policy_enforcer_, ssl_client_session_cache_,
+        ssl_client_session_cache_privacy_mode_, ssl_config_service_,
+        network_quality_estimator_, websocket_endpoint_lock_manager_, net_log_);
+  } else {
+    // TODO(mmenke): Can the SOCKS check be removed?
+    new_pool = std::make_unique<TransportClientSocketPool>(
+        sockets_per_proxy_server, sockets_per_group,
+        unused_idle_socket_timeout(pool_type_), socket_factory_, host_resolver_,
+        proxy_delegate_, cert_verifier_, channel_id_service_,
+        transport_security_state_, cert_transparency_verifier_,
+        ct_policy_enforcer_, ssl_client_session_cache_,
+        ssl_client_session_cache_privacy_mode_, ssl_config_service_,
+        proxy_server.is_socks() ? nullptr : socket_performance_watcher_factory_,
+        network_quality_estimator_, net_log_);
+  }
 
   std::pair<TransportSocketPoolMap::iterator, bool> ret =
-      http_proxy_socket_pools_.insert(std::make_pair(
-          http_proxy,
-          CreateTransportSocketPool(
-              http_proxy, true /* use_socket_performance_watcher_factory */)));
+      socket_pools_.insert(std::make_pair(proxy_server, std::move(new_pool)));
   return ret.first->second.get();
 }
 
 std::unique_ptr<base::Value>
 ClientSocketPoolManagerImpl::SocketPoolInfoToValue() const {
   std::unique_ptr<base::ListValue> list(new base::ListValue());
-  list->Append(transport_socket_pool_->GetInfoAsValue("transport_socket_pool",
-                                                "transport_socket_pool",
-                                                false));
-  AddSocketPoolsToList(list.get(), http_proxy_socket_pools_,
-                       "http_proxy_socket_pool", true);
-  AddSocketPoolsToList(list.get(), proxy_socket_pools_, "proxy_socket_pools",
-                       true);
+  for (const auto& socket_pool : socket_pools_) {
+    // TODO(menke): Is this really needed?
+    const char* type;
+    if (socket_pool.first.is_direct()) {
+      type = "transport_socket_pool";
+    } else if (socket_pool.first.is_socks()) {
+      type = "socks_socket_pool";
+    } else {
+      type = "http_proxy_socket_pool";
+    }
+    list->Append(
+        socket_pool.second->GetInfoAsValue(socket_pool.first.ToURI(), type));
+  }
 
   return std::move(list);
 }
@@ -190,35 +151,11 @@
 void ClientSocketPoolManagerImpl::DumpMemoryStats(
     base::trace_event::ProcessMemoryDump* pmd,
     const std::string& parent_dump_absolute_name) const {
-  return transport_socket_pool_->DumpMemoryStats(pmd,
-                                                 parent_dump_absolute_name);
-}
-
-std::unique_ptr<TransportClientSocketPool>
-ClientSocketPoolManagerImpl::CreateTransportSocketPool(
-    const ProxyServer& proxy_server,
-    bool use_socket_performance_watcher_factory) {
-  int sockets_per_proxy_server;
-  int sockets_per_group;
-  if (proxy_server.is_direct()) {
-    sockets_per_proxy_server = max_sockets_per_pool(pool_type_);
-    sockets_per_group = max_sockets_per_group(pool_type_);
-  } else {
-    sockets_per_proxy_server = max_sockets_per_proxy_server(pool_type_);
-    sockets_per_group =
-        std::min(sockets_per_proxy_server, max_sockets_per_group(pool_type_));
-  }
-  return std::make_unique<TransportClientSocketPool>(
-      sockets_per_proxy_server, sockets_per_group,
-      unused_idle_socket_timeout(pool_type_), socket_factory_, host_resolver_,
-      proxy_delegate_, cert_verifier_, channel_id_service_,
-      transport_security_state_, cert_transparency_verifier_,
-      ct_policy_enforcer_, ssl_client_session_cache_,
-      ssl_client_session_cache_privacy_mode_, ssl_config_service_,
-      use_socket_performance_watcher_factory
-          ? socket_performance_watcher_factory_
-          : nullptr,
-      network_quality_estimator_, net_log_);
+  TransportSocketPoolMap::const_iterator socket_pool =
+      socket_pools_.find(ProxyServer::Direct());
+  if (socket_pool == socket_pools_.end())
+    return;
+  socket_pool->second->DumpMemoryStats(pmd, parent_dump_absolute_name);
 }
 
 }  // namespace net
diff --git a/net/socket/client_socket_pool_manager_impl.h b/net/socket/client_socket_pool_manager_impl.h
index a2098331..8568410b 100644
--- a/net/socket/client_socket_pool_manager_impl.h
+++ b/net/socket/client_socket_pool_manager_impl.h
@@ -69,14 +69,9 @@
   void FlushSocketPoolsWithError(int error) override;
   void CloseIdleSockets() override;
 
-  TransportClientSocketPool* GetTransportSocketPool() override;
-
-  TransportClientSocketPool* GetSocketPoolForSOCKSProxy(
+  TransportClientSocketPool* GetSocketPool(
       const ProxyServer& proxy_server) override;
 
-  TransportClientSocketPool* GetSocketPoolForHTTPLikeProxy(
-      const ProxyServer& http_proxy) override;
-
   // Creates a Value summary of the state of the socket pools.
   std::unique_ptr<base::Value> SocketPoolInfoToValue() const override;
 
@@ -91,13 +86,6 @@
   using TransportSocketPoolMap =
       std::map<ProxyServer, std::unique_ptr<TransportClientSocketPool>>;
 
-  // Creates a TransportClientSocketPool appropriate for use with the passed in
-  // socket pool, passing in all needed parameters.
-  // TODO(mmenke): Can |use_socket_performance_watcher_factory| be removed?
-  std::unique_ptr<TransportClientSocketPool> CreateTransportSocketPool(
-      const ProxyServer& proxy_server,
-      bool use_socket_performance_watcher_factory);
-
   NetLog* const net_log_;
   ClientSocketFactory* const socket_factory_;
   SocketPerformanceWatcherFactory* socket_performance_watcher_factory_;
@@ -112,20 +100,11 @@
   SSLClientSessionCache* const ssl_client_session_cache_privacy_mode_;
   const std::string ssl_session_cache_shard_;
   SSLConfigService* const ssl_config_service_;
+  WebSocketEndpointLockManager* const websocket_endpoint_lock_manager_;
   ProxyDelegate* const proxy_delegate_;
   const HttpNetworkSession::SocketPoolType pool_type_;
 
-  // Note: this ordering is important.
-
-  std::unique_ptr<TransportClientSocketPool> transport_socket_pool_;
-
-  // Currently only contains socket pools for SOCKS proxies (With SSL over SOCKS
-  // connections layered on top of it, and appearing in
-  // |ssl_socket_pools_for_proxies_|), but will eventually contain all pools for
-  // proxies that use TCP connections.
-  TransportSocketPoolMap proxy_socket_pools_;
-
-  TransportSocketPoolMap http_proxy_socket_pools_;
+  TransportSocketPoolMap socket_pools_;
 
   THREAD_CHECKER(thread_checker_);
 
diff --git a/net/socket/fuzzed_socket_factory.cc b/net/socket/fuzzed_socket_factory.cc
index f9f8634..d8ecfe2 100644
--- a/net/socket/fuzzed_socket_factory.cc
+++ b/net/socket/fuzzed_socket_factory.cc
@@ -11,7 +11,6 @@
 #include "net/base/net_errors.h"
 #include "net/base/network_change_notifier.h"
 #include "net/log/net_log_with_source.h"
-#include "net/socket/client_socket_handle.h"
 #include "net/socket/connection_attempts.h"
 #include "net/socket/fuzzed_datagram_client_socket.h"
 #include "net/socket/fuzzed_socket.h"
@@ -145,37 +144,13 @@
 }
 
 std::unique_ptr<SSLClientSocket> FuzzedSocketFactory::CreateSSLClientSocket(
-    std::unique_ptr<ClientSocketHandle> transport_socket,
+    std::unique_ptr<StreamSocket> stream_socket,
     const HostPortPair& host_and_port,
     const SSLConfig& ssl_config,
     const SSLClientSocketContext& context) {
   return std::make_unique<FailingSSLClientSocket>();
 }
 
-std::unique_ptr<SSLClientSocket> FuzzedSocketFactory::CreateSSLClientSocket(
-    std::unique_ptr<StreamSocket> nested_socket,
-    const HostPortPair& host_and_port,
-    const SSLConfig& ssl_config,
-    const SSLClientSocketContext& context) {
-  return std::make_unique<FailingSSLClientSocket>();
-}
-
-std::unique_ptr<ProxyClientSocket> FuzzedSocketFactory::CreateProxyClientSocket(
-    std::unique_ptr<ClientSocketHandle> transport_socket,
-    const std::string& user_agent,
-    const HostPortPair& endpoint,
-    const ProxyServer& proxy_server,
-    HttpAuthController* http_auth_controller,
-    bool tunnel,
-    bool using_spdy,
-    NextProto negotiated_protocol,
-    ProxyDelegate* proxy_delegate,
-    bool is_https_proxy,
-    const NetworkTrafficAnnotationTag& traffic_annotation) {
-  NOTIMPLEMENTED();
-  return nullptr;
-}
-
 std::unique_ptr<ProxyClientSocket> FuzzedSocketFactory::CreateProxyClientSocket(
     std::unique_ptr<StreamSocket> stream_socket,
     const std::string& user_agent,
diff --git a/net/socket/fuzzed_socket_factory.h b/net/socket/fuzzed_socket_factory.h
index 42f949e..e09fba1 100644
--- a/net/socket/fuzzed_socket_factory.h
+++ b/net/socket/fuzzed_socket_factory.h
@@ -48,30 +48,11 @@
       const NetLogSource& source) override;
 
   std::unique_ptr<SSLClientSocket> CreateSSLClientSocket(
-      std::unique_ptr<ClientSocketHandle> transport_socket,
+      std::unique_ptr<StreamSocket> stream_socket,
       const HostPortPair& host_and_port,
       const SSLConfig& ssl_config,
       const SSLClientSocketContext& context) override;
 
-  std::unique_ptr<SSLClientSocket> CreateSSLClientSocket(
-      std::unique_ptr<StreamSocket> nested_socket,
-      const HostPortPair& host_and_port,
-      const SSLConfig& ssl_config,
-      const SSLClientSocketContext& context) override;
-
-  std::unique_ptr<ProxyClientSocket> CreateProxyClientSocket(
-      std::unique_ptr<ClientSocketHandle> transport_socket,
-      const std::string& user_agent,
-      const HostPortPair& endpoint,
-      const ProxyServer& proxy_server,
-      HttpAuthController* http_auth_controller,
-      bool tunnel,
-      bool using_spdy,
-      NextProto negotiated_protocol,
-      ProxyDelegate* proxy_delegate,
-      bool is_https_proxy,
-      const NetworkTrafficAnnotationTag& traffic_annotation) override;
-
   std::unique_ptr<ProxyClientSocket> CreateProxyClientSocket(
       std::unique_ptr<StreamSocket> stream_socket,
       const std::string& user_agent,
diff --git a/net/socket/mock_client_socket_pool_manager.cc b/net/socket/mock_client_socket_pool_manager.cc
index bbfb255..02d7b95 100644
--- a/net/socket/mock_client_socket_pool_manager.cc
+++ b/net/socket/mock_client_socket_pool_manager.cc
@@ -4,6 +4,8 @@
 
 #include "net/socket/mock_client_socket_pool_manager.h"
 
+#include <utility>
+
 #include "base/values.h"
 #include "net/socket/transport_client_socket_pool.h"
 
@@ -12,22 +14,10 @@
 MockClientSocketPoolManager::MockClientSocketPoolManager() = default;
 MockClientSocketPoolManager::~MockClientSocketPoolManager() = default;
 
-void MockClientSocketPoolManager::SetTransportSocketPool(
-    TransportClientSocketPool* pool) {
-  transport_socket_pool_.reset(pool);
-}
-
-void MockClientSocketPoolManager::SetSocketPoolForProxy(
+void MockClientSocketPoolManager::SetSocketPool(
     const ProxyServer& proxy_server,
     std::unique_ptr<TransportClientSocketPool> pool) {
-  DCHECK(proxy_server.is_socks());
-  proxy_socket_pools_[proxy_server] = std::move(pool);
-}
-
-void MockClientSocketPoolManager::SetSocketPoolForHTTPProxy(
-    const ProxyServer& http_proxy,
-    std::unique_ptr<TransportClientSocketPool> pool) {
-  http_proxy_socket_pools_[http_proxy] = std::move(pool);
+  socket_pools_[proxy_server] = std::move(pool);
 }
 
 void MockClientSocketPoolManager::FlushSocketPoolsWithError(int error) {
@@ -38,28 +28,11 @@
   NOTIMPLEMENTED();
 }
 
-TransportClientSocketPool*
-MockClientSocketPoolManager::GetTransportSocketPool() {
-  return transport_socket_pool_.get();
-}
-
-TransportClientSocketPool*
-MockClientSocketPoolManager::GetSocketPoolForSOCKSProxy(
+TransportClientSocketPool* MockClientSocketPoolManager::GetSocketPool(
     const ProxyServer& proxy_server) {
-  DCHECK(proxy_server.is_socks());
   TransportClientSocketPoolMap::const_iterator it =
-      proxy_socket_pools_.find(proxy_server);
-  if (it != proxy_socket_pools_.end())
-    return it->second.get();
-  return nullptr;
-}
-
-TransportClientSocketPool*
-MockClientSocketPoolManager::GetSocketPoolForHTTPLikeProxy(
-    const ProxyServer& http_proxy) {
-  TransportClientSocketPoolMap::const_iterator it =
-      http_proxy_socket_pools_.find(http_proxy);
-  if (it != http_proxy_socket_pools_.end())
+      socket_pools_.find(proxy_server);
+  if (it != socket_pools_.end())
     return it->second.get();
   return nullptr;
 }
diff --git a/net/socket/mock_client_socket_pool_manager.h b/net/socket/mock_client_socket_pool_manager.h
index 8eb68cc..25459a0 100644
--- a/net/socket/mock_client_socket_pool_manager.h
+++ b/net/socket/mock_client_socket_pool_manager.h
@@ -21,23 +21,15 @@
   MockClientSocketPoolManager();
   ~MockClientSocketPoolManager() override;
 
-  // Sets "override" socket pools that get used instead.
-  void SetTransportSocketPool(TransportClientSocketPool* pool);
-  // Currently only works for SOCKS proxies.
-  void SetSocketPoolForProxy(const ProxyServer& proxy_server,
-                             std::unique_ptr<TransportClientSocketPool> pool);
-  void SetSocketPoolForHTTPProxy(
-      const ProxyServer& http_proxy,
-      std::unique_ptr<TransportClientSocketPool> pool);
+  // Sets socket pool that gets used for the specified ProxyServer.
+  void SetSocketPool(const ProxyServer& proxy_server,
+                     std::unique_ptr<TransportClientSocketPool> pool);
 
   // ClientSocketPoolManager methods:
   void FlushSocketPoolsWithError(int error) override;
   void CloseIdleSockets() override;
-  TransportClientSocketPool* GetTransportSocketPool() override;
-  TransportClientSocketPool* GetSocketPoolForSOCKSProxy(
-      const ProxyServer& socks_proxy) override;
-  TransportClientSocketPool* GetSocketPoolForHTTPLikeProxy(
-      const ProxyServer& http_proxy) override;
+  TransportClientSocketPool* GetSocketPool(
+      const ProxyServer& proxy_server) override;
   std::unique_ptr<base::Value> SocketPoolInfoToValue() const override;
   void DumpMemoryStats(
       base::trace_event::ProcessMemoryDump* pmd,
@@ -47,9 +39,7 @@
   using TransportClientSocketPoolMap =
       std::map<ProxyServer, std::unique_ptr<TransportClientSocketPool>>;
 
-  std::unique_ptr<TransportClientSocketPool> transport_socket_pool_;
-  TransportClientSocketPoolMap proxy_socket_pools_;
-  TransportClientSocketPoolMap http_proxy_socket_pools_;
+  TransportClientSocketPoolMap socket_pools_;
 
   DISALLOW_COPY_AND_ASSIGN(MockClientSocketPoolManager);
 };
diff --git a/net/socket/socket_test_util.cc b/net/socket/socket_test_util.cc
index 92ba59ea..90ede319 100644
--- a/net/socket/socket_test_util.cc
+++ b/net/socket/socket_test_util.cc
@@ -777,7 +777,7 @@
 }
 
 std::unique_ptr<SSLClientSocket> MockClientSocketFactory::CreateSSLClientSocket(
-    std::unique_ptr<ClientSocketHandle> transport_socket,
+    std::unique_ptr<StreamSocket> stream_socket,
     const HostPortPair& host_and_port,
     const SSLConfig& ssl_config,
     const SSLClientSocketContext& context) {
@@ -793,52 +793,7 @@
   EXPECT_EQ(next_ssl_data->expected_ssl_version_min, ssl_config.version_min);
   EXPECT_EQ(next_ssl_data->expected_ssl_version_max, ssl_config.version_max);
   return std::unique_ptr<SSLClientSocket>(new MockSSLClientSocket(
-      std::move(transport_socket), host_and_port, ssl_config, next_ssl_data));
-}
-
-std::unique_ptr<SSLClientSocket> MockClientSocketFactory::CreateSSLClientSocket(
-    std::unique_ptr<StreamSocket> nested_socket,
-    const HostPortPair& host_and_port,
-    const SSLConfig& ssl_config,
-    const SSLClientSocketContext& context) {
-  SSLSocketDataProvider* next_ssl_data = mock_ssl_data_.GetNext();
-  if (next_ssl_data->next_protos_expected_in_ssl_config.has_value()) {
-    EXPECT_EQ(next_ssl_data->next_protos_expected_in_ssl_config.value().size(),
-              ssl_config.alpn_protos.size());
-    EXPECT_TRUE(std::equal(
-        next_ssl_data->next_protos_expected_in_ssl_config.value().begin(),
-        next_ssl_data->next_protos_expected_in_ssl_config.value().end(),
-        ssl_config.alpn_protos.begin()));
-  }
-  EXPECT_EQ(next_ssl_data->expected_ssl_version_min, ssl_config.version_min);
-  EXPECT_EQ(next_ssl_data->expected_ssl_version_max, ssl_config.version_max);
-  return std::unique_ptr<SSLClientSocket>(new MockSSLClientSocket(
-      std::move(nested_socket), host_and_port, ssl_config, next_ssl_data));
-}
-
-std::unique_ptr<ProxyClientSocket>
-MockClientSocketFactory::CreateProxyClientSocket(
-    std::unique_ptr<ClientSocketHandle> transport_socket,
-    const std::string& user_agent,
-    const HostPortPair& endpoint,
-    const ProxyServer& proxy_server,
-    HttpAuthController* http_auth_controller,
-    bool tunnel,
-    bool using_spdy,
-    NextProto negotiated_protocol,
-    ProxyDelegate* proxy_delegate,
-    bool is_https_proxy,
-    const NetworkTrafficAnnotationTag& traffic_annotation) {
-  if (use_mock_proxy_client_sockets_) {
-    ProxyClientSocketDataProvider* next_proxy_data = mock_proxy_data_.GetNext();
-    return std::make_unique<MockProxyClientSocket>(
-        std::move(transport_socket), http_auth_controller, next_proxy_data);
-  } else {
-    return GetDefaultFactory()->CreateProxyClientSocket(
-        std::move(transport_socket), user_agent, endpoint, proxy_server,
-        http_auth_controller, tunnel, using_spdy, negotiated_protocol,
-        proxy_delegate, is_https_proxy, traffic_annotation);
-  }
+      std::move(stream_socket), host_and_port, ssl_config, next_ssl_data));
 }
 
 std::unique_ptr<ProxyClientSocket>
@@ -1306,25 +1261,11 @@
 }
 
 MockProxyClientSocket::MockProxyClientSocket(
-    std::unique_ptr<StreamSocket> stream_socket,
+    std::unique_ptr<StreamSocket> socket,
     HttpAuthController* auth_controller,
     ProxyClientSocketDataProvider* data)
-    : net_log_(stream_socket->NetLog()),
-      stream_socket_(std::move(stream_socket)),
-      socket_(stream_socket_.get()),
-      data_(data),
-      auth_controller_(auth_controller),
-      weak_factory_(this) {
-  DCHECK(data_);
-}
-
-MockProxyClientSocket::MockProxyClientSocket(
-    std::unique_ptr<ClientSocketHandle> client_socket_handle,
-    HttpAuthController* auth_controller,
-    ProxyClientSocketDataProvider* data)
-    : net_log_(client_socket_handle->socket()->NetLog()),
-      client_socket_handle_(std::move(client_socket_handle)),
-      socket_(client_socket_handle_->socket()),
+    : net_log_(socket->NetLog()),
+      socket_(std::move(socket)),
       data_(data),
       auth_controller_(auth_controller),
       weak_factory_(this) {
@@ -1487,27 +1428,12 @@
 }
 
 MockSSLClientSocket::MockSSLClientSocket(
-    std::unique_ptr<ClientSocketHandle> transport_socket,
-    const HostPortPair& host_port_pair,
-    const SSLConfig& ssl_config,
-    SSLSocketDataProvider* data)
-    : net_log_(transport_socket->socket()->NetLog()),
-      client_socket_handle_(std::move(transport_socket)),
-      stream_socket_(client_socket_handle_->socket()),
-      data_(data),
-      weak_factory_(this) {
-  DCHECK(data_);
-  peer_addr_ = data->connect.peer_addr;
-}
-
-MockSSLClientSocket::MockSSLClientSocket(
-    std::unique_ptr<StreamSocket> nested_socket,
+    std::unique_ptr<StreamSocket> stream_socket,
     const HostPortPair& host_and_port,
     const SSLConfig& ssl_config,
     SSLSocketDataProvider* data)
-    : net_log_(nested_socket->NetLog()),
-      nested_socket_(std::move(nested_socket)),
-      stream_socket_(nested_socket_.get()),
+    : net_log_(stream_socket->NetLog()),
+      stream_socket_(std::move(stream_socket)),
       data_(data),
       weak_factory_(this) {
   DCHECK(data_);
diff --git a/net/socket/socket_test_util.h b/net/socket/socket_test_util.h
index 88bbae2ab..4dd63f7 100644
--- a/net/socket/socket_test_util.h
+++ b/net/socket/socket_test_util.h
@@ -621,27 +621,10 @@
       NetLog* net_log,
       const NetLogSource& source) override;
   std::unique_ptr<SSLClientSocket> CreateSSLClientSocket(
-      std::unique_ptr<ClientSocketHandle> transport_socket,
+      std::unique_ptr<StreamSocket> stream_socket,
       const HostPortPair& host_and_port,
       const SSLConfig& ssl_config,
       const SSLClientSocketContext& context) override;
-  std::unique_ptr<SSLClientSocket> CreateSSLClientSocket(
-      std::unique_ptr<StreamSocket> nested_socket,
-      const HostPortPair& host_and_port,
-      const SSLConfig& ssl_config,
-      const SSLClientSocketContext& context) override;
-  std::unique_ptr<ProxyClientSocket> CreateProxyClientSocket(
-      std::unique_ptr<ClientSocketHandle> transport_socket,
-      const std::string& user_agent,
-      const HostPortPair& endpoint,
-      const ProxyServer& proxy_server,
-      HttpAuthController* http_auth_controller,
-      bool tunnel,
-      bool using_spdy,
-      NextProto negotiated_protocol,
-      ProxyDelegate* proxy_delegate,
-      bool is_https_proxy,
-      const NetworkTrafficAnnotationTag& traffic_annotation) override;
   std::unique_ptr<ProxyClientSocket> CreateProxyClientSocket(
       std::unique_ptr<StreamSocket> stream_socket,
       const std::string& user_agent,
@@ -828,12 +811,7 @@
 
 class MockProxyClientSocket : public AsyncSocket, public ProxyClientSocket {
  public:
-  // TODO(mmenke): Remove this constructor.
-  MockProxyClientSocket(
-      std::unique_ptr<ClientSocketHandle> client_socket_handle,
-      HttpAuthController* auth_controller,
-      ProxyClientSocketDataProvider* data);
-  MockProxyClientSocket(std::unique_ptr<StreamSocket> stream_socket,
+  MockProxyClientSocket(std::unique_ptr<StreamSocket> socket,
                         HttpAuthController* auth_controller,
                         ProxyClientSocketDataProvider* data);
   ~MockProxyClientSocket() override;
@@ -887,9 +865,7 @@
   void RunCallbackAsync(CompletionOnceCallback callback, int result);
 
   NetLogWithSource net_log_;
-  std::unique_ptr<ClientSocketHandle> client_socket_handle_;
-  std::unique_ptr<StreamSocket> stream_socket_;
-  StreamSocket* socket_;
+  std::unique_ptr<StreamSocket> socket_;
   ProxyClientSocketDataProvider* data_;
   scoped_refptr<HttpAuthController> auth_controller_;
 
@@ -900,12 +876,7 @@
 
 class MockSSLClientSocket : public AsyncSocket, public SSLClientSocket {
  public:
-  // TODO(mmenke): Remove this constructor.
-  MockSSLClientSocket(std::unique_ptr<ClientSocketHandle> transport_socket,
-                      const HostPortPair& host_and_port,
-                      const SSLConfig& ssl_config,
-                      SSLSocketDataProvider* socket);
-  MockSSLClientSocket(std::unique_ptr<StreamSocket> nested_socket,
+  MockSSLClientSocket(std::unique_ptr<StreamSocket> stream_socket,
                       const HostPortPair& host_and_port,
                       const SSLConfig& ssl_config,
                       SSLSocketDataProvider* socket);
@@ -974,11 +945,7 @@
 
   bool connected_ = false;
   NetLogWithSource net_log_;
-  std::unique_ptr<ClientSocketHandle> client_socket_handle_;
-  std::unique_ptr<StreamSocket> nested_socket_;
-  // Owned by either |nested_socket_| or |client_socket_handle_|, depending on
-  // the constructor that was used.
-  StreamSocket* stream_socket_;
+  std::unique_ptr<StreamSocket> stream_socket_;
   SSLSocketDataProvider* data_;
   // Address of the "remote" peer we're connected to.
   IPEndPoint peer_addr_;
diff --git a/net/socket/ssl_client_socket_impl.cc b/net/socket/ssl_client_socket_impl.cc
index efa267ce..c49a65e 100644
--- a/net/socket/ssl_client_socket_impl.cc
+++ b/net/socket/ssl_client_socket_impl.cc
@@ -403,7 +403,7 @@
 };
 
 SSLClientSocketImpl::SSLClientSocketImpl(
-    std::unique_ptr<ClientSocketHandle> transport_socket,
+    std::unique_ptr<StreamSocket> stream_socket,
     const HostPortPair& host_and_port,
     const SSLConfig& ssl_config,
     const SSLClientSocketContext& context)
@@ -414,43 +414,7 @@
       cert_verifier_(context.cert_verifier),
       cert_verification_result_(kCertVerifyPending),
       cert_transparency_verifier_(context.cert_transparency_verifier),
-      client_socket_handle_(std::move(transport_socket)),
-      stream_socket_(client_socket_handle_->socket()),
-      host_and_port_(host_and_port),
-      ssl_config_(ssl_config),
-      ssl_client_session_cache_(context.ssl_client_session_cache),
-      next_handshake_state_(STATE_NONE),
-      in_confirm_handshake_(false),
-      disconnected_(false),
-      negotiated_protocol_(kProtoUnknown),
-      certificate_requested_(false),
-      signature_result_(kSSLClientSocketNoPendingResult),
-      transport_security_state_(context.transport_security_state),
-      policy_enforcer_(context.ct_policy_enforcer),
-      pkp_bypassed_(false),
-      is_fatal_cert_error_(false),
-      net_log_(stream_socket_->NetLog()),
-      weak_factory_(this) {
-  CHECK(cert_verifier_);
-  CHECK(transport_security_state_);
-  CHECK(cert_transparency_verifier_);
-  CHECK(policy_enforcer_);
-}
-
-SSLClientSocketImpl::SSLClientSocketImpl(
-    std::unique_ptr<StreamSocket> nested_socket,
-    const HostPortPair& host_and_port,
-    const SSLConfig& ssl_config,
-    const SSLClientSocketContext& context)
-    : pending_read_error_(kSSLClientSocketNoPendingResult),
-      pending_read_ssl_error_(SSL_ERROR_NONE),
-      completed_connect_(false),
-      was_ever_used_(false),
-      cert_verifier_(context.cert_verifier),
-      cert_verification_result_(kCertVerifyPending),
-      cert_transparency_verifier_(context.cert_transparency_verifier),
-      nested_socket_(std::move(nested_socket)),
-      stream_socket_(nested_socket_.get()),
+      stream_socket_(std::move(stream_socket)),
       host_and_port_(host_and_port),
       ssl_config_(ssl_config),
       ssl_client_session_cache_(context.ssl_client_session_cache),
@@ -839,7 +803,7 @@
   }
 
   transport_adapter_.reset(
-      new SocketBIOAdapter(stream_socket_, kDefaultOpenSSLBufferSize,
+      new SocketBIOAdapter(stream_socket_.get(), kDefaultOpenSSLBufferSize,
                            kDefaultOpenSSLBufferSize, this));
   BIO* transport_bio = transport_adapter_->bio();
 
diff --git a/net/socket/ssl_client_socket_impl.h b/net/socket/ssl_client_socket_impl.h
index 8cb0975..301f12f 100644
--- a/net/socket/ssl_client_socket_impl.h
+++ b/net/socket/ssl_client_socket_impl.h
@@ -18,12 +18,12 @@
 #include "base/memory/ref_counted.h"
 #include "base/memory/weak_ptr.h"
 #include "net/base/completion_callback.h"
+#include "net/base/host_port_pair.h"
 #include "net/base/io_buffer.h"
 #include "net/cert/cert_verifier.h"
 #include "net/cert/cert_verify_result.h"
 #include "net/cert/ct_verify_result.h"
 #include "net/log/net_log_with_source.h"
-#include "net/socket/client_socket_handle.h"
 #include "net/socket/next_proto.h"
 #include "net/socket/socket_bio_adapter.h"
 #include "net/socket/ssl_client_socket.h"
@@ -50,20 +50,11 @@
 class SSLClientSocketImpl : public SSLClientSocket,
                             public SocketBIOAdapter::Delegate {
  public:
-  // Takes ownership of the transport_socket, which may already be connected.
+  // Takes ownership of |stream_socket|, which may already be connected.
   // The given hostname will be compared with the name(s) in the server's
   // certificate during the SSL handshake.  |ssl_config| specifies the SSL
   // settings.
-  // TODO(mmenke): Remove this constructor in favor of the next one.
-  SSLClientSocketImpl(std::unique_ptr<ClientSocketHandle> transport_socket,
-                      const HostPortPair& host_and_port,
-                      const SSLConfig& ssl_config,
-                      const SSLClientSocketContext& context);
-  // Takes ownership of |nested_socket|, which may already be connected.
-  // The given hostname will be compared with the name(s) in the server's
-  // certificate during the SSL handshake.  |ssl_config| specifies the SSL
-  // settings.
-  SSLClientSocketImpl(std::unique_ptr<StreamSocket> nested_socket,
+  SSLClientSocketImpl(std::unique_ptr<StreamSocket> stream_socket,
                       const HostPortPair& host_and_port,
                       const SSLConfig& ssl_config,
                       const SSLClientSocketContext& context);
@@ -267,11 +258,7 @@
   // OpenSSL stuff
   bssl::UniquePtr<SSL> ssl_;
 
-  std::unique_ptr<ClientSocketHandle> client_socket_handle_;
-  std::unique_ptr<StreamSocket> nested_socket_;
-  // Either |nested_socket_| or the socket owned by |client_socket_handle_|,
-  // depending on constructor used.
-  StreamSocket* stream_socket_;
+  std::unique_ptr<StreamSocket> stream_socket_;
   std::unique_ptr<SocketBIOAdapter> transport_adapter_;
   const HostPortPair host_and_port_;
   SSLConfig ssl_config_;
diff --git a/net/socket/ssl_client_socket_pool_unittest.cc b/net/socket/ssl_client_socket_pool_unittest.cc
deleted file mode 100644
index 813cf98..0000000
--- a/net/socket/ssl_client_socket_pool_unittest.cc
+++ /dev/null
@@ -1,201 +0,0 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "base/callback.h"
-#include "base/compiler_specific.h"
-#include "base/stl_util.h"
-#include "base/strings/string_util.h"
-#include "base/strings/utf_string_conversions.h"
-#include "base/test/bind_test_util.h"
-#include "base/time/time.h"
-#include "build/build_config.h"
-#include "net/base/auth.h"
-#include "net/base/net_errors.h"
-#include "net/base/test_completion_callback.h"
-#include "net/cert/ct_policy_enforcer.h"
-#include "net/cert/mock_cert_verifier.h"
-#include "net/cert/multi_log_ct_verifier.h"
-#include "net/dns/mock_host_resolver.h"
-#include "net/http/http_auth_handler_factory.h"
-#include "net/http/http_network_session.h"
-#include "net/http/http_proxy_connect_job.h"
-#include "net/http/http_request_headers.h"
-#include "net/http/http_response_headers.h"
-#include "net/http/http_server_properties_impl.h"
-#include "net/http/transport_security_state.h"
-#include "net/log/net_log_source.h"
-#include "net/log/net_log_with_source.h"
-#include "net/proxy_resolution/proxy_resolution_service.h"
-#include "net/socket/client_socket_handle.h"
-#include "net/socket/next_proto.h"
-#include "net/socket/socket_tag.h"
-#include "net/socket/socket_test_util.h"
-#include "net/socket/socks_connect_job.h"
-#include "net/socket/ssl_connect_job.h"
-#include "net/socket/transport_client_socket_pool.h"
-#include "net/socket/transport_connect_job.h"
-#include "net/spdy/spdy_session.h"
-#include "net/spdy/spdy_session_pool.h"
-#include "net/spdy/spdy_test_util_common.h"
-#include "net/ssl/ssl_config_service_defaults.h"
-#include "net/test/embedded_test_server/embedded_test_server.h"
-#include "net/test/gtest_util.h"
-#include "net/test/test_certificate_data.h"
-#include "net/test/test_with_scoped_task_environment.h"
-#include "net/traffic_annotation/network_traffic_annotation_test_helper.h"
-#include "testing/gmock/include/gmock/gmock.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-using net::test::IsError;
-using net::test::IsOk;
-
-namespace net {
-
-namespace {
-
-const int kMaxSockets = 32;
-const int kMaxSocketsPerGroup = 6;
-constexpr base::TimeDelta kUnusedIdleSocketTimeout =
-    base::TimeDelta::FromSeconds(10);
-const char kGroupName[] = "a";
-
-class SSLClientSocketPoolTest : public TestWithScopedTaskEnvironment {
- protected:
-  SSLClientSocketPoolTest()
-      : cert_verifier_(new MockCertVerifier),
-        transport_security_state_(new TransportSecurityState),
-        proxy_resolution_service_(ProxyResolutionService::CreateDirect()),
-        ssl_config_service_(new SSLConfigServiceDefaults),
-        http_auth_handler_factory_(
-            HttpAuthHandlerFactory::CreateDefault(&host_resolver_)),
-        http_server_properties_(new HttpServerPropertiesImpl),
-        session_(CreateNetworkSession()),
-        direct_transport_socket_params_(
-            new TransportSocketParams(HostPortPair("host", 443),
-                                      false,
-                                      OnHostResolutionCallback())),
-        proxy_transport_socket_params_(
-            new TransportSocketParams(HostPortPair("proxy", 443),
-                                      false,
-                                      OnHostResolutionCallback())),
-        http_proxy_socket_params_(
-            new HttpProxySocketParams(proxy_transport_socket_params_,
-                                      NULL,
-                                      quic::QUIC_VERSION_UNSUPPORTED,
-                                      std::string(),
-                                      HostPortPair("host", 80),
-                                      session_->http_auth_cache(),
-                                      session_->http_auth_handler_factory(),
-                                      session_->spdy_session_pool(),
-                                      session_->quic_stream_factory(),
-                                      /*is_trusted_proxy=*/false,
-                                      /*tunnel=*/true,
-                                      TRAFFIC_ANNOTATION_FOR_TESTS)) {
-    ssl_config_service_->GetSSLConfig(&ssl_config_);
-  }
-
-  void CreatePool() {
-    pool_.reset(new TransportClientSocketPool(
-        kMaxSockets, kMaxSocketsPerGroup, kUnusedIdleSocketTimeout,
-        &socket_factory_, &host_resolver_, NULL /* proxy_delegate */,
-        cert_verifier_.get(), NULL /* channel_id_service */,
-        transport_security_state_.get(), &ct_verifier_, &ct_policy_enforcer_,
-        nullptr /* ssl_client_session_cache */,
-        nullptr /* ssl_client_session_cache_privacy_mode */,
-        nullptr /* ssl_config_service */,
-        nullptr /* socket_performance_watcher_factory */,
-        nullptr /* network_quality_estimator */, nullptr /* net_log */));
-  }
-
-  scoped_refptr<SSLSocketParams> SSLParams(ProxyServer::Scheme proxy) {
-    return base::MakeRefCounted<SSLSocketParams>(
-        proxy == ProxyServer::SCHEME_DIRECT ? direct_transport_socket_params_
-                                            : nullptr,
-        nullptr,
-        proxy == ProxyServer::SCHEME_HTTP ? http_proxy_socket_params_ : nullptr,
-        HostPortPair("host", 443), ssl_config_, PRIVACY_MODE_DISABLED);
-  }
-
-  void AddAuthToCache() {
-    const base::string16 kFoo(base::ASCIIToUTF16("foo"));
-    const base::string16 kBar(base::ASCIIToUTF16("bar"));
-    session_->http_auth_cache()->Add(GURL("http://proxy:443/"),
-                                     "MyRealm1",
-                                     HttpAuth::AUTH_SCHEME_BASIC,
-                                     "Basic realm=MyRealm1",
-                                     AuthCredentials(kFoo, kBar),
-                                     "/");
-  }
-
-  HttpNetworkSession* CreateNetworkSession() {
-    HttpNetworkSession::Context session_context;
-    session_context.host_resolver = &host_resolver_;
-    session_context.cert_verifier = cert_verifier_.get();
-    session_context.transport_security_state = transport_security_state_.get();
-    session_context.cert_transparency_verifier = &ct_verifier_;
-    session_context.ct_policy_enforcer = &ct_policy_enforcer_;
-    session_context.proxy_resolution_service = proxy_resolution_service_.get();
-    session_context.client_socket_factory = &socket_factory_;
-    session_context.ssl_config_service = ssl_config_service_.get();
-    session_context.http_auth_handler_factory =
-        http_auth_handler_factory_.get();
-    session_context.http_server_properties = http_server_properties_.get();
-    return new HttpNetworkSession(HttpNetworkSession::Params(),
-                                  session_context);
-  }
-
-  MockClientSocketFactory socket_factory_;
-  MockCachingHostResolver host_resolver_;
-  std::unique_ptr<MockCertVerifier> cert_verifier_;
-  std::unique_ptr<TransportSecurityState> transport_security_state_;
-  MultiLogCTVerifier ct_verifier_;
-  DefaultCTPolicyEnforcer ct_policy_enforcer_;
-  const std::unique_ptr<ProxyResolutionService> proxy_resolution_service_;
-  const std::unique_ptr<SSLConfigService> ssl_config_service_;
-  const std::unique_ptr<HttpAuthHandlerFactory> http_auth_handler_factory_;
-  const std::unique_ptr<HttpServerPropertiesImpl> http_server_properties_;
-  const std::unique_ptr<HttpNetworkSession> session_;
-
-  scoped_refptr<TransportSocketParams> direct_transport_socket_params_;
-
-  scoped_refptr<TransportSocketParams> proxy_transport_socket_params_;
-
-  scoped_refptr<HttpProxySocketParams> http_proxy_socket_params_;
-
-  SSLConfig ssl_config_;
-  std::unique_ptr<TransportClientSocketPool> pool_;
-};
-
-TEST_F(SSLClientSocketPoolTest, DirectCertError) {
-  StaticSocketDataProvider data;
-  socket_factory_.AddSocketDataProvider(&data);
-  SSLSocketDataProvider ssl(ASYNC, ERR_CERT_COMMON_NAME_INVALID);
-  socket_factory_.AddSSLSocketDataProvider(&ssl);
-
-  CreatePool();
-  scoped_refptr<SSLSocketParams> params = SSLParams(ProxyServer::SCHEME_DIRECT);
-
-  ClientSocketHandle handle;
-  TestCompletionCallback callback;
-  int rv = handle.Init(
-      kGroupName,
-      TransportClientSocketPool::SocketParams::CreateFromSSLSocketParams(
-          params),
-      MEDIUM, SocketTag(), ClientSocketPool::RespectLimits::ENABLED,
-      callback.callback(), ClientSocketPool::ProxyAuthCallback(), pool_.get(),
-      NetLogWithSource());
-  EXPECT_THAT(rv, IsError(ERR_IO_PENDING));
-  EXPECT_FALSE(handle.is_initialized());
-  EXPECT_FALSE(handle.socket());
-
-  EXPECT_THAT(callback.WaitForResult(), IsError(ERR_CERT_COMMON_NAME_INVALID));
-  EXPECT_TRUE(handle.is_initialized());
-  EXPECT_TRUE(handle.socket());
-}
-
-// It would be nice to also test the timeouts in SSLClientSocketPool.
-
-}  // namespace
-
-}  // namespace net
diff --git a/net/socket/ssl_client_socket_unittest.cc b/net/socket/ssl_client_socket_unittest.cc
index 78d9b70e..1eb85616 100644
--- a/net/socket/ssl_client_socket_unittest.cc
+++ b/net/socket/ssl_client_socket_unittest.cc
@@ -851,10 +851,8 @@
       std::unique_ptr<StreamSocket> transport_socket,
       const HostPortPair& host_and_port,
       const SSLConfig& ssl_config) {
-    std::unique_ptr<ClientSocketHandle> connection(new ClientSocketHandle);
-    connection->SetSocket(std::move(transport_socket));
     return socket_factory_->CreateSSLClientSocket(
-        std::move(connection), host_and_port, ssl_config, context_);
+        std::move(transport_socket), host_and_port, ssl_config, context_);
   }
 
   // Create an SSLClientSocket object and use it to connect to a test server,
@@ -2386,11 +2384,8 @@
   int rv = callback.GetResult(transport->Connect(callback.callback()));
   EXPECT_THAT(rv, IsOk());
 
-  std::unique_ptr<ClientSocketHandle> socket_handle(new ClientSocketHandle());
-  socket_handle->SetSocket(std::move(transport));
-
   std::unique_ptr<SSLClientSocket> sock(socket_factory_->CreateSSLClientSocket(
-      std::move(socket_handle), spawned_test_server()->host_port_pair(),
+      std::move(transport), spawned_test_server()->host_port_pair(),
       SSLConfig(), context_));
 
   EXPECT_FALSE(sock->IsConnected());
diff --git a/net/socket/ssl_connect_job_unittest.cc b/net/socket/ssl_connect_job_unittest.cc
index ddf58f3..eb684db0 100644
--- a/net/socket/ssl_connect_job_unittest.cc
+++ b/net/socket/ssl_connect_job_unittest.cc
@@ -89,8 +89,7 @@
                 MAIN_THREAD_MOCK_TIME),
         proxy_resolution_service_(ProxyResolutionService::CreateDirect()),
         ssl_config_service_(new SSLConfigServiceDefaults),
-        http_auth_handler_factory_(
-            HttpAuthHandlerFactory::CreateDefault(&host_resolver_)),
+        http_auth_handler_factory_(HttpAuthHandlerFactory::CreateDefault()),
         session_(CreateNetworkSession()),
         ssl_client_socket_context_(&cert_verifier_,
                                    nullptr /* channel_id_service_arg */,
diff --git a/net/socket/ssl_server_socket_unittest.cc b/net/socket/ssl_server_socket_unittest.cc
index 78bb3ca7..c5d55f6 100644
--- a/net/socket/ssl_server_socket_unittest.cc
+++ b/net/socket/ssl_server_socket_unittest.cc
@@ -417,12 +417,10 @@
     server_socket_.reset();
     channel_1_.reset(new FakeDataChannel());
     channel_2_.reset(new FakeDataChannel());
-    std::unique_ptr<ClientSocketHandle> client_connection(
-        new ClientSocketHandle);
-    client_connection->SetSocket(std::unique_ptr<StreamSocket>(
-        new FakeSocket(channel_1_.get(), channel_2_.get())));
-    std::unique_ptr<StreamSocket> server_socket(
-        new FakeSocket(channel_2_.get(), channel_1_.get()));
+    std::unique_ptr<StreamSocket> client_connection =
+        std::make_unique<FakeSocket>(channel_1_.get(), channel_2_.get());
+    std::unique_ptr<StreamSocket> server_socket =
+        std::make_unique<FakeSocket>(channel_2_.get(), channel_1_.get());
 
     HostPortPair host_and_pair("unittest", 0);
     SSLClientSocketContext context(
diff --git a/net/socket/transport_client_socket_pool.cc b/net/socket/transport_client_socket_pool.cc
index 05a619f..023d121 100644
--- a/net/socket/transport_client_socket_pool.cc
+++ b/net/socket/transport_client_socket_pool.cc
@@ -170,8 +170,7 @@
     SocketPerformanceWatcherFactory* socket_performance_watcher_factory,
     NetworkQualityEstimator* network_quality_estimator,
     NetLog* net_log)
-    : base_(this,
-            max_sockets,
+    : base_(max_sockets,
             max_sockets_per_group,
             unused_idle_socket_timeout,
             ClientSocketPool::used_idle_socket_timeout(),
@@ -301,8 +300,7 @@
 
 std::unique_ptr<base::DictionaryValue>
 TransportClientSocketPool::GetInfoAsValue(const std::string& name,
-                                          const std::string& type,
-                                          bool include_nested_pools) const {
+                                          const std::string& type) const {
   return base_.GetInfoAsValue(name, type);
 }
 
@@ -320,12 +318,6 @@
   base_.RemoveHigherLayeredPool(higher_pool);
 }
 
-bool TransportClientSocketPool::CloseOneIdleConnection() {
-  if (base_.CloseOneIdleSocket())
-    return true;
-  return base_.CloseOneIdleConnectionInHigherLayeredPool();
-}
-
 void TransportClientSocketPool::DumpMemoryStats(
     base::trace_event::ProcessMemoryDump* pmd,
     const std::string& parent_dump_absolute_name) const {
diff --git a/net/socket/transport_client_socket_pool.h b/net/socket/transport_client_socket_pool.h
index 03741b7..7197460 100644
--- a/net/socket/transport_client_socket_pool.h
+++ b/net/socket/transport_client_socket_pool.h
@@ -38,11 +38,8 @@
 class TransportSecurityState;
 class TransportSocketParams;
 
-// TODO(mmenke): Remove HigherLayeredPool implementation once this can no longer
-// sit on top of other socket pools.
 class NET_EXPORT_PRIVATE TransportClientSocketPool
     : public ClientSocketPool,
-      public HigherLayeredPool,
       public SSLConfigService::Observer {
  public:
   // Callback to create a ConnectJob using the provided arguments. The lower
@@ -145,17 +142,13 @@
                          const ClientSocketHandle* handle) const override;
   std::unique_ptr<base::DictionaryValue> GetInfoAsValue(
       const std::string& name,
-      const std::string& type,
-      bool include_nested_pools) const override;
+      const std::string& type) const override;
 
   // LowerLayeredPool implementation.
   bool IsStalled() const override;
   void AddHigherLayeredPool(HigherLayeredPool* higher_pool) override;
   void RemoveHigherLayeredPool(HigherLayeredPool* higher_pool) override;
 
-  // HigherLayeredPool implementation.
-  bool CloseOneIdleConnection() override;
-
   void DumpMemoryStats(base::trace_event::ProcessMemoryDump* pmd,
                        const std::string& parent_dump_absolute_name) const;
 
diff --git a/net/socket/transport_client_socket_pool_test_util.cc b/net/socket/transport_client_socket_pool_test_util.cc
index edb87f1..e3c5155ef 100644
--- a/net/socket/transport_client_socket_pool_test_util.cc
+++ b/net/socket/transport_client_socket_pool_test_util.cc
@@ -444,7 +444,7 @@
 
 std::unique_ptr<SSLClientSocket>
 MockTransportClientSocketFactory::CreateSSLClientSocket(
-    std::unique_ptr<ClientSocketHandle> transport_socket,
+    std::unique_ptr<StreamSocket> stream_socket,
     const HostPortPair& host_and_port,
     const SSLConfig& ssl_config,
     const SSLClientSocketContext& context) {
@@ -452,33 +452,6 @@
   return std::unique_ptr<SSLClientSocket>();
 }
 
-std::unique_ptr<SSLClientSocket>
-MockTransportClientSocketFactory::CreateSSLClientSocket(
-    std::unique_ptr<StreamSocket> nested_socket,
-    const HostPortPair& host_and_port,
-    const SSLConfig& ssl_config,
-    const SSLClientSocketContext& context) {
-  NOTIMPLEMENTED();
-  return std::unique_ptr<SSLClientSocket>();
-}
-
-std::unique_ptr<ProxyClientSocket>
-MockTransportClientSocketFactory::CreateProxyClientSocket(
-    std::unique_ptr<ClientSocketHandle> transport_socket,
-    const std::string& user_agent,
-    const HostPortPair& endpoint,
-    const ProxyServer& proxy_server,
-    HttpAuthController* http_auth_controller,
-    bool tunnel,
-    bool using_spdy,
-    NextProto negotiated_protocol,
-    ProxyDelegate* proxy_delegate,
-    bool is_https_proxy,
-    const NetworkTrafficAnnotationTag& traffic_annotation) {
-  NOTIMPLEMENTED();
-  return nullptr;
-}
-
 std::unique_ptr<ProxyClientSocket>
 MockTransportClientSocketFactory::CreateProxyClientSocket(
     std::unique_ptr<StreamSocket> stream_socket,
diff --git a/net/socket/transport_client_socket_pool_test_util.h b/net/socket/transport_client_socket_pool_test_util.h
index eeb7d03..ac2b73e 100644
--- a/net/socket/transport_client_socket_pool_test_util.h
+++ b/net/socket/transport_client_socket_pool_test_util.h
@@ -86,11 +86,6 @@
       NetLog* /* net_log */,
       const NetLogSource& /* source */) override;
 
-  std::unique_ptr<SSLClientSocket> CreateSSLClientSocket(
-      std::unique_ptr<ClientSocketHandle> transport_socket,
-      const HostPortPair& host_and_port,
-      const SSLConfig& ssl_config,
-      const SSLClientSocketContext& context) override;
 
   std::unique_ptr<SSLClientSocket> CreateSSLClientSocket(
       std::unique_ptr<StreamSocket> nested_socket,
@@ -99,19 +94,6 @@
       const SSLClientSocketContext& context) override;
 
   std::unique_ptr<ProxyClientSocket> CreateProxyClientSocket(
-      std::unique_ptr<ClientSocketHandle> transport_socket,
-      const std::string& user_agent,
-      const HostPortPair& endpoint,
-      const ProxyServer& proxy_server,
-      HttpAuthController* http_auth_controller,
-      bool tunnel,
-      bool using_spdy,
-      NextProto negotiated_protocol,
-      ProxyDelegate* proxy_delegate,
-      bool is_https_proxy,
-      const NetworkTrafficAnnotationTag& traffic_annotation) override;
-
-  std::unique_ptr<ProxyClientSocket> CreateProxyClientSocket(
       std::unique_ptr<StreamSocket> stream_socket,
       const std::string& user_agent,
       const HostPortPair& endpoint,
diff --git a/net/socket/transport_client_socket_pool_unittest.cc b/net/socket/transport_client_socket_pool_unittest.cc
index ce84a2c..2e8aa5e 100644
--- a/net/socket/transport_client_socket_pool_unittest.cc
+++ b/net/socket/transport_client_socket_pool_unittest.cc
@@ -991,6 +991,40 @@
   EXPECT_EQ(0, pool_->IdleSocketCount());
 }
 
+TEST_F(TransportClientSocketPoolTest, SSLCertError) {
+  StaticSocketDataProvider data;
+  tagging_client_socket_factory_.AddSocketDataProvider(&data);
+  SSLSocketDataProvider ssl(ASYNC, ERR_CERT_COMMON_NAME_INVALID);
+  tagging_client_socket_factory_.AddSSLSocketDataProvider(&ssl);
+
+  const HostPortPair kHostPortPair("ssl.server.test", 443);
+
+  scoped_refptr<TransportSocketParams> tcp_params =
+      base::MakeRefCounted<TransportSocketParams>(
+          kHostPortPair, false /* disable_resolver_cache */,
+          OnHostResolutionCallback());
+  scoped_refptr<SSLSocketParams> params(
+      new SSLSocketParams(tcp_params, nullptr, nullptr, kHostPortPair,
+                          GetSSLConfig(), PRIVACY_MODE_DISABLED));
+
+  ClientSocketHandle handle;
+  TestCompletionCallback callback;
+  int rv = handle.Init(
+      "group_name",
+      TransportClientSocketPool::SocketParams::CreateFromSSLSocketParams(
+          params),
+      MEDIUM, SocketTag(), ClientSocketPool::RespectLimits::ENABLED,
+      callback.callback(), ClientSocketPool::ProxyAuthCallback(),
+      tagging_pool_.get(), NetLogWithSource());
+  EXPECT_THAT(rv, IsError(ERR_IO_PENDING));
+  EXPECT_FALSE(handle.is_initialized());
+  EXPECT_FALSE(handle.socket());
+
+  EXPECT_THAT(callback.WaitForResult(), IsError(ERR_CERT_COMMON_NAME_INVALID));
+  EXPECT_TRUE(handle.is_initialized());
+  EXPECT_TRUE(handle.socket());
+}
+
 TEST_F(TransportClientSocketPoolTest, CloseIdleSocketsOnSSLConfigChange) {
   TestCompletionCallback callback;
   ClientSocketHandle handle;
diff --git a/net/socket/udp_socket_posix.cc b/net/socket/udp_socket_posix.cc
index 558135f..bbbe297 100644
--- a/net/socket/udp_socket_posix.cc
+++ b/net/socket/udp_socket_posix.cc
@@ -938,6 +938,13 @@
   if (multicast_interface_ != 0) {
     switch (addr_family_) {
       case AF_INET: {
+#if defined(OS_FUCHSIA)
+        // setsockopt(IP_MULTICAST_IF) is broken on Fuchsia.
+        // TODO(https://crbug.com/938101) Remove ifdef once the bug is fixed
+        // upstream.
+        return OK;
+#endif
+
 #if defined(OS_MACOSX)
         ip_mreq mreq = {};
         int error = GetIPv4AddressFromIndex(socket_, multicast_interface_,
@@ -951,15 +958,6 @@
 #endif  //  !defined(OS_MACOSX)
         int rv = setsockopt(socket_, IPPROTO_IP, IP_MULTICAST_IF,
                             reinterpret_cast<const char*>(&mreq), sizeof(mreq));
-#if defined(OS_FUCHSIA)
-        // Remove this workaround once IP_MULTICAST_IF is implemented.
-        // See https://crbug.com/924792
-        if (rv && errno == EOPNOTSUPP) {
-          LOG(WARNING)
-              << "IP_MULTICAST_IF is not supported. Proceeding anyway.";
-          rv = 0;
-        }
-#endif  // !defined(OS_FUCHSIA)
         if (rv)
           return MapSystemError(errno);
         break;
diff --git a/net/socket/websocket_transport_client_socket_pool.cc b/net/socket/websocket_transport_client_socket_pool.cc
index 31b123fb..28001117 100644
--- a/net/socket/websocket_transport_client_socket_pool.cc
+++ b/net/socket/websocket_transport_client_socket_pool.cc
@@ -287,8 +287,7 @@
 std::unique_ptr<base::DictionaryValue>
 WebSocketTransportClientSocketPool::GetInfoAsValue(
     const std::string& name,
-    const std::string& type,
-    bool include_nested_pools) const {
+    const std::string& type) const {
   std::unique_ptr<base::DictionaryValue> dict(new base::DictionaryValue());
   dict->SetString("name", name);
   dict->SetString("type", type);
diff --git a/net/socket/websocket_transport_client_socket_pool.h b/net/socket/websocket_transport_client_socket_pool.h
index af0f1e5..a740386 100644
--- a/net/socket/websocket_transport_client_socket_pool.h
+++ b/net/socket/websocket_transport_client_socket_pool.h
@@ -103,8 +103,7 @@
                          const ClientSocketHandle* handle) const override;
   std::unique_ptr<base::DictionaryValue> GetInfoAsValue(
       const std::string& name,
-      const std::string& type,
-      bool include_nested_pools) const override;
+      const std::string& type) const override;
 
   // HigherLayeredPool implementation.
   bool IsStalled() const override;
diff --git a/net/spdy/spdy_proxy_client_socket_unittest.cc b/net/spdy/spdy_proxy_client_socket_unittest.cc
index dc6ab1d..6a6171e 100644
--- a/net/spdy/spdy_proxy_client_socket_unittest.cc
+++ b/net/spdy/spdy_proxy_client_socket_unittest.cc
@@ -281,7 +281,8 @@
       spdy_stream, user_agent_, endpoint_host_port_pair_, net_log_.bound(),
       new HttpAuthController(
           HttpAuth::AUTH_PROXY, GURL("https://" + proxy_host_port_.ToString()),
-          session_->http_auth_cache(), session_->http_auth_handler_factory()));
+          session_->http_auth_cache(), session_->http_auth_handler_factory(),
+          session_->host_resolver()));
 }
 
 scoped_refptr<IOBufferWithSize> SpdyProxyClientSocketTest::CreateBuffer(
diff --git a/net/spdy/spdy_session_fuzzer.cc b/net/spdy/spdy_session_fuzzer.cc
index 8298d707..4b920299 100644
--- a/net/spdy/spdy_session_fuzzer.cc
+++ b/net/spdy/spdy_session_fuzzer.cc
@@ -12,7 +12,6 @@
 #include "net/cert/x509_certificate.h"
 #include "net/log/net_log_source.h"
 #include "net/log/test_net_log.h"
-#include "net/socket/client_socket_handle.h"
 #include "net/socket/fuzzed_socket_factory.h"
 #include "net/socket/socket_tag.h"
 #include "net/socket/socket_test_util.h"
@@ -65,12 +64,6 @@
   void AddSSLSocketDataProvider(SSLSocketDataProvider* socket);
 
   std::unique_ptr<SSLClientSocket> CreateSSLClientSocket(
-      std::unique_ptr<ClientSocketHandle> transport_socket,
-      const HostPortPair& host_and_port,
-      const SSLConfig& ssl_config,
-      const SSLClientSocketContext& context) override;
-
-  std::unique_ptr<SSLClientSocket> CreateSSLClientSocket(
       std::unique_ptr<StreamSocket> nested_socket,
       const HostPortPair& host_and_port,
       const SSLConfig& ssl_config,
@@ -91,17 +84,6 @@
 
 std::unique_ptr<SSLClientSocket>
 FuzzedSocketFactoryWithMockSSLData::CreateSSLClientSocket(
-    std::unique_ptr<ClientSocketHandle> transport_socket,
-    const HostPortPair& host_and_port,
-    const SSLConfig& ssl_config,
-    const SSLClientSocketContext& context) {
-  return std::make_unique<MockSSLClientSocket>(std::move(transport_socket),
-                                               host_and_port, ssl_config,
-                                               mock_ssl_data_.GetNext());
-}
-
-std::unique_ptr<SSLClientSocket>
-FuzzedSocketFactoryWithMockSSLData::CreateSSLClientSocket(
     std::unique_ptr<StreamSocket> nested_socket,
     const HostPortPair& host_and_port,
     const SSLConfig& ssl_config,
diff --git a/net/spdy/spdy_session_unittest.cc b/net/spdy/spdy_session_unittest.cc
index 3b13c99..0b86dc3 100644
--- a/net/spdy/spdy_session_unittest.cc
+++ b/net/spdy/spdy_session_unittest.cc
@@ -3535,9 +3535,8 @@
 
   CreateNetworkSession();
 
-  TransportClientSocketPool* pool =
-      http_session_->GetTransportSocketPool(
-          HttpNetworkSession::NORMAL_SOCKET_POOL);
+  TransportClientSocketPool* pool = http_session_->GetSocketPool(
+      HttpNetworkSession::NORMAL_SOCKET_POOL, ProxyServer::Direct());
 
   // Create an idle SPDY session.
   CreateSpdySession();
@@ -3597,9 +3596,8 @@
 
   CreateNetworkSession();
 
-  TransportClientSocketPool* pool =
-      http_session_->GetTransportSocketPool(
-          HttpNetworkSession::NORMAL_SOCKET_POOL);
+  TransportClientSocketPool* pool = http_session_->GetSocketPool(
+      HttpNetworkSession::NORMAL_SOCKET_POOL, ProxyServer::Direct());
 
   // Create an idle SPDY session.
   SpdySessionKey key1(HostPortPair("www.example.org", 80),
@@ -3684,9 +3682,8 @@
 
   CreateNetworkSession();
 
-  TransportClientSocketPool* pool =
-      http_session_->GetTransportSocketPool(
-          HttpNetworkSession::NORMAL_SOCKET_POOL);
+  TransportClientSocketPool* pool = http_session_->GetSocketPool(
+      HttpNetworkSession::NORMAL_SOCKET_POOL, ProxyServer::Direct());
 
   // Create a SPDY session.
   CreateSpdySession();
diff --git a/net/spdy/spdy_test_util_common.cc b/net/spdy/spdy_test_util_common.cc
index dc60022..570805fa 100644
--- a/net/spdy/spdy_test_util_common.cc
+++ b/net/spdy/spdy_test_util_common.cc
@@ -329,8 +329,7 @@
       proxy_resolution_service(std::move(proxy_resolution_service)),
       ssl_config_service(std::make_unique<SSLConfigServiceDefaults>()),
       socket_factory(std::make_unique<MockClientSocketFactory>()),
-      http_auth_handler_factory(
-          HttpAuthHandlerFactory::CreateDefault(host_resolver.get())),
+      http_auth_handler_factory(HttpAuthHandlerFactory::CreateDefault()),
       http_server_properties(std::make_unique<HttpServerPropertiesImpl>()),
       enable_ip_pooling(true),
       enable_ping(false),
@@ -437,7 +436,7 @@
       std::make_unique<DoNothingCTVerifier>());
   storage_.set_ssl_config_service(std::make_unique<SSLConfigServiceDefaults>());
   storage_.set_http_auth_handler_factory(
-      HttpAuthHandlerFactory::CreateDefault(host_resolver()));
+      HttpAuthHandlerFactory::CreateDefault());
   storage_.set_http_server_properties(
       std::make_unique<HttpServerPropertiesImpl>());
   storage_.set_job_factory(std::make_unique<URLRequestJobFactoryImpl>());
@@ -504,8 +503,8 @@
           ssl_params),
       MEDIUM, key.socket_tag(), ClientSocketPool::RespectLimits::ENABLED,
       callback.callback(), ClientSocketPool::ProxyAuthCallback(),
-      http_session->GetTransportSocketPool(
-          HttpNetworkSession::NORMAL_SOCKET_POOL),
+      http_session->GetSocketPool(HttpNetworkSession::NORMAL_SOCKET_POOL,
+                                  ProxyServer::Direct()),
       net_log);
   rv = callback.GetResult(rv);
   EXPECT_THAT(rv, IsOk());
diff --git a/net/ssl/client_cert_store_mac.cc b/net/ssl/client_cert_store_mac.cc
index c7ab20a..a64f448 100644
--- a/net/ssl/client_cert_store_mac.cc
+++ b/net/ssl/client_cert_store_mac.cc
@@ -11,6 +11,7 @@
 #include <Security/Security.h>
 
 #include <algorithm>
+#include <functional>
 #include <memory>
 #include <string>
 #include <utility>
@@ -399,9 +400,8 @@
   if (base::PostTaskAndReplyWithResult(
           GetSSLPlatformKeyTaskRunner().get(), FROM_HERE,
           // Caller is responsible for keeping the |request| alive
-          // until the callback is run, so ConstRef is safe.
-          base::Bind(&GetClientCertsOnBackgroundThread,
-                     base::ConstRef(request)),
+          // until the callback is run, so std::cref is safe.
+          base::Bind(&GetClientCertsOnBackgroundThread, std::cref(request)),
           callback)) {
     return;
   }
diff --git a/net/ssl/client_cert_store_win.cc b/net/ssl/client_cert_store_win.cc
index 2fcd5020..6e3845d 100644
--- a/net/ssl/client_cert_store_win.cc
+++ b/net/ssl/client_cert_store_win.cc
@@ -5,6 +5,7 @@
 #include "net/ssl/client_cert_store_win.h"
 
 #include <algorithm>
+#include <functional>
 #include <memory>
 #include <string>
 
@@ -239,9 +240,9 @@
   if (base::PostTaskAndReplyWithResult(
           GetSSLPlatformKeyTaskRunner().get(), FROM_HERE,
           // Caller is responsible for keeping the |request| alive
-          // until the callback is run, so ConstRef is safe.
+          // until the callback is run, so std::cref is safe.
           base::Bind(&ClientCertStoreWin::GetClientCertsWithMyCertStore,
-                     base::ConstRef(request)),
+                     std::cref(request)),
           callback)) {
     return;
   }
diff --git a/net/ssl/default_channel_id_store_unittest.cc b/net/ssl/default_channel_id_store_unittest.cc
index 8cb947f46..0ec71ced 100644
--- a/net/ssl/default_channel_id_store_unittest.cc
+++ b/net/ssl/default_channel_id_store_unittest.cc
@@ -268,9 +268,8 @@
   // Whitelist deletion.
   int deletions_finished = 0;
   store.DeleteForDomainsCreatedBetween(
-      base::Bind(&DomainEquals, base::ConstRef(std::string("verisign.com"))),
-      base::Time(), base::Time(),
-      base::Bind(&CallCounter, &deletions_finished));
+      base::Bind(&DomainEquals, std::string("verisign.com")), base::Time(),
+      base::Time(), base::Bind(&CallCounter, &deletions_finished));
   ASSERT_EQ(1, deletions_finished);
   EXPECT_EQ(2u, store.GetChannelIDCount());
   ChannelIDStore::ChannelIDList channel_ids;
@@ -280,9 +279,8 @@
 
   // Blacklist deletion.
   store.DeleteForDomainsCreatedBetween(
-      base::Bind(&DomainNotEquals, base::ConstRef(std::string("google.com"))),
-      base::Time(), base::Time(),
-      base::Bind(&CallCounter, &deletions_finished));
+      base::Bind(&DomainNotEquals, std::string("google.com")), base::Time(),
+      base::Time(), base::Bind(&CallCounter, &deletions_finished));
   ASSERT_EQ(2, deletions_finished);
   EXPECT_EQ(1u, store.GetChannelIDCount());
   store.GetAllChannelIDs(base::Bind(GetAllCallback, &channel_ids));
diff --git a/net/third_party/quic/core/chlo_extractor_test.cc b/net/third_party/quic/core/chlo_extractor_test.cc
index e9cb2f87..c183f1f05 100644
--- a/net/third_party/quic/core/chlo_extractor_test.cc
+++ b/net/third_party/quic/core/chlo_extractor_test.cc
@@ -47,7 +47,7 @@
  public:
   ChloExtractorTest() {
     header_.destination_connection_id = TestConnectionId();
-    header_.destination_connection_id_length = PACKET_8BYTE_CONNECTION_ID;
+    header_.destination_connection_id_included = CONNECTION_ID_PRESENT;
     header_.version_flag = true;
     header_.version = AllSupportedVersions().front();
     header_.reset_flag = false;
diff --git a/net/third_party/quic/core/crypto/crypto_secret_boxer.cc b/net/third_party/quic/core/crypto/crypto_secret_boxer.cc
index 1f3bd7e..df8a38f 100644
--- a/net/third_party/quic/core/crypto/crypto_secret_boxer.cc
+++ b/net/third_party/quic/core/crypto/crypto_secret_boxer.cc
@@ -2,25 +2,28 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+#include <cstdint>
+
 #include "net/third_party/quic/core/crypto/crypto_secret_boxer.h"
 
-#include <memory>
-
-#include "base/logging.h"
-#include "base/strings/string_util.h"
-#include "net/third_party/quic/core/crypto/quic_decrypter.h"
-#include "net/third_party/quic/core/crypto/quic_encrypter.h"
 #include "net/third_party/quic/core/crypto/quic_random.h"
+#include "net/third_party/quic/platform/api/quic_logging.h"
+#include "net/third_party/quic/platform/api/quic_string.h"
 #include "third_party/boringssl/src/include/openssl/aead.h"
-#include "third_party/boringssl/src/include/openssl/crypto.h"
+#include "third_party/boringssl/src/include/openssl/err.h"
 
 namespace quic {
 
-// Defined kKeySize for GetKeySize() and SetKey().
-static const size_t kKeySize = 16;
+// kSIVNonceSize contains the number of bytes of nonce in each AES-GCM-SIV box.
+// AES-GCM-SIV takes a 12-byte nonce and, since the messages are so small, each
+// key is good for more than 2^64 source-address tokens. See table 1 of
+// https://eprint.iacr.org/2017/168.pdf
+static const size_t kSIVNonceSize = 12;
 
-// kBoxNonceSize contains the number of bytes of nonce that we use in each box.
-static const size_t kBoxNonceSize = 12;
+// AES-GCM-SIV comes in AES-128 and AES-256 flavours. The AES-256 version is
+// used here so that the key size matches the 256-bit XSalsa20 keys that we
+// used to use.
+static const size_t kBoxKeySize = 32;
 
 struct CryptoSecretBoxer::State {
   // ctxs are the initialised AEAD contexts. These objects contain the
@@ -28,19 +31,17 @@
   std::vector<bssl::UniquePtr<EVP_AEAD_CTX>> ctxs;
 };
 
-CryptoSecretBoxer::CryptoSecretBoxer() {
-  CRYPTO_library_init();
-}
+CryptoSecretBoxer::CryptoSecretBoxer() {}
 
 CryptoSecretBoxer::~CryptoSecretBoxer() {}
 
 // static
 size_t CryptoSecretBoxer::GetKeySize() {
-  return kKeySize;
+  return kBoxKeySize;
 }
 
-// kAEAD is the AEAD used for boxing: AES-128-GCM-SIV.
-static const EVP_AEAD* (*const kAEAD)() = EVP_aead_aes_128_gcm_siv;
+// kAEAD is the AEAD used for boxing: AES-256-GCM-SIV.
+static const EVP_AEAD* (*const kAEAD)() = EVP_aead_aes_256_gcm_siv;
 
 void CryptoSecretBoxer::SetKeys(const std::vector<QuicString>& keys) {
   DCHECK(!keys.empty());
@@ -48,11 +49,12 @@
   std::unique_ptr<State> new_state(new State);
 
   for (const QuicString& key : keys) {
-    DCHECK_EQ(kKeySize, key.size());
+    DCHECK_EQ(kBoxKeySize, key.size());
     bssl::UniquePtr<EVP_AEAD_CTX> ctx(
         EVP_AEAD_CTX_new(aead, reinterpret_cast<const uint8_t*>(key.data()),
                          key.size(), EVP_AEAD_DEFAULT_TAG_LENGTH));
     if (!ctx) {
+      ERR_clear_error();
       LOG(DFATAL) << "EVP_AEAD_CTX_init failed";
       return;
     }
@@ -71,62 +73,71 @@
   //   n bytes of ciphertext
   //   16 bytes of authenticator
   size_t out_len =
-      kBoxNonceSize + plaintext.size() + EVP_AEAD_max_overhead(kAEAD());
+      kSIVNonceSize + plaintext.size() + EVP_AEAD_max_overhead(kAEAD());
 
   QuicString ret;
-  uint8_t* out = reinterpret_cast<uint8_t*>(base::WriteInto(&ret, out_len + 1));
+  ret.resize(out_len);
+  uint8_t* out = reinterpret_cast<uint8_t*>(const_cast<char*>(ret.data()));
 
-  // Write kBoxNonceSize bytes of random nonce to the beginning of the output
+  // Write kSIVNonceSize bytes of random nonce to the beginning of the output
   // buffer.
-  rand->RandBytes(out, kBoxNonceSize);
+  rand->RandBytes(out, kSIVNonceSize);
   const uint8_t* const nonce = out;
-  out += kBoxNonceSize;
-  out_len -= kBoxNonceSize;
+  out += kSIVNonceSize;
+  out_len -= kSIVNonceSize;
 
   size_t bytes_written;
   {
     QuicReaderMutexLock l(&lock_);
     if (!EVP_AEAD_CTX_seal(state_->ctxs[0].get(), out, &bytes_written, out_len,
-                           nonce, kBoxNonceSize,
+                           nonce, kSIVNonceSize,
                            reinterpret_cast<const uint8_t*>(plaintext.data()),
                            plaintext.size(), nullptr, 0)) {
+      ERR_clear_error();
       LOG(DFATAL) << "EVP_AEAD_CTX_seal failed";
       return "";
     }
   }
 
   DCHECK_EQ(out_len, bytes_written);
-
   return ret;
 }
 
 bool CryptoSecretBoxer::Unbox(QuicStringPiece in_ciphertext,
                               QuicString* out_storage,
                               QuicStringPiece* out) const {
-  if (in_ciphertext.size() <= kBoxNonceSize) {
+  if (in_ciphertext.size() < kSIVNonceSize) {
     return false;
   }
 
   const uint8_t* const nonce =
       reinterpret_cast<const uint8_t*>(in_ciphertext.data());
-  const uint8_t* const ciphertext = nonce + kBoxNonceSize;
-  const size_t ciphertext_len = in_ciphertext.size() - kBoxNonceSize;
+  const uint8_t* const ciphertext = nonce + kSIVNonceSize;
+  const size_t ciphertext_len = in_ciphertext.size() - kSIVNonceSize;
 
-  uint8_t* out_data = reinterpret_cast<uint8_t*>(
-      base::WriteInto(out_storage, ciphertext_len + 1));
+  out_storage->resize(ciphertext_len);
 
-  QuicReaderMutexLock l(&lock_);
-  for (const bssl::UniquePtr<EVP_AEAD_CTX>& ctx : state_->ctxs) {
-    size_t bytes_written;
-    if (EVP_AEAD_CTX_open(ctx.get(), out_data, &bytes_written, ciphertext_len,
-                          nonce, kBoxNonceSize, ciphertext, ciphertext_len,
-                          nullptr, 0)) {
-      *out = QuicStringPiece(out_storage->data(), bytes_written);
-      return true;
+  bool ok = false;
+  {
+    QuicReaderMutexLock l(&lock_);
+    for (const bssl::UniquePtr<EVP_AEAD_CTX>& ctx : state_->ctxs) {
+      size_t bytes_written;
+      if (EVP_AEAD_CTX_open(ctx.get(),
+                            reinterpret_cast<uint8_t*>(
+                                const_cast<char*>(out_storage->data())),
+                            &bytes_written, ciphertext_len, nonce,
+                            kSIVNonceSize, ciphertext, ciphertext_len, nullptr,
+                            0)) {
+        ok = true;
+        *out = QuicStringPiece(out_storage->data(), bytes_written);
+        break;
+      }
+
+      ERR_clear_error();
     }
   }
 
-  return false;
+  return ok;
 }
 
 }  // namespace quic
diff --git a/net/third_party/quic/core/http/end_to_end_test.cc b/net/third_party/quic/core/http/end_to_end_test.cc
index e24f689..6cc4a659 100644
--- a/net/third_party/quic/core/http/end_to_end_test.cc
+++ b/net/third_party/quic/core/http/end_to_end_test.cc
@@ -21,6 +21,7 @@
 #include "net/third_party/quic/core/quic_session.h"
 #include "net/third_party/quic/core/quic_utils.h"
 #include "net/third_party/quic/platform/api/quic_epoll.h"
+#include "net/third_party/quic/platform/api/quic_error_code_wrappers.h"
 #include "net/third_party/quic/platform/api/quic_expect_bug.h"
 #include "net/third_party/quic/platform/api/quic_flags.h"
 #include "net/third_party/quic/platform/api/quic_logging.h"
@@ -1407,7 +1408,7 @@
 }
 
 // TODO(rch): this test seems to cause net_unittests timeouts :|
-TEST_P(EndToEndTestWithTls, DISABLED_MultipleTermination) {
+TEST_P(EndToEndTestWithTls, QUIC_TEST_DISABLED_IN_CHROME(MultipleTermination)) {
   ASSERT_TRUE(Initialize());
 
   // Set the offset so we won't frame.  Otherwise when we pick up termination
@@ -1686,8 +1687,7 @@
       client_->client()->client_session()->connection();
   QuicPacketHeader* header =
       QuicConnectionPeer::GetLastHeader(client_connection);
-  EXPECT_EQ(PACKET_0BYTE_CONNECTION_ID,
-            header->destination_connection_id_length);
+  EXPECT_EQ(CONNECTION_ID_ABSENT, header->destination_connection_id_included);
 }
 
 TEST_P(EndToEndTestWithTls, 8ByteConnectionId) {
@@ -1701,11 +1701,10 @@
   QuicPacketHeader* header =
       QuicConnectionPeer::GetLastHeader(client_connection);
   if (client_connection->transport_version() > QUIC_VERSION_43) {
-    EXPECT_EQ(PACKET_0BYTE_CONNECTION_ID,
-              header->destination_connection_id_length);
+    EXPECT_EQ(CONNECTION_ID_ABSENT, header->destination_connection_id_included);
   } else {
-    EXPECT_EQ(PACKET_8BYTE_CONNECTION_ID,
-              header->destination_connection_id_length);
+    EXPECT_EQ(CONNECTION_ID_PRESENT,
+              header->destination_connection_id_included);
   }
 }
 
@@ -1721,11 +1720,10 @@
   QuicPacketHeader* header =
       QuicConnectionPeer::GetLastHeader(client_connection);
   if (client_connection->transport_version() > QUIC_VERSION_43) {
-    EXPECT_EQ(PACKET_0BYTE_CONNECTION_ID,
-              header->destination_connection_id_length);
+    EXPECT_EQ(CONNECTION_ID_ABSENT, header->destination_connection_id_included);
   } else {
-    EXPECT_EQ(PACKET_8BYTE_CONNECTION_ID,
-              header->destination_connection_id_length);
+    EXPECT_EQ(CONNECTION_ID_PRESENT,
+              header->destination_connection_id_included);
   }
 }
 
@@ -2464,8 +2462,7 @@
   std::unique_ptr<QuicEncryptedPacket> packet(ConstructEncryptedPacket(
       client_->client()->client_session()->connection()->connection_id(),
       EmptyQuicConnectionId(), false, false, 1, "At least 20 characters.",
-      PACKET_8BYTE_CONNECTION_ID, PACKET_0BYTE_CONNECTION_ID,
-      PACKET_4BYTE_PACKET_NUMBER));
+      CONNECTION_ID_PRESENT, CONNECTION_ID_ABSENT, PACKET_4BYTE_PACKET_NUMBER));
   // Damage the encrypted data.
   QuicString damaged_packet(packet->data(), packet->length());
   damaged_packet[30] ^= 0x01;
@@ -3229,8 +3226,7 @@
   EXPECT_TRUE(client_->client()->WaitForCryptoHandshakeConfirmed());
   QuicConfig* config = client_->client()->session()->config();
   EXPECT_TRUE(config->HasReceivedStatelessResetToken());
-  // TODO(dschinazi) b/120240679 - convert connection ID to UInt128
-  EXPECT_EQ(TestConnectionIdToUInt64(
+  EXPECT_EQ(QuicUtils::GenerateStatelessResetToken(
                 client_->client()->session()->connection()->connection_id()),
             config->ReceivedStatelessResetToken());
   client_->Disconnect();
@@ -3356,7 +3352,7 @@
 }
 
 // TODO: reenable once we have a way to make this run faster.
-TEST_P(EndToEndTest, DISABLED_PreSharedKeyMismatch) {
+TEST_P(EndToEndTest, QUIC_TEST_DISABLED_IN_CHROME(PreSharedKeyMismatch)) {
   client_config_.set_max_time_before_crypto_handshake(
       QuicTime::Delta::FromSeconds(1));
   client_config_.set_max_idle_time_before_crypto_handshake(
@@ -3375,7 +3371,7 @@
 }
 
 // TODO: reenable once we have a way to make this run faster.
-TEST_P(EndToEndTest, DISABLED_PreSharedKeyNoClient) {
+TEST_P(EndToEndTest, QUIC_TEST_DISABLED_IN_CHROME(PreSharedKeyNoClient)) {
   client_config_.set_max_time_before_crypto_handshake(
       QuicTime::Delta::FromSeconds(1));
   client_config_.set_max_idle_time_before_crypto_handshake(
@@ -3387,7 +3383,7 @@
 }
 
 // TODO: reenable once we have a way to make this run faster.
-TEST_P(EndToEndTest, DISABLED_PreSharedKeyNoServer) {
+TEST_P(EndToEndTest, QUIC_TEST_DISABLED_IN_CHROME(PreSharedKeyNoServer)) {
   client_config_.set_max_time_before_crypto_handshake(
       QuicTime::Delta::FromSeconds(1));
   client_config_.set_max_idle_time_before_crypto_handshake(
@@ -3700,6 +3696,123 @@
   ASSERT_EQ(nullptr, client_->latest_created_stream());
 }
 
+class BadShloPacketWriter : public QuicPacketWriterWrapper {
+ public:
+  BadShloPacketWriter() : error_returned_(false) {}
+  ~BadShloPacketWriter() override {}
+
+  WriteResult WritePacket(const char* buffer,
+                          size_t buf_len,
+                          const QuicIpAddress& self_address,
+                          const QuicSocketAddress& peer_address,
+                          quic::PerPacketOptions* options) override {
+    const WriteResult result = QuicPacketWriterWrapper::WritePacket(
+        buffer, buf_len, self_address, peer_address, options);
+    const uint8_t type_byte = buffer[0];
+    if (!error_returned_ && (type_byte & FLAGS_LONG_HEADER) &&
+        (((type_byte & 0x30) >> 4) == 1 || (type_byte & 0x7F) == 0x7C)) {
+      QUIC_DVLOG(1) << "Return write error for ZERO_RTT_PACKET";
+      error_returned_ = true;
+      return WriteResult(WRITE_STATUS_ERROR, QUIC_EMSGSIZE);
+    }
+    return result;
+  }
+
+ private:
+  bool error_returned_;
+};
+
+TEST_P(EndToEndTest, ZeroRttProtectedConnectionClose) {
+  // This test ensures ZERO_RTT_PROTECTED connection close could close a client
+  // which has switched to forward secure.
+  connect_to_server_on_initialize_ =
+      negotiated_version_.transport_version <= QUIC_VERSION_43;
+  ASSERT_TRUE(Initialize());
+  if (negotiated_version_.transport_version <= QUIC_VERSION_43) {
+    // Only runs for IETF QUIC header.
+    return;
+  }
+  server_thread_->Pause();
+  QuicDispatcher* dispatcher =
+      QuicServerPeer::GetDispatcher(server_thread_->server());
+  ASSERT_EQ(0u, dispatcher->session_map().size());
+  // Note: this writer will only used by the server connection, not the time
+  // wait list.
+  QuicDispatcherPeer::UseWriter(
+      dispatcher,
+      // This causes the first server sent ZERO_RTT_PROTECTED packet (i.e.,
+      // SHLO) to be sent, but WRITE_ERROR is returned. Such that a
+      // ZERO_RTT_PROTECTED connection close would be sent to a client with
+      // encryption level FORWARD_SECURE.
+      new BadShloPacketWriter());
+  server_thread_->Resume();
+
+  client_.reset(CreateQuicClient(client_writer_));
+  EXPECT_EQ("", client_->SendSynchronousRequest("/foo"));
+  // Verify ZERO_RTT_PROTECTED connection close is successfully processed by
+  // client which switches to FORWARD_SECURE.
+  EXPECT_EQ(QUIC_PACKET_WRITE_ERROR, client_->connection_error());
+}
+
+class BadShloPacketWriter2 : public QuicPacketWriterWrapper {
+ public:
+  BadShloPacketWriter2() : error_returned_(false) {}
+  ~BadShloPacketWriter2() override {}
+
+  WriteResult WritePacket(const char* buffer,
+                          size_t buf_len,
+                          const QuicIpAddress& self_address,
+                          const QuicSocketAddress& peer_address,
+                          quic::PerPacketOptions* options) override {
+    const uint8_t type_byte = buffer[0];
+    if ((type_byte & FLAGS_LONG_HEADER) &&
+        (((type_byte & 0x30) >> 4) == 1 || (type_byte & 0x7F) == 0x7C)) {
+      QUIC_DVLOG(1) << "Dropping ZERO_RTT_PACKET packet";
+      return WriteResult(WRITE_STATUS_OK, buf_len);
+    }
+    if (!error_returned_ && !(type_byte & FLAGS_LONG_HEADER)) {
+      QUIC_DVLOG(1) << "Return write error for short header packet";
+      error_returned_ = true;
+      return WriteResult(WRITE_STATUS_ERROR, QUIC_EMSGSIZE);
+    }
+    return QuicPacketWriterWrapper::WritePacket(buffer, buf_len, self_address,
+                                                peer_address, options);
+  }
+
+ private:
+  bool error_returned_;
+};
+
+TEST_P(EndToEndTest, ForwardSecureConnectionClose) {
+  // This test ensures ZERO_RTT_PROTECTED connection close is sent to a client
+  // which has ZERO_RTT_PROTECTED encryption level.
+  SetQuicReloadableFlag(quic_fix_termination_packets, true);
+  connect_to_server_on_initialize_ =
+      negotiated_version_.transport_version <= QUIC_VERSION_43;
+  ASSERT_TRUE(Initialize());
+  if (negotiated_version_.transport_version <= QUIC_VERSION_43) {
+    // Only runs for IETF QUIC header.
+    return;
+  }
+  server_thread_->Pause();
+  QuicDispatcher* dispatcher =
+      QuicServerPeer::GetDispatcher(server_thread_->server());
+  ASSERT_EQ(0u, dispatcher->session_map().size());
+  // Note: this writer will only used by the server connection, not the time
+  // wait list.
+  QuicDispatcherPeer::UseWriter(
+      dispatcher,
+      // This causes the all server sent ZERO_RTT_PROTECTED packets to be
+      // dropped, and first short header packet causes write error.
+      new BadShloPacketWriter2());
+  server_thread_->Resume();
+  client_.reset(CreateQuicClient(client_writer_));
+  EXPECT_EQ("", client_->SendSynchronousRequest("/foo"));
+  // Verify ZERO_RTT_PROTECTED connection close is successfully processed by
+  // client.
+  EXPECT_EQ(QUIC_PACKET_WRITE_ERROR, client_->connection_error());
+}
+
 }  // namespace
 }  // namespace test
 }  // namespace quic
diff --git a/net/third_party/quic/core/http/quic_server_session_base_test.cc b/net/third_party/quic/core/http/quic_server_session_base_test.cc
index e764a14..c79d73e 100644
--- a/net/third_party/quic/core/http/quic_server_session_base_test.cc
+++ b/net/third_party/quic/core/http/quic_server_session_base_test.cc
@@ -703,9 +703,8 @@
   std::vector<ParsedQuicVersion> packet_version_list = {GetParam()};
   std::unique_ptr<QuicEncryptedPacket> packet(ConstructEncryptedPacket(
       TestConnectionId(1), EmptyQuicConnectionId(), true, false, 1,
-      QuicString(chlo.GetSerialized().AsStringPiece()),
-      PACKET_8BYTE_CONNECTION_ID, PACKET_0BYTE_CONNECTION_ID,
-      PACKET_4BYTE_PACKET_NUMBER, &packet_version_list));
+      QuicString(chlo.GetSerialized().AsStringPiece()), CONNECTION_ID_PRESENT,
+      CONNECTION_ID_ABSENT, PACKET_4BYTE_PACKET_NUMBER, &packet_version_list));
 
   EXPECT_CALL(stream_helper_, CanAcceptClientHello(_, _, _, _, _))
       .WillOnce(testing::Return(true));
diff --git a/net/third_party/quic/core/http/quic_spdy_client_session_test.cc b/net/third_party/quic/core/http/quic_spdy_client_session_test.cc
index da397eca..1bd99d2 100644
--- a/net/third_party/quic/core/http/quic_spdy_client_session_test.cc
+++ b/net/third_party/quic/core/http/quic_spdy_client_session_test.cc
@@ -462,8 +462,8 @@
   ParsedQuicVersionVector versions = SupportedVersions(GetParam());
   std::unique_ptr<QuicEncryptedPacket> packet(ConstructEncryptedPacket(
       connection_id, EmptyQuicConnectionId(), false, false, 100, "data",
-      PACKET_0BYTE_CONNECTION_ID, PACKET_0BYTE_CONNECTION_ID,
-      PACKET_4BYTE_PACKET_NUMBER, &versions, Perspective::IS_SERVER));
+      CONNECTION_ID_ABSENT, CONNECTION_ID_ABSENT, PACKET_4BYTE_PACKET_NUMBER,
+      &versions, Perspective::IS_SERVER));
   std::unique_ptr<QuicReceivedPacket> received(
       ConstructReceivedPacket(*packet, QuicTime::Zero()));
   // Change the last byte of the encrypted data.
@@ -490,8 +490,8 @@
   ParsedQuicVersionVector versions = {GetParam()};
   std::unique_ptr<QuicEncryptedPacket> packet(ConstructMisFramedEncryptedPacket(
       connection_id, EmptyQuicConnectionId(), false, false, 100, "data",
-      PACKET_0BYTE_CONNECTION_ID, PACKET_0BYTE_CONNECTION_ID,
-      PACKET_4BYTE_PACKET_NUMBER, &versions, Perspective::IS_SERVER));
+      CONNECTION_ID_ABSENT, CONNECTION_ID_ABSENT, PACKET_4BYTE_PACKET_NUMBER,
+      &versions, Perspective::IS_SERVER));
   std::unique_ptr<QuicReceivedPacket> received(
       ConstructReceivedPacket(*packet, QuicTime::Zero()));
   EXPECT_CALL(*connection_, CloseConnection(_, _, _)).Times(1);
diff --git a/net/third_party/quic/core/http/quic_spdy_client_stream_test.cc b/net/third_party/quic/core/http/quic_spdy_client_stream_test.cc
index ebd311f..29a6ab1 100644
--- a/net/third_party/quic/core/http/quic_spdy_client_stream_test.cc
+++ b/net/third_party/quic/core/http/quic_spdy_client_stream_test.cc
@@ -166,7 +166,8 @@
   EXPECT_EQ(body_, stream_->data());
 }
 
-TEST_F(QuicSpdyClientStreamTest, DISABLED_TestFramingExtraData) {
+TEST_P(QuicSpdyClientStreamTest,
+       QUIC_TEST_DISABLED_IN_CHROME(TestFramingExtraData)) {
   QuicString large_body = "hello world!!!!!!";
 
   auto headers = AsHeaderList(headers_);
diff --git a/net/third_party/quic/core/http/quic_spdy_session_test.cc b/net/third_party/quic/core/http/quic_spdy_session_test.cc
index 8f0853fe..8d535dc 100644
--- a/net/third_party/quic/core/http/quic_spdy_session_test.cc
+++ b/net/third_party/quic/core/http/quic_spdy_session_test.cc
@@ -80,10 +80,6 @@
   }
 
   // QuicCryptoStream implementation
-  QuicLongHeaderType GetLongHeaderType(
-      QuicStreamOffset /*offset*/) const override {
-    return HANDSHAKE;
-  }
   bool encryption_established() const override {
     return encryption_established_;
   }
diff --git a/net/third_party/quic/core/quic_connection.cc b/net/third_party/quic/core/quic_connection.cc
index bd1e418..848231b9 100644
--- a/net/third_party/quic/core/quic_connection.cc
+++ b/net/third_party/quic/core/quic_connection.cc
@@ -81,6 +81,10 @@
     DCHECK(connection_->ack_frame_updated());
     QuicConnection::ScopedPacketFlusher flusher(connection_,
                                                 QuicConnection::SEND_ACK);
+    if (connection_->packet_generator().deprecate_ack_bundling_mode()) {
+      DCHECK(!connection_->GetUpdatedAckFrame().ack_frame->packets.Empty());
+      connection_->SendAck();
+    }
   }
 
  private:
@@ -332,10 +336,20 @@
       processing_ack_frame_(false),
       supports_release_time_(false),
       release_time_into_future_(QuicTime::Delta::Zero()),
-      no_version_negotiation_(supported_versions.size() == 1) {
+      no_version_negotiation_(supported_versions.size() == 1),
+      fix_termination_packets_(
+          GetQuicReloadableFlag(quic_fix_termination_packets)),
+      send_ack_when_on_can_write_(false) {
   if (ack_mode_ == ACK_DECIMATION) {
     QUIC_RELOADABLE_FLAG_COUNT(quic_enable_ack_decimation);
   }
+  if (perspective_ == Perspective::IS_SERVER &&
+      supported_versions.size() == 1) {
+    QUIC_RESTART_FLAG_COUNT(quic_no_server_conn_ver_negotiation2);
+  }
+  if (packet_generator_.deprecate_ack_bundling_mode()) {
+    QUIC_RELOADABLE_FLAG_COUNT(quic_deprecate_ack_bundling_mode);
+  }
   QUIC_DLOG(INFO) << ENDPOINT
                   << "Created connection with connection_id: " << connection_id
                   << " and version: "
@@ -424,24 +438,14 @@
   if (config.HasClientSentConnectionOption(kACKD, perspective_)) {
     ack_mode_ = ACK_DECIMATION;
   }
-  if ((!GetQuicReloadableFlag(quic_enable_ack_decimation) ||
-       GetQuicReloadableFlag(quic_keep_ack_decimation_reordering)) &&
-      config.HasClientSentConnectionOption(kAKD2, perspective_)) {
-    if (GetQuicReloadableFlag(quic_keep_ack_decimation_reordering)) {
-      QUIC_RELOADABLE_FLAG_COUNT_N(quic_keep_ack_decimation_reordering, 1, 2);
-    }
+  if (config.HasClientSentConnectionOption(kAKD2, perspective_)) {
     ack_mode_ = ACK_DECIMATION_WITH_REORDERING;
   }
   if (config.HasClientSentConnectionOption(kAKD3, perspective_)) {
     ack_mode_ = ACK_DECIMATION;
     ack_decimation_delay_ = kShortAckDecimationDelay;
   }
-  if ((!GetQuicReloadableFlag(quic_enable_ack_decimation) ||
-       GetQuicReloadableFlag(quic_keep_ack_decimation_reordering)) &&
-      config.HasClientSentConnectionOption(kAKD4, perspective_)) {
-    if (GetQuicReloadableFlag(quic_keep_ack_decimation_reordering)) {
-      QUIC_RELOADABLE_FLAG_COUNT_N(quic_keep_ack_decimation_reordering, 2, 2);
-    }
+  if (config.HasClientSentConnectionOption(kAKD4, perspective_)) {
     ack_mode_ = ACK_DECIMATION_WITH_REORDERING;
     ack_decimation_delay_ = kShortAckDecimationDelay;
   }
@@ -1463,7 +1467,11 @@
         sent_packet_manager_.unacked_packets().largest_sent_largest_acked();
     if (largest_sent_largest_acked.IsInitialized() &&
         last_header_.packet_number < largest_sent_largest_acked) {
-      ack_queued_ = true;
+      if (packet_generator_.deprecate_ack_bundling_mode()) {
+        MaybeSetAckAlarmTo(clock_->ApproximateNow());
+      } else {
+        ack_queued_ = true;
+      }
     }
   }
 
@@ -1477,7 +1485,11 @@
       if (!unlimited_ack_decimation_ &&
           num_retransmittable_packets_received_since_last_ack_sent_ >=
               kMaxRetransmittablePacketsBeforeAck) {
-        ack_queued_ = true;
+        if (packet_generator_.deprecate_ack_bundling_mode()) {
+          MaybeSetAckAlarmTo(clock_->ApproximateNow());
+        } else {
+          ack_queued_ = true;
+        }
       } else if (ShouldSetAckAlarm()) {
         // Wait for the minimum of the ack decimation delay or the delayed ack
         // time before sending an ack.
@@ -1500,7 +1512,11 @@
       // Ack with a timer or every 2 packets by default.
       if (num_retransmittable_packets_received_since_last_ack_sent_ >=
           ack_frequency_before_ack_decimation_) {
-        ack_queued_ = true;
+        if (packet_generator_.deprecate_ack_bundling_mode()) {
+          MaybeSetAckAlarmTo(clock_->ApproximateNow());
+        } else {
+          ack_queued_ = true;
+        }
       } else if (ShouldSetAckAlarm()) {
         const QuicTime approximate_now = clock_->ApproximateNow();
         if (fast_ack_after_quiescence_ &&
@@ -1521,8 +1537,6 @@
     // If there are new missing packets to report, send an ack immediately.
     if (received_packet_manager_.HasNewMissingPackets()) {
       if (ack_mode_ == ACK_DECIMATION_WITH_REORDERING) {
-        DCHECK(!GetQuicReloadableFlag(quic_enable_ack_decimation) ||
-               GetQuicReloadableFlag(quic_keep_ack_decimation_reordering));
         // Wait the minimum of an eighth min_rtt and the existing ack time.
         QuicTime ack_time =
             clock_->ApproximateNow() +
@@ -1531,7 +1545,11 @@
           ack_alarm_->Update(ack_time, QuicTime::Delta::Zero());
         }
       } else {
-        ack_queued_ = true;
+        if (packet_generator_.deprecate_ack_bundling_mode()) {
+          MaybeSetAckAlarmTo(clock_->ApproximateNow());
+        } else {
+          ack_queued_ = true;
+        }
       }
     }
 
@@ -1854,10 +1872,7 @@
 }
 
 void QuicConnection::OnBlockedWriterCanWrite() {
-  if (GetQuicRestartFlag(quic_check_blocked_writer_for_blockage)) {
-    QUIC_RESTART_FLAG_COUNT_N(quic_check_blocked_writer_for_blockage, 3, 6);
-    writer_->SetWritable();
-  }
+  writer_->SetWritable();
   OnCanWrite();
 }
 
@@ -1868,6 +1883,13 @@
   ScopedPacketFlusher flusher(this, NO_ACK);
 
   WriteQueuedPackets();
+  if (send_ack_when_on_can_write_) {
+    // Send an ACK now because either 1) we were write blocked when we last
+    // tried to send an ACK, or 2) both ack alarm and send alarm were set to go
+    // off together.
+    DCHECK(packet_generator_.deprecate_ack_bundling_mode());
+    SendAck();
+  }
   if (!session_decides_what_to_write()) {
     WritePendingRetransmissions();
   }
@@ -2139,6 +2161,25 @@
   return CanWrite(retransmittable);
 }
 
+const QuicFrames QuicConnection::MaybeBundleAckOpportunistically() {
+  DCHECK(packet_generator_.deprecate_ack_bundling_mode());
+  QuicFrames frames;
+  if (!ack_alarm_->IsSet() && stop_waiting_count_ <= 1) {
+    // No need to send an ACK.
+    return frames;
+  }
+  ResetAckStates();
+
+  QUIC_DVLOG(1) << ENDPOINT << "Bundle an ACK opportunistically";
+  frames.push_back(GetUpdatedAckFrame());
+  if (!no_stop_waiting_frames_) {
+    QuicStopWaitingFrame stop_waiting;
+    PopulateStopWaitingFrame(&stop_waiting);
+    frames.push_back(QuicFrame(stop_waiting));
+  }
+  return frames;
+}
+
 bool QuicConnection::CanWrite(HasRetransmittableData retransmittable) {
   if (!connected_) {
     return false;
@@ -2258,9 +2299,6 @@
   WriteResult result = writer_->WritePacket(
       packet->encrypted_buffer, encrypted_length, self_address().host(),
       peer_address(), per_packet_options_);
-  if (result.error_code == net::ERR_IO_PENDING) {
-    DCHECK_EQ(WRITE_STATUS_BLOCKED_DATA_BUFFERED, result.status);
-  }
 
   QUIC_HISTOGRAM_ENUM(
       "QuicConnection.WritePacketStatus", result.status,
@@ -2367,9 +2405,7 @@
 }
 
 void QuicConnection::FlushPackets() {
-  if (GetQuicRestartFlag(quic_check_blocked_writer_for_blockage) &&
-      !connected_) {
-    QUIC_RESTART_FLAG_COUNT_N(quic_check_blocked_writer_for_blockage, 5, 6);
+  if (!connected_) {
     return;
   }
 
@@ -2561,13 +2597,21 @@
 }
 
 void QuicConnection::SendAck() {
-  ack_alarm_->Cancel();
-  ack_queued_ = false;
-  stop_waiting_count_ = 0;
-  num_retransmittable_packets_received_since_last_ack_sent_ = 0;
-  num_packets_received_since_last_ack_sent_ = 0;
+  ResetAckStates();
 
-  packet_generator_.SetShouldSendAck(!no_stop_waiting_frames_);
+  if (packet_generator_.deprecate_ack_bundling_mode()) {
+    QUIC_DVLOG(1) << ENDPOINT << "Sending an ACK proactively";
+    QuicFrames frames;
+    frames.push_back(GetUpdatedAckFrame());
+    if (!no_stop_waiting_frames_) {
+      QuicStopWaitingFrame stop_waiting;
+      PopulateStopWaitingFrame(&stop_waiting);
+      frames.push_back(QuicFrame(stop_waiting));
+    }
+    send_ack_when_on_can_write_ = !packet_generator_.FlushAckFrame(frames);
+  } else {
+    packet_generator_.SetShouldSendAck(!no_stop_waiting_frames_);
+  }
   if (consecutive_num_packets_with_no_retransmittable_frames_ <
       max_consecutive_num_packets_with_no_retransmittable_frames_) {
     return;
@@ -2795,8 +2839,16 @@
                                                const QuicString& details,
                                                AckBundling ack_mode) {
   QUIC_DLOG(INFO) << ENDPOINT << "Sending connection close packet.";
+  if (fix_termination_packets_) {
+    QUIC_RELOADABLE_FLAG_COUNT(quic_fix_termination_packets);
+    SetDefaultEncryptionLevel(GetConnectionCloseEncryptionLevel());
+  }
   ClearQueuedPackets();
   ScopedPacketFlusher flusher(this, ack_mode);
+  if (packet_generator_.deprecate_ack_bundling_mode() && ack_mode == SEND_ACK &&
+      !GetUpdatedAckFrame().ack_frame->packets.Empty()) {
+    SendAck();
+  }
   QuicConnectionCloseFrame* frame = new QuicConnectionCloseFrame();
   frame->error_code = error;
   frame->error_details = details;
@@ -2814,10 +2866,7 @@
   }
 
   // If we are using a batch writer, flush packets queued in it, if any.
-  if (GetQuicRestartFlag(quic_check_blocked_writer_for_blockage)) {
-    QUIC_RESTART_FLAG_COUNT_N(quic_check_blocked_writer_for_blockage, 6, 6);
-    FlushPackets();
-  }
+  FlushPackets();
   connected_ = false;
   DCHECK(visitor_ != nullptr);
   visitor_->OnConnectionClosed(error, error_details, source);
@@ -3028,6 +3077,13 @@
   }
 }
 
+void QuicConnection::MaybeSetAckAlarmTo(QuicTime time) {
+  DCHECK(packet_generator_.deprecate_ack_bundling_mode());
+  if (!ack_alarm_->IsSet() || ack_alarm_->deadline() > time) {
+    ack_alarm_->Update(time, QuicTime::Delta::Zero());
+  }
+}
+
 QuicConnection::ScopedPacketFlusher::ScopedPacketFlusher(
     QuicConnection* connection,
     AckBundling ack_mode)
@@ -3041,6 +3097,10 @@
     flush_and_set_pending_retransmission_alarm_on_delete_ = true;
     connection->packet_generator_.AttachPacketFlusher();
   }
+  if (connection_->packet_generator_.deprecate_ack_bundling_mode()) {
+    return;
+  }
+
   // If caller wants us to include an ack, check the delayed-ack timer to see if
   // there's ack info to be sent.
   if (ShouldSendAck(ack_mode)) {
@@ -3053,6 +3113,7 @@
 
 bool QuicConnection::ScopedPacketFlusher::ShouldSendAck(
     AckBundling ack_mode) const {
+  DCHECK(!connection_->packet_generator_.deprecate_ack_bundling_mode());
   // If the ack alarm is set, make sure the ack has been updated.
   DCHECK(!connection_->ack_alarm_->IsSet() || connection_->ack_frame_updated())
       << "ack_mode:" << ack_mode;
@@ -3078,6 +3139,23 @@
   }
 
   if (flush_and_set_pending_retransmission_alarm_on_delete_) {
+    if (connection_->packet_generator_.deprecate_ack_bundling_mode() &&
+        connection_->ack_alarm_->IsSet() &&
+        connection_->ack_alarm_->deadline() <=
+            connection_->clock_->ApproximateNow()) {
+      // An ACK needs to be sent right now. This ACK did not get bundled
+      // because either there was no data to write or packets were marked as
+      // received after frames were queued in the generator.
+      if (connection_->send_alarm_->IsSet() &&
+          connection_->send_alarm_->deadline() <=
+              connection_->clock_->ApproximateNow()) {
+        // If send alarm will go off soon, let send alarm send the ACK.
+        connection_->ack_alarm_->Cancel();
+        connection_->send_ack_when_on_can_write_ = true;
+      } else {
+        connection_->SendAck();
+      }
+    }
     connection_->packet_generator_.Flush();
     connection_->FlushPackets();
     if (connection_->session_decides_what_to_write()) {
@@ -3559,10 +3637,6 @@
   packet_generator_.SetTransmissionType(type);
 }
 
-void QuicConnection::SetLongHeaderType(QuicLongHeaderType type) {
-  packet_generator_.SetLongHeaderType(type);
-}
-
 bool QuicConnection::session_decides_what_to_write() const {
   return sent_packet_manager_.session_decides_what_to_write();
 }
@@ -3579,6 +3653,14 @@
               GetQuicFlag(FLAGS_quic_pace_time_into_future_srtt_fraction)));
 }
 
+void QuicConnection::ResetAckStates() {
+  ack_alarm_->Cancel();
+  ack_queued_ = false;
+  stop_waiting_count_ = 0;
+  num_retransmittable_packets_received_since_last_ack_sent_ = 0;
+  num_packets_received_since_last_ack_sent_ = 0;
+}
+
 MessageStatus QuicConnection::SendMessage(QuicMessageId message_id,
                                           QuicMemSliceSpan message) {
   if (transport_version() <= QUIC_VERSION_44) {
@@ -3617,5 +3699,28 @@
   return true;
 }
 
+EncryptionLevel QuicConnection::GetConnectionCloseEncryptionLevel() const {
+  DCHECK(fix_termination_packets_);
+  if (perspective_ == Perspective::IS_CLIENT) {
+    return encryption_level_;
+  }
+  if (sent_packet_manager_.handshake_confirmed()) {
+    // A forward secure packet has been received.
+    QUIC_BUG_IF(encryption_level_ != ENCRYPTION_FORWARD_SECURE);
+    return ENCRYPTION_FORWARD_SECURE;
+  }
+  if (framer_.HasEncrypterOfEncryptionLevel(ENCRYPTION_ZERO_RTT)) {
+    if (encryption_level_ != ENCRYPTION_ZERO_RTT) {
+      if (transport_version() > QUIC_VERSION_43) {
+        QUIC_CODE_COUNT(quic_wrong_encryption_level_connection_close_ietf);
+      } else {
+        QUIC_CODE_COUNT(quic_wrong_encryption_level_connection_close);
+      }
+    }
+    return ENCRYPTION_ZERO_RTT;
+  }
+  return ENCRYPTION_NONE;
+}
+
 #undef ENDPOINT  // undef for jumbo builds
 }  // namespace quic
diff --git a/net/third_party/quic/core/quic_connection.h b/net/third_party/quic/core/quic_connection.h
index 63ffde9..6b730a91 100644
--- a/net/third_party/quic/core/quic_connection.h
+++ b/net/third_party/quic/core/quic_connection.h
@@ -323,6 +323,8 @@
       public QuicPacketGenerator::DelegateInterface,
       public QuicSentPacketManager::NetworkChangeVisitor {
  public:
+  // TODO(fayang): Remove this enum when deprecating
+  // quic_deprecate_ack_bundling_mode.
   enum AckBundling {
     // Send an ack if it's already queued in the connection.
     SEND_ACK_IF_QUEUED,
@@ -528,6 +530,7 @@
   // QuicPacketGenerator::DelegateInterface
   bool ShouldGeneratePacket(HasRetransmittableData retransmittable,
                             IsHandshake handshake) override;
+  const QuicFrames MaybeBundleAckOpportunistically() override;
   const QuicFrame GetUpdatedAckFrame() override;
   void PopulateStopWaitingFrame(QuicStopWaitingFrame* stop_waiting) override;
 
@@ -703,6 +706,8 @@
    public:
     // Setting |include_ack| to true ensures that an ACK frame is
     // opportunistically bundled with the first outgoing packet.
+    // TODO(fayang): Remove |ack_mode| when deprecating
+    // quic_deprecate_ack_bundling_mode.
     ScopedPacketFlusher(QuicConnection* connection, AckBundling ack_mode);
     ~ScopedPacketFlusher();
 
@@ -752,9 +757,6 @@
   // Set transmission type of next sending packets.
   void SetTransmissionType(TransmissionType type);
 
-  // Set long header type of next sending packets.
-  void SetLongHeaderType(QuicLongHeaderType type);
-
   // Tries to send |message| and returns the message status.
   virtual MessageStatus SendMessage(QuicMessageId message_id,
                                     QuicMemSliceSpan message);
@@ -926,6 +928,8 @@
 
   // Sends the connection close packet to the peer. |ack_mode| determines
   // whether ack frame will be bundled with the connection close packet.
+  // TODO(fayang): change |ack_mode| to bool |force_sending_ack| when
+  // deprecating quic_deprecate_ack_bundling_mode.
   virtual void SendConnectionClosePacket(QuicErrorCode error,
                                          const QuicString& details,
                                          AckBundling ack_mode);
@@ -1041,6 +1045,9 @@
   // |sent_packet_number| is the recently sent packet number.
   void MaybeSetMtuAlarm(QuicPacketNumber sent_packet_number);
 
+  // Sets ack alarm to |time| if ack alarm is not set or the deadline > time.
+  void MaybeSetAckAlarmTo(QuicTime time);
+
   HasRetransmittableData IsRetransmittable(const SerializedPacket& packet);
   bool IsTerminationPacket(const SerializedPacket& packet);
 
@@ -1093,10 +1100,19 @@
                                   const QuicSocketAddress& peer_address,
                                   bool is_response);
 
+  // Called when an ACK is about to send. Resets ACK related internal states,
+  // e.g., cancels ack_alarm_, resets
+  // num_retransmittable_packets_received_since_last_ack_sent_ etc.
+  void ResetAckStates();
+
   // Returns true if ack alarm is not set and there is no pending ack in the
   // generator.
   bool ShouldSetAckAlarm() const;
 
+  // Returns the encryption level the connection close packet should be sent at,
+  // which is the highest encryption level that peer can guarantee to process.
+  EncryptionLevel GetConnectionCloseEncryptionLevel() const;
+
   QuicFramer framer_;
 
   // Contents received in the current packet, especially used to identify
@@ -1214,6 +1230,8 @@
   QuicReceivedPacketManager received_packet_manager_;
 
   // Indicates whether an ack should be sent the next time we try to write.
+  // TODO(fayang): Remove ack_queued_ when deprecating
+  // quic_deprecate_ack_bundling_mode.
   bool ack_queued_;
   // How many retransmittable packets have arrived without sending an ack.
   QuicPacketCount num_retransmittable_packets_received_since_last_ack_sent_;
@@ -1430,6 +1448,16 @@
   // might send a packet with more than one PATH_CHALLENGE, so all need to be
   // saved and responded to.
   QuicDeque<QuicPathFrameBuffer> received_path_challenge_payloads_;
+
+  // Latched value of quic_fix_termination_packets.
+  const bool fix_termination_packets_;
+
+  // Indicates whether an ACK needs to be sent in OnCanWrite(). Only used when
+  // deprecate_ack_bundling_mode is true.
+  // TODO(fayang): Remove this when ACK sending logic is moved to received
+  // packet manager, and an ACK timeout would be used to record when an ACK
+  // needs to be sent.
+  bool send_ack_when_on_can_write_;
 };
 
 }  // namespace quic
diff --git a/net/third_party/quic/core/quic_connection_id.h b/net/third_party/quic/core/quic_connection_id.h
index 9120e0f..015f78a 100644
--- a/net/third_party/quic/core/quic_connection_id.h
+++ b/net/third_party/quic/core/quic_connection_id.h
@@ -16,6 +16,13 @@
   PACKET_8BYTE_CONNECTION_ID = 8,
 };
 
+// This is a property of QUIC headers, it indicates whether the connection ID
+// should actually be sent over the wire (or was sent on received packets).
+enum QuicConnectionIdIncluded : uint8_t {
+  CONNECTION_ID_PRESENT = 1,
+  CONNECTION_ID_ABSENT = 2,
+};
+
 // Connection IDs can be 0-18 bytes per IETF specifications.
 const uint8_t kQuicMaxConnectionIdLength = 18;
 
diff --git a/net/third_party/quic/core/quic_connection_test.cc b/net/third_party/quic/core/quic_connection_test.cc
index 1440188d..1203b71 100644
--- a/net/third_party/quic/core/quic_connection_test.cc
+++ b/net/third_party/quic/core/quic_connection_test.cc
@@ -15,6 +15,7 @@
 #include "net/third_party/quic/core/crypto/null_encrypter.h"
 #include "net/third_party/quic/core/crypto/quic_decrypter.h"
 #include "net/third_party/quic/core/crypto/quic_encrypter.h"
+#include "net/third_party/quic/core/quic_connection_id.h"
 #include "net/third_party/quic/core/quic_packets.h"
 #include "net/third_party/quic/core/quic_simple_buffer_allocator.h"
 #include "net/third_party/quic/core/quic_types.h"
@@ -860,7 +861,7 @@
                 3,
                 QuicStringPiece(data2)),
         packet_number_length_(PACKET_4BYTE_PACKET_NUMBER),
-        connection_id_length_(PACKET_8BYTE_CONNECTION_ID),
+        connection_id_included_(CONNECTION_ID_PRESENT),
         notifier_(&connection_) {
     SetQuicFlag(&FLAGS_quic_supports_tls_handshake, true);
     connection_.set_defer_send_in_response_to_packets(GetParam().ack_response ==
@@ -1022,10 +1023,10 @@
     QuicPacketHeader header;
     header.destination_connection_id = connection_id_;
     header.packet_number_length = packet_number_length_;
-    header.destination_connection_id_length = connection_id_length_;
+    header.destination_connection_id_included = connection_id_included_;
     if (peer_framer_.transport_version() > QUIC_VERSION_43 &&
         peer_framer_.perspective() == Perspective::IS_SERVER) {
-      header.destination_connection_id_length = PACKET_0BYTE_CONNECTION_ID;
+      header.destination_connection_id_included = CONNECTION_ID_ABSENT;
     }
     header.packet_number = QuicPacketNumber(number);
     QuicFrames frames;
@@ -1175,10 +1176,10 @@
     // is created by peer_framer.
     header.destination_connection_id = connection_id_;
     header.packet_number_length = packet_number_length_;
-    header.destination_connection_id_length = connection_id_length_;
+    header.destination_connection_id_included = connection_id_included_;
     if (peer_framer_.transport_version() > QUIC_VERSION_43 &&
         peer_framer_.perspective() == Perspective::IS_SERVER) {
-      header.destination_connection_id_length = PACKET_0BYTE_CONNECTION_ID;
+      header.destination_connection_id_included = CONNECTION_ID_ABSENT;
     }
     header.packet_number = QuicPacketNumber(number);
 
@@ -1210,7 +1211,7 @@
     header.packet_number = QuicPacketNumber(number);
     if (peer_framer_.transport_version() > QUIC_VERSION_43 &&
         peer_framer_.perspective() == Perspective::IS_SERVER) {
-      header.destination_connection_id_length = PACKET_0BYTE_CONNECTION_ID;
+      header.destination_connection_id_included = CONNECTION_ID_ABSENT;
     }
 
     QuicConnectionCloseFrame qccf;
@@ -1344,7 +1345,7 @@
   QuicAckFrame ack_;
   QuicStopWaitingFrame stop_waiting_;
   QuicPacketNumberLength packet_number_length_;
-  QuicConnectionIdLength connection_id_length_;
+  QuicConnectionIdIncluded connection_id_included_;
 
   SimpleSessionNotifier notifier_;
 };
@@ -2339,11 +2340,10 @@
 }
 
 TEST_P(QuicConnectionTest, AckDecimationReducesAcks) {
-  if (GetQuicReloadableFlag(quic_enable_ack_decimation) &&
-      !GetQuicReloadableFlag(quic_keep_ack_decimation_reordering)) {
-    return;
-  }
-
+  const size_t kMinRttMs = 40;
+  RttStats* rtt_stats = const_cast<RttStats*>(manager_->GetRttStats());
+  rtt_stats->UpdateRtt(QuicTime::Delta::FromMilliseconds(kMinRttMs),
+                       QuicTime::Delta::Zero(), QuicTime::Zero());
   EXPECT_CALL(visitor_, OnAckNeedsRetransmittableFrame()).Times(AnyNumber());
 
   QuicConnectionPeer::SetAckMode(
@@ -3369,11 +3369,7 @@
   EXPECT_CALL(visitor_, OnConnectionClosed(QUIC_PEER_GOING_AWAY, _,
                                            ConnectionCloseSource::FROM_SELF));
 
-  if (GetQuicRestartFlag(quic_check_blocked_writer_for_blockage)) {
-    EXPECT_CALL(visitor_, OnWriteBlocked()).Times(0);
-  } else {
-    EXPECT_CALL(visitor_, OnWriteBlocked()).Times(1);
-  }
+  EXPECT_CALL(visitor_, OnWriteBlocked()).Times(0);
 
   {
     QuicConnection::ScopedPacketFlusher flusher(&connection_,
@@ -5461,10 +5457,6 @@
 }
 
 TEST_P(QuicConnectionTest, SendDelayedAckDecimationWithReordering) {
-  if (GetQuicReloadableFlag(quic_enable_ack_decimation) &&
-      !GetQuicReloadableFlag(quic_keep_ack_decimation_reordering)) {
-    return;
-  }
   EXPECT_CALL(visitor_, OnAckNeedsRetransmittableFrame()).Times(AnyNumber());
   QuicConnectionPeer::SetAckMode(
       &connection_, QuicConnection::ACK_DECIMATION_WITH_REORDERING);
@@ -5530,10 +5522,6 @@
 }
 
 TEST_P(QuicConnectionTest, SendDelayedAckDecimationWithLargeReordering) {
-  if (GetQuicReloadableFlag(quic_enable_ack_decimation) &&
-      !GetQuicReloadableFlag(quic_keep_ack_decimation_reordering)) {
-    return;
-  }
   EXPECT_CALL(visitor_, OnAckNeedsRetransmittableFrame()).Times(AnyNumber());
   QuicConnectionPeer::SetAckMode(
       &connection_, QuicConnection::ACK_DECIMATION_WITH_REORDERING);
@@ -5618,10 +5606,6 @@
 }
 
 TEST_P(QuicConnectionTest, SendDelayedAckDecimationWithReorderingEighthRtt) {
-  if (GetQuicReloadableFlag(quic_enable_ack_decimation) &&
-      !GetQuicReloadableFlag(quic_keep_ack_decimation_reordering)) {
-    return;
-  }
   EXPECT_CALL(visitor_, OnAckNeedsRetransmittableFrame()).Times(AnyNumber());
   QuicConnectionPeer::SetAckMode(
       &connection_, QuicConnection::ACK_DECIMATION_WITH_REORDERING);
@@ -5691,10 +5675,6 @@
 
 TEST_P(QuicConnectionTest,
        SendDelayedAckDecimationWithLargeReorderingEighthRtt) {
-  if (GetQuicReloadableFlag(quic_enable_ack_decimation) &&
-      !GetQuicReloadableFlag(quic_keep_ack_decimation_reordering)) {
-    return;
-  }
   EXPECT_CALL(visitor_, OnAckNeedsRetransmittableFrame()).Times(AnyNumber());
   QuicConnectionPeer::SetAckMode(
       &connection_, QuicConnection::ACK_DECIMATION_WITH_REORDERING);
@@ -6422,7 +6402,7 @@
   // NEGOTIATED_VERSION state and tell the packet creator to StopSendingVersion.
   QuicPacketHeader header;
   header.destination_connection_id = connection_id_;
-  header.destination_connection_id_length = PACKET_0BYTE_CONNECTION_ID;
+  header.destination_connection_id_included = CONNECTION_ID_ABSENT;
   header.packet_number = QuicPacketNumber(12);
   header.version_flag = false;
   QuicFrames frames;
@@ -6521,7 +6501,7 @@
   QuicPacketHeader header;
   header.destination_connection_id = connection_id_;
   if (peer_framer_.transport_version() > QUIC_VERSION_43) {
-    header.destination_connection_id_length = PACKET_0BYTE_CONNECTION_ID;
+    header.destination_connection_id_included = CONNECTION_ID_ABSENT;
   }
   header.packet_number = QuicPacketNumber(1);
   header.version_flag = false;
@@ -7338,8 +7318,8 @@
   EXPECT_EQ(Perspective::IS_CLIENT, connection_.perspective());
   EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(1);
   connection_.SendStreamDataWithString(3, "foo", 0, NO_FIN);
-  EXPECT_EQ(PACKET_8BYTE_CONNECTION_ID,
-            writer_->last_packet_header().destination_connection_id_length);
+  EXPECT_EQ(CONNECTION_ID_PRESENT,
+            writer_->last_packet_header().destination_connection_id_included);
 
   EXPECT_CALL(*send_algorithm_, SetFromConfig(_, _));
   QuicConfig config;
@@ -7349,8 +7329,8 @@
   EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(1);
   connection_.SendStreamDataWithString(3, "bar", 3, NO_FIN);
   // Verify connection id is still sent in the packet.
-  EXPECT_EQ(PACKET_8BYTE_CONNECTION_ID,
-            writer_->last_packet_header().destination_connection_id_length);
+  EXPECT_EQ(CONNECTION_ID_PRESENT,
+            writer_->last_packet_header().destination_connection_id_included);
 }
 
 TEST_P(QuicConnectionTest, SendProbingRetransmissions) {
diff --git a/net/third_party/quic/core/quic_crypto_client_handshaker.cc b/net/third_party/quic/core/quic_crypto_client_handshaker.cc
index d3c697d2..184f83d6 100644
--- a/net/third_party/quic/core/quic_crypto_client_handshaker.cc
+++ b/net/third_party/quic/core/quic_crypto_client_handshaker.cc
@@ -161,11 +161,6 @@
   return channel_id_source_callback_run_;
 }
 
-QuicLongHeaderType QuicCryptoClientHandshaker::GetLongHeaderType(
-    QuicStreamOffset offset) const {
-  return offset == 0 ? INITIAL : HANDSHAKE;
-}
-
 QuicString QuicCryptoClientHandshaker::chlo_hash() const {
   return chlo_hash_;
 }
diff --git a/net/third_party/quic/core/quic_crypto_client_handshaker.h b/net/third_party/quic/core/quic_crypto_client_handshaker.h
index 26c68a47..bad6c62 100644
--- a/net/third_party/quic/core/quic_crypto_client_handshaker.h
+++ b/net/third_party/quic/core/quic_crypto_client_handshaker.h
@@ -40,7 +40,6 @@
   int num_scup_messages_received() const override;
   bool WasChannelIDSent() const override;
   bool WasChannelIDSourceCallbackRun() const override;
-  QuicLongHeaderType GetLongHeaderType(QuicStreamOffset offset) const override;
   QuicString chlo_hash() const override;
   bool encryption_established() const override;
   bool handshake_confirmed() const override;
diff --git a/net/third_party/quic/core/quic_crypto_client_stream.cc b/net/third_party/quic/core/quic_crypto_client_stream.cc
index b3e8680a..e946e72 100644
--- a/net/third_party/quic/core/quic_crypto_client_stream.cc
+++ b/net/third_party/quic/core/quic_crypto_client_stream.cc
@@ -92,11 +92,6 @@
   return handshaker_->WasChannelIDSourceCallbackRun();
 }
 
-QuicLongHeaderType QuicCryptoClientStream::GetLongHeaderType(
-    QuicStreamOffset offset) const {
-  return handshaker_->GetLongHeaderType(offset);
-}
-
 QuicString QuicCryptoClientStream::chlo_hash() const {
   return handshaker_->chlo_hash();
 }
diff --git a/net/third_party/quic/core/quic_crypto_client_stream.h b/net/third_party/quic/core/quic_crypto_client_stream.h
index 215b6c2b..8cf889f 100644
--- a/net/third_party/quic/core/quic_crypto_client_stream.h
+++ b/net/third_party/quic/core/quic_crypto_client_stream.h
@@ -90,10 +90,6 @@
     // ChannelIDSource operated asynchronously. Intended for testing.
     virtual bool WasChannelIDSourceCallbackRun() const = 0;
 
-    // Returns long header type for next sending handshake message.
-    virtual QuicLongHeaderType GetLongHeaderType(
-        QuicStreamOffset offset) const = 0;
-
     virtual QuicString chlo_hash() const = 0;
 
     // Returns true once any encrypter (initial/0RTT or final/1RTT) has been set
@@ -147,7 +143,6 @@
   int num_scup_messages_received() const override;
 
   // From QuicCryptoStream
-  QuicLongHeaderType GetLongHeaderType(QuicStreamOffset offset) const override;
   bool encryption_established() const override;
   bool handshake_confirmed() const override;
   const QuicCryptoNegotiatedParameters& crypto_negotiated_params()
diff --git a/net/third_party/quic/core/quic_crypto_server_handshaker.cc b/net/third_party/quic/core/quic_crypto_server_handshaker.cc
index 533a568..7fda5fa 100644
--- a/net/third_party/quic/core/quic_crypto_server_handshaker.cc
+++ b/net/third_party/quic/core/quic_crypto_server_handshaker.cc
@@ -353,22 +353,6 @@
   return signed_config_->proof.send_expect_ct_header;
 }
 
-QuicLongHeaderType QuicCryptoServerHandshaker::GetLongHeaderType(
-    QuicStreamOffset /*offset*/) const {
-  if (last_sent_handshake_message_tag() == kSREJ) {
-    if (QuicVersionHasLongHeaderLengths(
-            session()->connection()->transport_version())) {
-      return HANDSHAKE;
-    }
-    // TODO(b/123493765): we should probably not be sending RETRY here.
-    return RETRY;
-  }
-  if (last_sent_handshake_message_tag() == kSHLO) {
-    return ZERO_RTT_PROTECTED;
-  }
-  return HANDSHAKE;
-}
-
 bool QuicCryptoServerHandshaker::GetBase64SHA256ClientChannelID(
     QuicString* output) const {
   if (!encryption_established() ||
@@ -446,7 +430,8 @@
       server_designated_connection_id, connection->clock(),
       connection->random_generator(), compressed_certs_cache_,
       crypto_negotiated_params_, signed_config_,
-      QuicCryptoStream::CryptoMessageFramingOverhead(transport_version()),
+      QuicCryptoStream::CryptoMessageFramingOverhead(
+          transport_version(), connection->connection_id()),
       chlo_packet_size_, std::move(done_cb));
 }
 
diff --git a/net/third_party/quic/core/quic_crypto_server_handshaker.h b/net/third_party/quic/core/quic_crypto_server_handshaker.h
index 92f0bd6..1e78573 100644
--- a/net/third_party/quic/core/quic_crypto_server_handshaker.h
+++ b/net/third_party/quic/core/quic_crypto_server_handshaker.h
@@ -50,7 +50,6 @@
   void SetPreviousCachedNetworkParams(
       CachedNetworkParameters cached_network_params) override;
   bool ShouldSendExpectCTHeader() const override;
-  QuicLongHeaderType GetLongHeaderType(QuicStreamOffset offset) const override;
 
   // From QuicCryptoStream
   bool encryption_established() const override;
diff --git a/net/third_party/quic/core/quic_crypto_server_stream.cc b/net/third_party/quic/core/quic_crypto_server_stream.cc
index 743241c9..c238b45 100644
--- a/net/third_party/quic/core/quic_crypto_server_stream.cc
+++ b/net/third_party/quic/core/quic_crypto_server_stream.cc
@@ -123,11 +123,6 @@
   return handshaker()->ShouldSendExpectCTHeader();
 }
 
-QuicLongHeaderType QuicCryptoServerStream::GetLongHeaderType(
-    QuicStreamOffset offset) const {
-  return handshaker()->GetLongHeaderType(offset);
-}
-
 bool QuicCryptoServerStream::encryption_established() const {
   if (!handshaker()) {
     return false;
diff --git a/net/third_party/quic/core/quic_crypto_server_stream.h b/net/third_party/quic/core/quic_crypto_server_stream.h
index 25b0d5c..e0a590c 100644
--- a/net/third_party/quic/core/quic_crypto_server_stream.h
+++ b/net/third_party/quic/core/quic_crypto_server_stream.h
@@ -128,10 +128,6 @@
 
     // Used by QuicCryptoStream to parse data received on this stream.
     virtual CryptoMessageParser* crypto_message_parser() = 0;
-
-    // Returns long header type for next sending handshake message.
-    virtual QuicLongHeaderType GetLongHeaderType(
-        QuicStreamOffset offset) const = 0;
   };
 
   class Helper {
@@ -191,7 +187,6 @@
   // configuration for the certificate used in the connection is accessible.
   bool ShouldSendExpectCTHeader() const;
 
-  QuicLongHeaderType GetLongHeaderType(QuicStreamOffset offset) const override;
   bool encryption_established() const override;
   bool handshake_confirmed() const override;
   const QuicCryptoNegotiatedParameters& crypto_negotiated_params()
diff --git a/net/third_party/quic/core/quic_crypto_stream.cc b/net/third_party/quic/core/quic_crypto_stream.cc
index eb58f53..82afbbd 100644
--- a/net/third_party/quic/core/quic_crypto_stream.cc
+++ b/net/third_party/quic/core/quic_crypto_stream.cc
@@ -39,9 +39,12 @@
 
 // static
 QuicByteCount QuicCryptoStream::CryptoMessageFramingOverhead(
-    QuicTransportVersion version) {
+    QuicTransportVersion version,
+    QuicConnectionId connection_id) {
+  DCHECK(QuicUtils::IsConnectionIdValidForVersion(connection_id, version));
   return QuicPacketCreator::StreamFramePacketOverhead(
-      version, PACKET_8BYTE_CONNECTION_ID, PACKET_0BYTE_CONNECTION_ID,
+      version, static_cast<QuicConnectionIdLength>(connection_id.length()),
+      PACKET_0BYTE_CONNECTION_ID,
       /*include_version=*/true,
       /*include_diversification_nonce=*/true,
       version > QUIC_VERSION_43 ? PACKET_4BYTE_PACKET_NUMBER
@@ -142,16 +145,6 @@
                                "Writing too much crypto handshake data");
   }
 
-  // Set long header type based on the encryption level.
-  if (level != ENCRYPTION_FORWARD_SECURE) {
-    QuicStreamOffset fake_offset = level == ENCRYPTION_NONE ? 0 : 1;
-    // Implementations of GetLongHeaderType either don't care at all about the
-    // offset, or only care whether or not it's 0. However, they do care that it
-    // is an absolute offset from the start of unencrypted crypto data, not the
-    // offset at a particular encryption level.
-    QuicLongHeaderType type = GetLongHeaderType(fake_offset);
-    session()->connection()->SetLongHeaderType(type);
-  }
   EncryptionLevel current_level = session()->connection()->encryption_level();
   session()->connection()->SetDefaultEncryptionLevel(level);
   size_t bytes_consumed =
diff --git a/net/third_party/quic/core/quic_crypto_stream.h b/net/third_party/quic/core/quic_crypto_stream.h
index 132751af..4bb5365 100644
--- a/net/third_party/quic/core/quic_crypto_stream.h
+++ b/net/third_party/quic/core/quic_crypto_stream.h
@@ -42,7 +42,8 @@
   // Returns the per-packet framing overhead associated with sending a
   // handshake message for |version|.
   static QuicByteCount CryptoMessageFramingOverhead(
-      QuicTransportVersion version);
+      QuicTransportVersion version,
+      QuicConnectionId connection_id);
 
   // QuicStream implementation
   void OnStreamFrame(const QuicStreamFrame& frame) override;
@@ -67,10 +68,6 @@
   // Writes |data| to the QuicStream at level |level|.
   virtual void WriteCryptoData(EncryptionLevel level, QuicStringPiece data);
 
-  // Returns appropriate long header type when sending data starts at |offset|.
-  virtual QuicLongHeaderType GetLongHeaderType(
-      QuicStreamOffset offset) const = 0;
-
   // Returns true once an encrypter has been set for the connection.
   virtual bool encryption_established() const = 0;
 
diff --git a/net/third_party/quic/core/quic_crypto_stream_test.cc b/net/third_party/quic/core/quic_crypto_stream_test.cc
index 8f3902aa..542b2a19 100644
--- a/net/third_party/quic/core/quic_crypto_stream_test.cc
+++ b/net/third_party/quic/core/quic_crypto_stream_test.cc
@@ -45,11 +45,6 @@
 
   std::vector<CryptoHandshakeMessage>* messages() { return &messages_; }
 
-  QuicLongHeaderType GetLongHeaderType(
-      QuicStreamOffset /*offset*/) const override {
-    return HANDSHAKE;
-  }
-
   bool encryption_established() const override { return false; }
   bool handshake_confirmed() const override { return false; }
 
diff --git a/net/third_party/quic/core/quic_dispatcher.cc b/net/third_party/quic/core/quic_dispatcher.cc
index 72a8bc4..536aac74 100644
--- a/net/third_party/quic/core/quic_dispatcher.cc
+++ b/net/third_party/quic/core/quic_dispatcher.cc
@@ -140,14 +140,6 @@
     QuicConnectionCloseFrame* frame = new QuicConnectionCloseFrame;
     frame->error_code = error_code;
     frame->error_details = error_details;
-    // TODO(fayang): Use the right long header type for conneciton close sent by
-    // dispatcher.
-    if (QuicVersionHasLongHeaderLengths(framer_->transport_version())) {
-      creator_.SetLongHeaderType(HANDSHAKE);
-    } else {
-      // TODO(b/123493765): we should probably not be sending RETRY here.
-      creator_.SetLongHeaderType(RETRY);
-    }
     if (!creator_.AddSavedFrame(QuicFrame(frame), NOT_RETRANSMISSION)) {
       QUIC_BUG << "Unable to add frame to an empty packet";
       delete frame;
@@ -158,7 +150,7 @@
     time_wait_list_manager_->AddConnectionIdToTimeWait(
         connection_id_, ietf_quic,
         QuicTimeWaitListManager::SEND_TERMINATION_PACKETS,
-        collector_.packets());
+        quic::ENCRYPTION_NONE, collector_.packets());
   }
 
   // Generates a series of termination packets containing the crypto handshake
@@ -169,12 +161,6 @@
     collector_.SaveStatelessRejectFrameData(reject);
     while (offset < reject.length()) {
       QuicFrame frame;
-      if (QuicVersionHasLongHeaderLengths(framer_->transport_version())) {
-        creator_.SetLongHeaderType(HANDSHAKE);
-      } else {
-        // TODO(b/123493765): we should probably not be sending RETRY here.
-        creator_.SetLongHeaderType(RETRY);
-      }
       if (framer_->transport_version() < QUIC_VERSION_47) {
         if (!creator_.ConsumeData(
                 QuicUtils::GetCryptoStreamId(framer_->transport_version()),
@@ -203,7 +189,7 @@
     }
     time_wait_list_manager_->AddConnectionIdToTimeWait(
         connection_id_, ietf_quic,
-        QuicTimeWaitListManager::SEND_TERMINATION_PACKETS,
+        QuicTimeWaitListManager::SEND_TERMINATION_PACKETS, ENCRYPTION_NONE,
         collector_.packets());
     DCHECK(time_wait_list_manager_->IsConnectionIdInTimeWait(connection_id_));
   }
@@ -309,9 +295,7 @@
               Perspective::IS_SERVER),
       last_error_(QUIC_NO_ERROR),
       new_sessions_allowed_per_event_loop_(0u),
-      accept_new_connections_(true),
-      check_blocked_writer_for_blockage_(
-          GetQuicRestartFlag(quic_check_blocked_writer_for_blockage)) {
+      accept_new_connections_(true) {
   framer_.set_visitor(this);
 }
 
@@ -355,11 +339,9 @@
     return false;
   }
 
-  // Stopgap test: The code does not construct full-length connection IDs
-  // correctly from truncated connection ID fields.  Prevent this from causing
-  // the connection ID lookup to error by dropping any packet with a short
-  // connection ID.
-  if (header.destination_connection_id_length != PACKET_8BYTE_CONNECTION_ID) {
+  // The dispatcher requires the connection ID to be present in order to
+  // look up the matching QuicConnection, so we error out if it is absent.
+  if (header.destination_connection_id_included != CONNECTION_ID_PRESENT) {
     return false;
   }
 
@@ -404,7 +386,7 @@
     // This connection ID is already in time-wait state.
     time_wait_list_manager_->ProcessPacket(
         current_self_address_, current_peer_address_,
-        header.destination_connection_id, GetPerPacketContext());
+        header.destination_connection_id, header.form, GetPerPacketContext());
     return false;
   }
 
@@ -486,7 +468,7 @@
       }
       DCHECK(time_wait_list_manager_->IsConnectionIdInTimeWait(connection_id));
       time_wait_list_manager_->ProcessPacket(
-          current_self_address_, current_peer_address_, connection_id,
+          current_self_address_, current_peer_address_, connection_id, form,
           GetPerPacketContext());
 
       // Any packets which were buffered while the stateless rejector logic was
@@ -574,13 +556,7 @@
       !connection->termination_packets()->empty()) {
     action = QuicTimeWaitListManager::SEND_TERMINATION_PACKETS;
   } else if (connection->transport_version() > QUIC_VERSION_43) {
-    // TODO(fayang): Always resetting IETF connections is a debugging
-    // expediency. Stop doing this when removing flag
-    // quic_always_reset_ietf_connections.
-    if (!GetQuicReloadableFlag(quic_always_reset_ietf_connections) &&
-        source == ConnectionCloseSource::FROM_PEER) {
-      action = QuicTimeWaitListManager::DO_NOTHING;
-    } else if (!connection->IsHandshakeConfirmed()) {
+    if (!connection->IsHandshakeConfirmed()) {
       QUIC_CODE_COUNT(quic_v44_add_to_time_wait_list_with_handshake_failed);
       action = QuicTimeWaitListManager::SEND_TERMINATION_PACKETS;
       // This serializes a connection close termination packet with error code
@@ -595,13 +571,12 @@
           action);
       session_map_.erase(it);
       return;
-    } else {
-      QUIC_CODE_COUNT(quic_v44_add_to_time_wait_list_with_stateless_reset);
     }
+    QUIC_CODE_COUNT(quic_v44_add_to_time_wait_list_with_stateless_reset);
   }
   time_wait_list_manager_->AddConnectionIdToTimeWait(
       it->first, connection->transport_version() > QUIC_VERSION_43, action,
-      connection->termination_packets());
+      connection->encryption_level(), connection->termination_packets());
   session_map_.erase(it);
 }
 
@@ -609,10 +584,6 @@
   accept_new_connections_ = false;
 }
 
-bool QuicDispatcher::ShouldAddToBlockedList() {
-  return writer_->IsWriteBlocked();
-}
-
 std::unique_ptr<QuicPerPacketContext> QuicDispatcher::GetPerPacketContext()
     const {
   return nullptr;
@@ -637,41 +608,28 @@
   // The socket is now writable.
   writer_->SetWritable();
 
-  if (check_blocked_writer_for_blockage_) {
-    QUIC_RESTART_FLAG_COUNT_N(quic_check_blocked_writer_for_blockage, 2, 6);
-    // Move every blocked writer in |write_blocked_list_| to a temporary list.
-    const size_t num_blocked_writers_before = write_blocked_list_.size();
-    WriteBlockedList temp_list;
-    temp_list.swap(write_blocked_list_);
-    DCHECK(write_blocked_list_.empty());
+  // Move every blocked writer in |write_blocked_list_| to a temporary list.
+  const size_t num_blocked_writers_before = write_blocked_list_.size();
+  WriteBlockedList temp_list;
+  temp_list.swap(write_blocked_list_);
+  DCHECK(write_blocked_list_.empty());
 
-    // Give each blocked writer a chance to write what they indended to write.
-    // If they are blocked again, they will call |OnWriteBlocked| to add
-    // themselves back into |write_blocked_list_|.
-    while (!temp_list.empty()) {
-      QuicBlockedWriterInterface* blocked_writer = temp_list.begin()->first;
-      temp_list.erase(temp_list.begin());
-      blocked_writer->OnBlockedWriterCanWrite();
-    }
-    const size_t num_blocked_writers_after = write_blocked_list_.size();
-    if (num_blocked_writers_after != 0) {
-      if (num_blocked_writers_before == num_blocked_writers_after) {
-        QUIC_CODE_COUNT(quic_zero_progress_on_can_write);
-      } else {
-        QUIC_CODE_COUNT(quic_blocked_again_on_can_write);
-      }
-    }
-    return;
-  }
-
-  // Give all the blocked writers one chance to write, until we're blocked again
-  // or there's no work left.
-  while (!write_blocked_list_.empty() && !writer_->IsWriteBlocked()) {
-    QuicBlockedWriterInterface* blocked_writer =
-        write_blocked_list_.begin()->first;
-    write_blocked_list_.erase(write_blocked_list_.begin());
+  // Give each blocked writer a chance to write what they indended to write.
+  // If they are blocked again, they will call |OnWriteBlocked| to add
+  // themselves back into |write_blocked_list_|.
+  while (!temp_list.empty()) {
+    QuicBlockedWriterInterface* blocked_writer = temp_list.begin()->first;
+    temp_list.erase(temp_list.begin());
     blocked_writer->OnBlockedWriterCanWrite();
   }
+  const size_t num_blocked_writers_after = write_blocked_list_.size();
+  if (num_blocked_writers_after != 0) {
+    if (num_blocked_writers_before == num_blocked_writers_after) {
+      QUIC_CODE_COUNT(quic_zero_progress_on_can_write);
+    } else {
+      QUIC_CODE_COUNT(quic_blocked_again_on_can_write);
+    }
+  }
 }
 
 bool QuicDispatcher::HasPendingWrites() const {
@@ -726,26 +684,16 @@
 
 void QuicDispatcher::OnWriteBlocked(
     QuicBlockedWriterInterface* blocked_writer) {
-  if (check_blocked_writer_for_blockage_) {
-    QUIC_RESTART_FLAG_COUNT_N(quic_check_blocked_writer_for_blockage, 1, 6);
-    if (!blocked_writer->IsWriterBlocked()) {
-      // It is a programming error if this ever happens. When we are sure it is
-      // not happening, replace it with a DCHECK.
-      QUIC_BUG
-          << "Tried to add writer into blocked list when it shouldn't be added";
-      // Return without adding the connection to the blocked list, to avoid
-      // infinite loops in OnCanWrite.
-      return;
-    }
-  } else {
-    if (!ShouldAddToBlockedList()) {
-      QUIC_BUG
-          << "Tried to add writer into blocked list when it shouldn't be added";
-      // Return without adding the connection to the blocked list, to avoid
-      // infinite loops in OnCanWrite.
-      return;
-    }
+  if (!blocked_writer->IsWriterBlocked()) {
+    // It is a programming error if this ever happens. When we are sure it is
+    // not happening, replace it with a DCHECK.
+    QUIC_BUG
+        << "Tried to add writer into blocked list when it shouldn't be added";
+    // Return without adding the connection to the blocked list, to avoid
+    // infinite loops in OnCanWrite.
+    return;
   }
+
   write_blocked_list_.insert(std::make_pair(blocked_writer, true));
 }
 
@@ -772,8 +720,8 @@
                   << ", error_code:" << error_code
                   << ", error_details:" << error_details;
     time_wait_list_manager_->AddConnectionIdToTimeWait(
-        connection_id, format != GOOGLE_QUIC_PACKET, action,
-        /*termination_packets=*/nullptr);
+        connection_id, format != GOOGLE_QUIC_PACKET, action, ENCRYPTION_NONE,
+        nullptr);
     return;
   }
 
@@ -812,7 +760,8 @@
       ParsedQuicVersionVector{UnsupportedQuicVersion()}));
   time_wait_list_manager()->AddConnectionIdToTimeWait(
       connection_id, /*ietf_quic=*/true,
-      QuicTimeWaitListManager::SEND_TERMINATION_PACKETS, &termination_packets);
+      QuicTimeWaitListManager::SEND_TERMINATION_PACKETS, ENCRYPTION_NONE,
+      &termination_packets);
 }
 
 void QuicDispatcher::OnPacket() {}
@@ -1108,7 +1057,7 @@
     // Time wait list will reject the packet correspondingly.
     time_wait_list_manager()->ProcessPacket(
         current_self_address(), current_peer_address(), current_connection_id(),
-        GetPerPacketContext());
+        form, GetPerPacketContext());
     return;
   }
   if (!buffered_packets_.HasBufferedPackets(current_connection_id_) &&
@@ -1316,7 +1265,7 @@
           rejector->connection_id())) {
     time_wait_list_manager_->ProcessPacket(
         current_self_address, current_peer_address, rejector->connection_id(),
-        GetPerPacketContext());
+        current_packet_format, GetPerPacketContext());
     return;
   }
 
diff --git a/net/third_party/quic/core/quic_dispatcher.h b/net/third_party/quic/core/quic_dispatcher.h
index b3ec81933..99d3025 100644
--- a/net/third_party/quic/core/quic_dispatcher.h
+++ b/net/third_party/quic/core/quic_dispatcher.h
@@ -328,10 +328,6 @@
 
   void StopAcceptingNewConnections();
 
-  // Return true if the blocked writer should be added to blocked list.
-  // TODO(wub): Remove when deprecating --quic_check_blocked_writer_for_blockage
-  virtual bool ShouldAddToBlockedList();
-
   // Called to terminate a connection statelessly. Depending on |format|, either
   // 1) send connection close with |error_code| and |error_details| and add
   // connection to time wait list or 2) directly add connection to time wait
@@ -474,9 +470,6 @@
 
   // True if this dispatcher is not draining.
   bool accept_new_connections_;
-
-  // Latched value of --quic_check_blocked_writer_for_blockage.
-  const bool check_blocked_writer_for_blockage_;
 };
 
 }  // namespace quic
diff --git a/net/third_party/quic/core/quic_dispatcher_test.cc b/net/third_party/quic/core/quic_dispatcher_test.cc
index a9151913..2c2343b 100644
--- a/net/third_party/quic/core/quic_dispatcher_test.cc
+++ b/net/third_party/quic/core/quic_dispatcher_test.cc
@@ -13,6 +13,7 @@
 #include "net/third_party/quic/core/crypto/crypto_protocol.h"
 #include "net/third_party/quic/core/crypto/quic_crypto_server_config.h"
 #include "net/third_party/quic/core/crypto/quic_random.h"
+#include "net/third_party/quic/core/quic_connection_id.h"
 #include "net/third_party/quic/core/quic_crypto_stream.h"
 #include "net/third_party/quic/core/quic_packet_writer_wrapper.h"
 #include "net/third_party/quic/core/quic_time_wait_list_manager.h"
@@ -246,7 +247,7 @@
                      bool has_version_flag,
                      const QuicString& data) {
     ProcessPacket(peer_address, connection_id, has_version_flag, data,
-                  PACKET_8BYTE_CONNECTION_ID, PACKET_4BYTE_PACKET_NUMBER);
+                  CONNECTION_ID_PRESENT, PACKET_4BYTE_PACKET_NUMBER);
   }
 
   // Process a packet with a default path id, and packet number 1,
@@ -255,10 +256,10 @@
                      QuicConnectionId connection_id,
                      bool has_version_flag,
                      const QuicString& data,
-                     QuicConnectionIdLength connection_id_length,
+                     QuicConnectionIdIncluded connection_id_included,
                      QuicPacketNumberLength packet_number_length) {
     ProcessPacket(peer_address, connection_id, has_version_flag, data,
-                  connection_id_length, packet_number_length, 1);
+                  connection_id_included, packet_number_length, 1);
   }
 
   // Process a packet using the first supported version.
@@ -266,12 +267,12 @@
                      QuicConnectionId connection_id,
                      bool has_version_flag,
                      const QuicString& data,
-                     QuicConnectionIdLength connection_id_length,
+                     QuicConnectionIdIncluded connection_id_included,
                      QuicPacketNumberLength packet_number_length,
                      uint64_t packet_number) {
     ProcessPacket(peer_address, connection_id, has_version_flag,
                   CurrentSupportedVersions().front(), data,
-                  connection_id_length, packet_number_length, packet_number);
+                  connection_id_included, packet_number_length, packet_number);
   }
 
   // Processes a packet.
@@ -280,13 +281,13 @@
                      bool has_version_flag,
                      ParsedQuicVersion version,
                      const QuicString& data,
-                     QuicConnectionIdLength connection_id_length,
+                     QuicConnectionIdIncluded connection_id_included,
                      QuicPacketNumberLength packet_number_length,
                      uint64_t packet_number) {
     ParsedQuicVersionVector versions(SupportedVersions(version));
     std::unique_ptr<QuicEncryptedPacket> packet(ConstructEncryptedPacket(
         connection_id, EmptyQuicConnectionId(), has_version_flag, false,
-        packet_number, data, connection_id_length, PACKET_0BYTE_CONNECTION_ID,
+        packet_number, data, connection_id_included, CONNECTION_ID_ABSENT,
         packet_number_length, &versions));
     std::unique_ptr<QuicReceivedPacket> received_packet(
         ConstructReceivedPacket(*packet, mock_helper_.GetClock()->Now()));
@@ -395,8 +396,7 @@
       client_address, TestConnectionId(1), true,
       ParsedQuicVersion(PROTOCOL_TLS1_3,
                         CurrentSupportedVersions().front().transport_version),
-      SerializeCHLO(), PACKET_8BYTE_CONNECTION_ID, PACKET_4BYTE_PACKET_NUMBER,
-      1);
+      SerializeCHLO(), CONNECTION_ID_PRESENT, PACKET_4BYTE_PACKET_NUMBER, 1);
   EXPECT_EQ(client_address, dispatcher_->current_peer_address());
   EXPECT_EQ(server_address_, dispatcher_->current_self_address());
 }
@@ -472,15 +472,14 @@
       client_address, TestConnectionId(1), true,
       ParsedQuicVersion(PROTOCOL_QUIC_CRYPTO,
                         CurrentSupportedVersions().front().transport_version),
-      SerializeCHLO(), PACKET_8BYTE_CONNECTION_ID, PACKET_4BYTE_PACKET_NUMBER,
-      1);
+      SerializeCHLO(), CONNECTION_ID_PRESENT, PACKET_4BYTE_PACKET_NUMBER, 1);
   // Packet number 256 with packet number length 1 would be considered as 0 in
   // dispatcher.
   ProcessPacket(
       client_address, TestConnectionId(1), false,
       ParsedQuicVersion(PROTOCOL_QUIC_CRYPTO,
                         CurrentSupportedVersions().front().transport_version),
-      "", PACKET_8BYTE_CONNECTION_ID, PACKET_1BYTE_PACKET_NUMBER, 256);
+      "", CONNECTION_ID_PRESENT, PACKET_1BYTE_PACKET_NUMBER, 256);
   EXPECT_EQ(client_address, dispatcher_->current_peer_address());
   EXPECT_EQ(server_address_, dispatcher_->current_self_address());
 }
@@ -501,7 +500,7 @@
   QuicString chlo = SerializeCHLO() + QuicString(1200, 'a');
   DCHECK_LE(1200u, chlo.length());
   ProcessPacket(client_address, TestConnectionId(1), true, parsed_version, chlo,
-                PACKET_8BYTE_CONNECTION_ID, PACKET_4BYTE_PACKET_NUMBER, 1);
+                CONNECTION_ID_PRESENT, PACKET_4BYTE_PACKET_NUMBER, 1);
 }
 
 TEST_F(QuicDispatcherTest, NoVersionNegotiationWithSmallPacket) {
@@ -522,7 +521,7 @@
   QuicString truncated_chlo = chlo.substr(0, 1100);
   DCHECK_EQ(1100u, truncated_chlo.length());
   ProcessPacket(client_address, TestConnectionId(1), true, parsed_version,
-                truncated_chlo, PACKET_8BYTE_CONNECTION_ID,
+                truncated_chlo, CONNECTION_ID_PRESENT,
                 PACKET_4BYTE_PACKET_NUMBER, 1);
 }
 
@@ -548,7 +547,7 @@
   QuicString truncated_chlo = chlo.substr(0, 1100);
   DCHECK_EQ(1100u, truncated_chlo.length());
   ProcessPacket(client_address, TestConnectionId(1), true, parsed_version,
-                truncated_chlo, PACKET_8BYTE_CONNECTION_ID,
+                truncated_chlo, CONNECTION_ID_PRESENT,
                 PACKET_4BYTE_PACKET_NUMBER, 1);
 }
 
@@ -608,9 +607,11 @@
 
   // Dispatcher forwards subsequent packets for this connection_id to the time
   // wait list manager.
-  EXPECT_CALL(*time_wait_list_manager_, ProcessPacket(_, _, connection_id, _))
+  EXPECT_CALL(*time_wait_list_manager_,
+              ProcessPacket(_, _, connection_id, _, _))
       .Times(1);
-  EXPECT_CALL(*time_wait_list_manager_, AddConnectionIdToTimeWait(_, _, _, _))
+  EXPECT_CALL(*time_wait_list_manager_,
+              AddConnectionIdToTimeWait(_, _, _, _, _))
       .Times(0);
   ProcessPacket(client_address, connection_id, true, "data");
 }
@@ -624,9 +625,11 @@
   // list manager.
   EXPECT_CALL(*dispatcher_, CreateQuicSession(_, _, QuicStringPiece("hq"), _))
       .Times(0);
-  EXPECT_CALL(*time_wait_list_manager_, ProcessPacket(_, _, connection_id, _))
+  EXPECT_CALL(*time_wait_list_manager_,
+              ProcessPacket(_, _, connection_id, _, _))
       .Times(1);
-  EXPECT_CALL(*time_wait_list_manager_, AddConnectionIdToTimeWait(_, _, _, _))
+  EXPECT_CALL(*time_wait_list_manager_,
+              AddConnectionIdToTimeWait(_, _, _, _, _))
       .Times(1);
   ProcessPacket(client_address, connection_id, false, SerializeCHLO());
 }
@@ -641,8 +644,9 @@
               CreateQuicSession(TestConnectionId(1), client_address,
                                 QuicStringPiece("hq"), _))
       .Times(0);
-  EXPECT_CALL(*time_wait_list_manager_, ProcessPacket(_, _, _, _)).Times(0);
-  EXPECT_CALL(*time_wait_list_manager_, AddConnectionIdToTimeWait(_, _, _, _))
+  EXPECT_CALL(*time_wait_list_manager_, ProcessPacket(_, _, _, _, _)).Times(0);
+  EXPECT_CALL(*time_wait_list_manager_,
+              AddConnectionIdToTimeWait(_, _, _, _, _))
       .Times(0);
   ProcessPacket(client_address, TestConnectionId(1), true, SerializeCHLO());
 }
@@ -669,7 +673,7 @@
   EXPECT_CALL(*dispatcher_,
               ShouldCreateOrBufferPacketForConnection(connection_id, _));
   ProcessPacket(client_address, connection_id, true, SerializeCHLO(),
-                PACKET_8BYTE_CONNECTION_ID, PACKET_4BYTE_PACKET_NUMBER,
+                CONNECTION_ID_PRESENT, PACKET_4BYTE_PACKET_NUMBER,
                 QuicDispatcher::kMaxReasonableInitialPacketNumber);
   EXPECT_EQ(client_address, dispatcher_->current_peer_address());
   EXPECT_EQ(server_address_, dispatcher_->current_self_address());
@@ -686,22 +690,23 @@
   EXPECT_CALL(*dispatcher_, CreateQuicSession(_, _, QuicStringPiece("hq"), _))
       .Times(0);
   EXPECT_CALL(*time_wait_list_manager_,
-              ProcessPacket(_, _, TestConnectionId(1), _))
+              ProcessPacket(_, _, TestConnectionId(1), _, _))
       .Times(1);
   EXPECT_CALL(*time_wait_list_manager_,
-              ProcessPacket(_, _, TestConnectionId(2), _))
+              ProcessPacket(_, _, TestConnectionId(2), _, _))
       .Times(1);
-  EXPECT_CALL(*time_wait_list_manager_, AddConnectionIdToTimeWait(_, _, _, _))
+  EXPECT_CALL(*time_wait_list_manager_,
+              AddConnectionIdToTimeWait(_, _, _, _, _))
       .Times(2);
   // A packet whose packet number is one to large to be allowed to start a
   // connection.
   ProcessPacket(client_address, connection_id, true, SerializeCHLO(),
-                PACKET_8BYTE_CONNECTION_ID, PACKET_4BYTE_PACKET_NUMBER,
+                CONNECTION_ID_PRESENT, PACKET_4BYTE_PACKET_NUMBER,
                 QuicDispatcher::kMaxReasonableInitialPacketNumber + 1);
   connection_id = TestConnectionId(2);
   SetQuicRestartFlag(quic_enable_accept_random_ipn, true);
   ProcessPacket(client_address, connection_id, true, SerializeCHLO(),
-                PACKET_8BYTE_CONNECTION_ID, PACKET_4BYTE_PACKET_NUMBER,
+                CONNECTION_ID_PRESENT, PACKET_4BYTE_PACKET_NUMBER,
                 MaxRandomInitialPacketNumber().ToUint64() +
                     QuicDispatcher::kMaxReasonableInitialPacketNumber + 1);
 }
@@ -726,7 +731,7 @@
       PROTOCOL_QUIC_CRYPTO,
       static_cast<QuicTransportVersion>(QuicTransportVersionMin() - 1));
   ProcessPacket(client_address, connection_id, true, version, SerializeCHLO(),
-                PACKET_8BYTE_CONNECTION_ID, PACKET_4BYTE_PACKET_NUMBER, 1);
+                CONNECTION_ID_PRESENT, PACKET_4BYTE_PACKET_NUMBER, 1);
   connection_id = TestConnectionId(++conn_id);
   EXPECT_CALL(*dispatcher_, CreateQuicSession(connection_id, client_address,
                                               QuicStringPiece("hq"), _))
@@ -745,7 +750,7 @@
   ProcessPacket(client_address, connection_id, true,
                 ParsedQuicVersion(PROTOCOL_QUIC_CRYPTO,
                                   QuicVersionMin().transport_version),
-                SerializeCHLO(), PACKET_8BYTE_CONNECTION_ID,
+                SerializeCHLO(), CONNECTION_ID_PRESENT,
                 PACKET_4BYTE_PACKET_NUMBER, 1);
   connection_id = TestConnectionId(++conn_id);
   EXPECT_CALL(*dispatcher_, CreateQuicSession(connection_id, client_address,
@@ -763,7 +768,7 @@
   EXPECT_CALL(*dispatcher_,
               ShouldCreateOrBufferPacketForConnection(connection_id, _));
   ProcessPacket(client_address, connection_id, true, QuicVersionMax(),
-                SerializeCHLO(), PACKET_8BYTE_CONNECTION_ID,
+                SerializeCHLO(), CONNECTION_ID_PRESENT,
                 PACKET_4BYTE_PACKET_NUMBER, 1);
 
   // Turn off version 47.
@@ -774,7 +779,7 @@
       .Times(0);
   ProcessPacket(client_address, connection_id, true,
                 ParsedQuicVersion(PROTOCOL_QUIC_CRYPTO, QUIC_VERSION_47),
-                SerializeCHLO(), PACKET_8BYTE_CONNECTION_ID,
+                SerializeCHLO(), CONNECTION_ID_PRESENT,
                 PACKET_4BYTE_PACKET_NUMBER, 1);
 
   // Turn on version 47.
@@ -796,7 +801,7 @@
               ShouldCreateOrBufferPacketForConnection(connection_id, _));
   ProcessPacket(client_address, connection_id, true,
                 ParsedQuicVersion(PROTOCOL_QUIC_CRYPTO, QUIC_VERSION_47),
-                SerializeCHLO(), PACKET_8BYTE_CONNECTION_ID,
+                SerializeCHLO(), CONNECTION_ID_PRESENT,
                 PACKET_4BYTE_PACKET_NUMBER, 1);
 
   // Turn off version 46.
@@ -807,7 +812,7 @@
       .Times(0);
   ProcessPacket(client_address, connection_id, true,
                 ParsedQuicVersion(PROTOCOL_QUIC_CRYPTO, QUIC_VERSION_46),
-                SerializeCHLO(), PACKET_8BYTE_CONNECTION_ID,
+                SerializeCHLO(), CONNECTION_ID_PRESENT,
                 PACKET_4BYTE_PACKET_NUMBER, 1);
 
   // Turn on version 46.
@@ -829,7 +834,7 @@
               ShouldCreateOrBufferPacketForConnection(connection_id, _));
   ProcessPacket(client_address, connection_id, true,
                 ParsedQuicVersion(PROTOCOL_QUIC_CRYPTO, QUIC_VERSION_46),
-                SerializeCHLO(), PACKET_8BYTE_CONNECTION_ID,
+                SerializeCHLO(), CONNECTION_ID_PRESENT,
                 PACKET_4BYTE_PACKET_NUMBER, 1);
 
   // Turn off version 44.
@@ -840,7 +845,7 @@
       .Times(0);
   ProcessPacket(client_address, connection_id, true,
                 ParsedQuicVersion(PROTOCOL_QUIC_CRYPTO, QUIC_VERSION_44),
-                SerializeCHLO(), PACKET_8BYTE_CONNECTION_ID,
+                SerializeCHLO(), CONNECTION_ID_PRESENT,
                 PACKET_4BYTE_PACKET_NUMBER, 1);
 
   // Turn on version 44.
@@ -862,7 +867,7 @@
               ShouldCreateOrBufferPacketForConnection(connection_id, _));
   ProcessPacket(client_address, connection_id, true,
                 ParsedQuicVersion(PROTOCOL_QUIC_CRYPTO, QUIC_VERSION_44),
-                SerializeCHLO(), PACKET_8BYTE_CONNECTION_ID,
+                SerializeCHLO(), CONNECTION_ID_PRESENT,
                 PACKET_4BYTE_PACKET_NUMBER, 1);
 
   // Turn off version 43.
@@ -873,7 +878,7 @@
       .Times(0);
   ProcessPacket(client_address, connection_id, true,
                 ParsedQuicVersion(PROTOCOL_QUIC_CRYPTO, QUIC_VERSION_43),
-                SerializeCHLO(), PACKET_8BYTE_CONNECTION_ID,
+                SerializeCHLO(), CONNECTION_ID_PRESENT,
                 PACKET_4BYTE_PACKET_NUMBER, 1);
 
   // Turn on version 43.
@@ -895,7 +900,7 @@
               ShouldCreateOrBufferPacketForConnection(connection_id, _));
   ProcessPacket(client_address, connection_id, true,
                 ParsedQuicVersion(PROTOCOL_QUIC_CRYPTO, QUIC_VERSION_43),
-                SerializeCHLO(), PACKET_8BYTE_CONNECTION_ID,
+                SerializeCHLO(), CONNECTION_ID_PRESENT,
                 PACKET_4BYTE_PACKET_NUMBER, 1);
 
   // Turn off version 39.
@@ -906,7 +911,7 @@
       .Times(0);
   ProcessPacket(client_address, connection_id, true,
                 ParsedQuicVersion(PROTOCOL_QUIC_CRYPTO, QUIC_VERSION_39),
-                SerializeCHLO(), PACKET_8BYTE_CONNECTION_ID,
+                SerializeCHLO(), CONNECTION_ID_PRESENT,
                 PACKET_4BYTE_PACKET_NUMBER, 1);
 
   // Turn on version 39.
@@ -928,7 +933,7 @@
               ShouldCreateOrBufferPacketForConnection(connection_id, _));
   ProcessPacket(client_address, connection_id, true,
                 ParsedQuicVersion(PROTOCOL_QUIC_CRYPTO, QUIC_VERSION_39),
-                SerializeCHLO(), PACKET_8BYTE_CONNECTION_ID,
+                SerializeCHLO(), CONNECTION_ID_PRESENT,
                 PACKET_4BYTE_PACKET_NUMBER, 1);
 }
 
@@ -1099,7 +1104,8 @@
             time_wait_list_manager_->IsConnectionIdInTimeWait(connection_id));
   if (ExpectStatelessReject()) {
     // The second packet will be processed on the time-wait list.
-    EXPECT_CALL(*time_wait_list_manager_, ProcessPacket(_, _, connection_id, _))
+    EXPECT_CALL(*time_wait_list_manager_,
+                ProcessPacket(_, _, connection_id, _, _))
         .Times(1);
   } else {
     // The second packet will trigger a packet-validation
@@ -1150,7 +1156,8 @@
                                     kClientHelloMinimumSize);
 
   if (GetParam().enable_stateless_rejects_via_flag) {
-    EXPECT_CALL(*time_wait_list_manager_, ProcessPacket(_, _, connection_id, _))
+    EXPECT_CALL(*time_wait_list_manager_,
+                ProcessPacket(_, _, connection_id, _, _))
         .Times(1);
   } else {
     EXPECT_CALL(*dispatcher_,
@@ -1229,18 +1236,22 @@
       .Times(0);
   if (CurrentSupportedVersions()[0].transport_version > QUIC_VERSION_43) {
     // This IETF packet has invalid connection ID length.
-    EXPECT_CALL(*time_wait_list_manager_, ProcessPacket(_, _, _, _)).Times(0);
-    EXPECT_CALL(*time_wait_list_manager_, AddConnectionIdToTimeWait(_, _, _, _))
+    EXPECT_CALL(*time_wait_list_manager_, ProcessPacket(_, _, _, _, _))
+        .Times(0);
+    EXPECT_CALL(*time_wait_list_manager_,
+                AddConnectionIdToTimeWait(_, _, _, _, _))
         .Times(0);
   } else {
     // This GQUIC packet is considered as IETF QUIC packet with short header
     // with unacceptable packet number.
-    EXPECT_CALL(*time_wait_list_manager_, ProcessPacket(_, _, _, _)).Times(1);
-    EXPECT_CALL(*time_wait_list_manager_, AddConnectionIdToTimeWait(_, _, _, _))
+    EXPECT_CALL(*time_wait_list_manager_, ProcessPacket(_, _, _, _, _))
+        .Times(1);
+    EXPECT_CALL(*time_wait_list_manager_,
+                AddConnectionIdToTimeWait(_, _, _, _, _))
         .Times(1);
   }
   ProcessPacket(client_address, connection_id, true, "data",
-                PACKET_0BYTE_CONNECTION_ID, PACKET_4BYTE_PACKET_NUMBER);
+                CONNECTION_ID_ABSENT, PACKET_4BYTE_PACKET_NUMBER);
 }
 
 class BlockingWriter : public QuicPacketWriterWrapper {
@@ -1429,25 +1440,6 @@
   dispatcher_->OnCanWrite();
 }
 
-TEST_F(QuicDispatcherWriteBlockedListTest, OnCanWriteHandleBlock) {
-  if (GetQuicRestartFlag(quic_check_blocked_writer_for_blockage)) {
-    return;
-  }
-  // Finally make sure if we write block on a write call, we stop calling.
-  InSequence s;
-  SetBlocked();
-  dispatcher_->OnWriteBlocked(connection1());
-  dispatcher_->OnWriteBlocked(connection2());
-  EXPECT_CALL(*connection1(), OnCanWrite())
-      .WillOnce(Invoke(this, &QuicDispatcherWriteBlockedListTest::SetBlocked));
-  EXPECT_CALL(*connection2(), OnCanWrite()).Times(0);
-  dispatcher_->OnCanWrite();
-
-  // And we'll resume where we left off when we get another call.
-  EXPECT_CALL(*connection2(), OnCanWrite());
-  dispatcher_->OnCanWrite();
-}
-
 TEST_F(QuicDispatcherWriteBlockedListTest, OnCanWriteHandleBlockConnection1) {
   // If the 1st blocked writer gets blocked in OnCanWrite, it will be added back
   // into the write blocked list.
@@ -1496,9 +1488,6 @@
 
 TEST_F(QuicDispatcherWriteBlockedListTest,
        OnCanWriteHandleBlockBothConnections) {
-  if (!GetQuicRestartFlag(quic_check_blocked_writer_for_blockage)) {
-    return;
-  }
   // Both connections get blocked in OnCanWrite, and added back into the write
   // blocked list.
   InSequence s;
@@ -1535,13 +1524,6 @@
   connection2()->SetQuicPacketWriter(new BlockingWriter, /*owns_writer=*/true);
   EXPECT_NE(dispatcher_->writer(), connection2()->writer());
 
-  if (!GetQuicRestartFlag(quic_check_blocked_writer_for_blockage)) {
-    EXPECT_QUIC_BUG(
-        BlockConnection2(),
-        "Tried to add writer into blocked list when it shouldn't be added");
-    return;
-  }
-
   BlockConnection2();
   EXPECT_TRUE(dispatcher_->HasPendingWrites());
 
@@ -1667,7 +1649,7 @@
       .Times(1);
   for (size_t i = 1; i <= kDefaultMaxUndecryptablePackets + 1; ++i) {
     ProcessPacket(client_address, conn_id, true,
-                  QuicStrCat("data packet ", i + 1), PACKET_8BYTE_CONNECTION_ID,
+                  QuicStrCat("data packet ", i + 1), CONNECTION_ID_PRESENT,
                   PACKET_4BYTE_PACKET_NUMBER, /*packet_number=*/i + 1);
   }
   EXPECT_EQ(0u, dispatcher_->session_map().size())
@@ -1708,7 +1690,7 @@
                 ShouldCreateOrBufferPacketForConnection(conn_id, _));
     ProcessPacket(client_address, conn_id, true,
                   QuicStrCat("data packet on connection ", i),
-                  PACKET_8BYTE_CONNECTION_ID, PACKET_4BYTE_PACKET_NUMBER,
+                  CONNECTION_ID_PRESENT, PACKET_4BYTE_PACKET_NUMBER,
                   /*packet_number=*/2);
   }
 
@@ -1773,7 +1755,7 @@
   QuicSocketAddress client_address(QuicIpAddress::Loopback4(), 1);
   QuicConnectionId conn_id = TestConnectionId(1);
   ProcessPacket(client_address, conn_id, true, QuicStrCat("data packet ", 2),
-                PACKET_8BYTE_CONNECTION_ID, PACKET_4BYTE_PACKET_NUMBER,
+                CONNECTION_ID_PRESENT, PACKET_4BYTE_PACKET_NUMBER,
                 /*packet_number=*/2);
 
   // When CHLO arrives, a new session should be created, and all packets
@@ -1808,7 +1790,7 @@
   QuicSocketAddress client_address(QuicIpAddress::Loopback4(), 1);
   QuicConnectionId conn_id = TestConnectionId(1);
   ProcessPacket(client_address, conn_id, true, QuicStrCat("data packet ", 2),
-                PACKET_8BYTE_CONNECTION_ID, PACKET_4BYTE_PACKET_NUMBER,
+                CONNECTION_ID_PRESENT, PACKET_4BYTE_PACKET_NUMBER,
                 /*packet_number=*/2);
 
   mock_helper_.AdvanceTime(
@@ -1820,7 +1802,7 @@
   // New arrived CHLO will be dropped because this connection is in time wait
   // list.
   ASSERT_TRUE(time_wait_list_manager_->IsConnectionIdInTimeWait(conn_id));
-  EXPECT_CALL(*time_wait_list_manager_, ProcessPacket(_, _, conn_id, _));
+  EXPECT_CALL(*time_wait_list_manager_, ProcessPacket(_, _, conn_id, _, _));
   ProcessPacket(client_address, conn_id, true, SerializeFullCHLO());
 }
 
@@ -2010,7 +1992,7 @@
 
   uint64_t conn_id = 1;
   ProcessPacket(client_addr_, TestConnectionId(conn_id), true, "data packet",
-                PACKET_8BYTE_CONNECTION_ID, PACKET_4BYTE_PACKET_NUMBER,
+                CONNECTION_ID_PRESENT, PACKET_4BYTE_PACKET_NUMBER,
                 /*packet_number=*/1);
   // Fill packet buffer to full with CHLOs on other connections. Need to feed
   // extra CHLOs because the first |kMaxNumSessionsToCreate| are going to create
@@ -2077,7 +2059,7 @@
               })));
     }
     ProcessPacket(client_addr_, TestConnectionId(conn_id), true, version,
-                  SerializeFullCHLO(), PACKET_8BYTE_CONNECTION_ID,
+                  SerializeFullCHLO(), CONNECTION_ID_PRESENT,
                   PACKET_4BYTE_PACKET_NUMBER, 1);
   }
 
@@ -2349,16 +2331,16 @@
     InSequence s;
     EXPECT_CALL(check, Call(1));
     EXPECT_CALL(*time_wait_list_manager_,
-                AddConnectionIdToTimeWait(conn_id, _, _, _));
+                AddConnectionIdToTimeWait(conn_id, _, _, _, _));
     EXPECT_CALL(*time_wait_list_manager_,
-                ProcessPacket(_, client_addr_, conn_id, _));
+                ProcessPacket(_, client_addr_, conn_id, _, _));
 
     EXPECT_CALL(check, Call(2));
     EXPECT_CALL(*dispatcher_, CreateQuicSession(conn_id, client_addr_,
                                                 QuicStringPiece("hq"), _))
         .Times(0);
     EXPECT_CALL(*time_wait_list_manager_,
-                ProcessPacket(_, client_addr_, conn_id, _));
+                ProcessPacket(_, client_addr_, conn_id, _, _));
   }
 
   // Send a CHLO that the StatelessRejector will reject.
@@ -2477,13 +2459,13 @@
     EXPECT_CALL(*dispatcher_, CreateQuicSession(conn_id_2, client_addr_, _, _))
         .Times(0);
     EXPECT_CALL(*time_wait_list_manager_,
-                AddConnectionIdToTimeWait(conn_id_2, _, _, _));
+                AddConnectionIdToTimeWait(conn_id_2, _, _, _, _));
     EXPECT_CALL(*time_wait_list_manager_,
-                ProcessPacket(_, client_addr_, conn_id_2, _));
+                ProcessPacket(_, client_addr_, conn_id_2, _, _));
 
     EXPECT_CALL(check, Call(2));
     EXPECT_CALL(*time_wait_list_manager_,
-                ProcessPacket(_, client_addr_, conn_id_2, _));
+                ProcessPacket(_, client_addr_, conn_id_2, _, _));
 
     EXPECT_CALL(check, Call(3));
     EXPECT_CALL(*dispatcher_,
@@ -2491,9 +2473,9 @@
 
     EXPECT_CALL(check, Call(4));
     EXPECT_CALL(*time_wait_list_manager_,
-                AddConnectionIdToTimeWait(conn_id_1, _, _, _));
+                AddConnectionIdToTimeWait(conn_id_1, _, _, _, _));
     EXPECT_CALL(*time_wait_list_manager_,
-                ProcessPacket(_, client_addr_, conn_id_1, _));
+                ProcessPacket(_, client_addr_, conn_id_1, _, _));
   }
 
   // Send a CHLO that the StatelessRejector will reject.
@@ -2551,9 +2533,9 @@
                                                 QuicStringPiece(), _))
         .Times(0);
     EXPECT_CALL(*time_wait_list_manager_,
-                AddConnectionIdToTimeWait(conn_id_1, _, _, _));
+                AddConnectionIdToTimeWait(conn_id_1, _, _, _, _));
     EXPECT_CALL(*time_wait_list_manager_,
-                ProcessPacket(_, client_addr_, conn_id_1, _));
+                ProcessPacket(_, client_addr_, conn_id_1, _, _));
   }
 
   // Send a CHLO that the StatelessRejector will reject.
@@ -2594,7 +2576,7 @@
 
     EXPECT_CALL(check, Call(2));
     EXPECT_CALL(*time_wait_list_manager_,
-                ProcessPacket(_, client_addr_, conn_id, _));
+                ProcessPacket(_, client_addr_, conn_id, _, _));
     EXPECT_CALL(*dispatcher_,
                 CreateQuicSession(conn_id, client_addr_, QuicStringPiece(), _))
         .Times(0);
@@ -2717,7 +2699,7 @@
   chlo_.SetVersion(kVER, chlo_version);
   // Send a CHLO with v43. Dispatcher framer's version is set to v43.
   ProcessPacket(client_addr_, TestConnectionId(1), true, chlo_version,
-                SerializeCHLO(), PACKET_8BYTE_CONNECTION_ID,
+                SerializeCHLO(), CONNECTION_ID_PRESENT,
                 PACKET_4BYTE_PACKET_NUMBER, 1);
 
   // Send another CHLO with v39. Dispatcher framer's version is set to v39.
@@ -2726,7 +2708,7 @@
   // Invalidate the cached serialized form.
   chlo_.MarkDirty();
   ProcessPacket(client_addr_, TestConnectionId(2), true, chlo_version,
-                SerializeCHLO(), PACKET_8BYTE_CONNECTION_ID,
+                SerializeCHLO(), CONNECTION_ID_PRESENT,
                 PACKET_4BYTE_PACKET_NUMBER, 1);
   ASSERT_EQ(GetFakeProofSource()->NumPendingCallbacks(), 2);
 
@@ -2742,7 +2724,7 @@
   ParsedQuicVersion chlo_version(PROTOCOL_QUIC_CRYPTO, QUIC_VERSION_44);
   chlo_.SetVersion(kVER, chlo_version);
   ProcessPacket(client_addr_, TestConnectionId(1), true, chlo_version,
-                SerializeCHLO(), PACKET_8BYTE_CONNECTION_ID,
+                SerializeCHLO(), CONNECTION_ID_PRESENT,
                 PACKET_4BYTE_PACKET_NUMBER, 1);
 
   // Process another packet of v43.
@@ -2751,7 +2733,7 @@
   // Invalidate the cached serialized form.
   chlo_.MarkDirty();
   ProcessPacket(client_addr_, TestConnectionId(2), true, chlo_version,
-                SerializeCHLO(), PACKET_8BYTE_CONNECTION_ID,
+                SerializeCHLO(), CONNECTION_ID_PRESENT,
                 PACKET_4BYTE_PACKET_NUMBER, 1);
   ASSERT_EQ(GetFakeProofSource()->NumPendingCallbacks(), 2);
 
diff --git a/net/third_party/quic/core/quic_epoll_alarm_factory.cc b/net/third_party/quic/core/quic_epoll_alarm_factory.cc
index a5285738..3f6f3df3 100644
--- a/net/third_party/quic/core/quic_epoll_alarm_factory.cc
+++ b/net/third_party/quic/core/quic_epoll_alarm_factory.cc
@@ -4,8 +4,9 @@
 
 #include "net/third_party/quic/core/quic_epoll_alarm_factory.h"
 
-namespace quic {
+#include "net/third_party/quic/core/quic_arena_scoped_ptr.h"
 
+namespace quic {
 namespace {
 
 class QuicEpollAlarm : public QuicAlarm {
@@ -28,6 +29,16 @@
     epoll_alarm_impl_.UnregisterIfRegistered();
   }
 
+  void UpdateImpl() override {
+    DCHECK(deadline().IsInitialized());
+    int64_t epoll_deadline = (deadline() - QuicTime::Zero()).ToMicroseconds();
+    if (epoll_alarm_impl_.registered()) {
+      epoll_alarm_impl_.ReregisterAlarm(epoll_deadline);
+    } else {
+      epoll_server_->RegisterAlarm(epoll_deadline, &epoll_alarm_impl_);
+    }
+  }
+
  private:
   class EpollAlarmImpl : public QuicEpollAlarmBase {
    public:
diff --git a/net/third_party/quic/core/quic_epoll_alarm_factory_test.cc b/net/third_party/quic/core/quic_epoll_alarm_factory_test.cc
index 5f4ef0c..073637d 100644
--- a/net/third_party/quic/core/quic_epoll_alarm_factory_test.cc
+++ b/net/third_party/quic/core/quic_epoll_alarm_factory_test.cc
@@ -4,9 +4,9 @@
 
 #include "net/third_party/quic/core/quic_epoll_alarm_factory.h"
 
+#include "net/third_party/quic/platform/api/quic_epoll_test_tools.h"
 #include "net/third_party/quic/platform/api/quic_test.h"
 #include "net/third_party/quic/platform/impl/quic_epoll_clock.h"
-#include "net/third_party/quic/test_tools/fake_epoll_server.h"
 
 namespace quic {
 namespace test {
@@ -36,7 +36,7 @@
 
   const QuicEpollClock clock_;
   QuicEpollAlarmFactory alarm_factory_;
-  test::FakeEpollServer epoll_server_;
+  QuicFakeEpollServer epoll_server_;
   QuicConnectionArena arena_;
 };
 
diff --git a/net/third_party/quic/core/quic_epoll_connection_helper.h b/net/third_party/quic/core/quic_epoll_connection_helper.h
index 55832aa6..95d5545 100644
--- a/net/third_party/quic/core/quic_epoll_connection_helper.h
+++ b/net/third_party/quic/core/quic_epoll_connection_helper.h
@@ -36,7 +36,7 @@
       delete;
   ~QuicEpollConnectionHelper() override;
 
-  // QuicEpollConnectionHelperInterface
+  // QuicConnectionHelperInterface
   const QuicClock* GetClock() const override;
   QuicRandom* GetRandomGenerator() override;
   QuicBufferAllocator* GetStreamSendBufferAllocator() override;
diff --git a/net/third_party/quic/core/quic_epoll_connection_helper_test.cc b/net/third_party/quic/core/quic_epoll_connection_helper_test.cc
index 3149f87..7c38299 100644
--- a/net/third_party/quic/core/quic_epoll_connection_helper_test.cc
+++ b/net/third_party/quic/core/quic_epoll_connection_helper_test.cc
@@ -5,8 +5,8 @@
 #include "net/third_party/quic/core/quic_epoll_connection_helper.h"
 
 #include "net/third_party/quic/core/crypto/quic_random.h"
+#include "net/third_party/quic/platform/api/quic_epoll_test_tools.h"
 #include "net/third_party/quic/platform/api/quic_test.h"
-#include "net/third_party/quic/test_tools/fake_epoll_server.h"
 
 namespace quic {
 namespace test {
@@ -17,7 +17,7 @@
   QuicEpollConnectionHelperTest()
       : helper_(&epoll_server_, QuicAllocator::BUFFER_POOL) {}
 
-  test::FakeEpollServer epoll_server_;
+  QuicFakeEpollServer epoll_server_;
   QuicEpollConnectionHelper helper_;
 };
 
diff --git a/net/third_party/quic/core/quic_framer.cc b/net/third_party/quic/core/quic_framer.cc
index 657514ee..d4b93ab 100644
--- a/net/third_party/quic/core/quic_framer.cc
+++ b/net/third_party/quic/core/quic_framer.cc
@@ -782,12 +782,6 @@
          frame.token.length();
 }
 
-// static
-size_t QuicFramer::GetVersionNegotiationPacketSize(size_t number_versions) {
-  return kPublicFlagsSize + PACKET_8BYTE_CONNECTION_ID +
-         number_versions * kQuicVersionSize;
-}
-
 // TODO(nharper): Change this method to take a ParsedQuicVersion.
 bool QuicFramer::IsSupportedTransportVersion(
     const QuicTransportVersion version) const {
@@ -1381,8 +1375,8 @@
   }
   const QuicData& reset_serialized = reset.GetSerialized();
 
-  size_t len =
-      kPublicFlagsSize + PACKET_8BYTE_CONNECTION_ID + reset_serialized.length();
+  size_t len = kPublicFlagsSize + packet.connection_id.length() +
+               reset_serialized.length();
   std::unique_ptr<char[]> buffer(new char[len]);
   // Endianness is not a concern here, as writer is not going to write integers
   // or floating numbers.
@@ -1451,7 +1445,8 @@
     return BuildIetfVersionNegotiationPacket(connection_id, versions);
   }
   DCHECK(!versions.empty());
-  size_t len = GetVersionNegotiationPacketSize(versions.size());
+  size_t len = kPublicFlagsSize + connection_id.length() +
+               versions.size() * kQuicVersionSize;
   std::unique_ptr<char[]> buffer(new char[len]);
   // Endianness is not a concern here, version negotiation packet does not have
   // integers or floating numbers.
@@ -1488,7 +1483,7 @@
   QUIC_DVLOG(1) << "Building IETF version negotiation packet.";
   DCHECK(!versions.empty());
   size_t len = kPacketHeaderTypeSize + kConnectionIdLengthSize +
-               PACKET_8BYTE_CONNECTION_ID +
+               connection_id.length() +
                (versions.size() + 1) * kQuicVersionSize;
   std::unique_ptr<char[]> buffer(new char[len]);
   QuicDataWriter writer(len, buffer.get());
@@ -1826,8 +1821,8 @@
   QuicStringPiece encrypted = encrypted_reader->ReadRemainingPayload();
   QuicStringPiece associated_data = GetAssociatedDataFromEncryptedPacket(
       version_.transport_version, packet,
-      header->destination_connection_id_length,
-      header->source_connection_id_length, header->version_flag,
+      GetIncludedDestinationConnectionIdLength(*header),
+      GetIncludedSourceConnectionIdLength(*header), header->version_flag,
       header->nonce != nullptr, header->packet_number_length,
       header->retry_token_length_length, header->retry_token.length(),
       header->length_length);
@@ -1910,8 +1905,8 @@
   QuicStringPiece encrypted = encrypted_reader->ReadRemainingPayload();
   QuicStringPiece associated_data = GetAssociatedDataFromEncryptedPacket(
       version_.transport_version, packet,
-      header->destination_connection_id_length,
-      header->source_connection_id_length, header->version_flag,
+      GetIncludedDestinationConnectionIdLength(*header),
+      GetIncludedSourceConnectionIdLength(*header), header->version_flag,
       header->nonce != nullptr, header->packet_number_length,
       header->retry_token_length_length, header->retry_token.length(),
       header->length_length);
@@ -2014,6 +2009,10 @@
              header.possible_stateless_reset_token);
 }
 
+bool QuicFramer::HasEncrypterOfEncryptionLevel(EncryptionLevel level) const {
+  return encrypter_[level] != nullptr;
+}
+
 bool QuicFramer::AppendPacketHeader(const QuicPacketHeader& header,
                                     QuicDataWriter* writer,
                                     size_t* length_field_offset) {
@@ -2036,15 +2035,15 @@
     DCHECK_EQ(Perspective::IS_SERVER, perspective_);
     public_flags |= PACKET_PUBLIC_FLAGS_NONCE;
   }
-  DCHECK_EQ(PACKET_0BYTE_CONNECTION_ID, header.source_connection_id_length);
-  switch (header.destination_connection_id_length) {
-    case PACKET_0BYTE_CONNECTION_ID:
+  DCHECK_EQ(CONNECTION_ID_ABSENT, header.source_connection_id_included);
+  switch (header.destination_connection_id_included) {
+    case CONNECTION_ID_ABSENT:
       if (!writer->WriteUInt8(public_flags |
                               PACKET_PUBLIC_FLAGS_0BYTE_CONNECTION_ID)) {
         return false;
       }
       break;
-    case PACKET_8BYTE_CONNECTION_ID:
+    case CONNECTION_ID_PRESENT:
       QUIC_BUG_IF(!QuicUtils::IsConnectionIdValidForVersion(
           header.destination_connection_id, transport_version()))
           << "AppendPacketHeader: attempted to use connection ID "
@@ -2150,22 +2149,21 @@
   if (!QuicUtils::VariableLengthConnectionIdAllowedForVersion(
           transport_version()) &&
       !GetQuicReloadableFlag(quic_use_new_append_connection_id)) {
-    if (!AppendIetfConnectionId(header.version_flag,
-                                header.destination_connection_id,
-                                header.destination_connection_id_length,
-                                header.source_connection_id,
-                                header.source_connection_id_length, writer)) {
+    if (!AppendIetfConnectionId(
+            header.version_flag, header.destination_connection_id,
+            GetIncludedDestinationConnectionIdLength(header),
+            header.source_connection_id,
+            GetIncludedSourceConnectionIdLength(header), writer)) {
       return false;
     }
   } else {
     QUIC_RELOADABLE_FLAG_COUNT_N(quic_use_new_append_connection_id, 2, 2);
     if (!AppendIetfConnectionIdsNew(
             header.version_flag,
-            header.destination_connection_id_length !=
-                    PACKET_0BYTE_CONNECTION_ID
+            header.destination_connection_id_included != CONNECTION_ID_ABSENT
                 ? header.destination_connection_id
                 : EmptyQuicConnectionId(),
-            header.source_connection_id_length != PACKET_0BYTE_CONNECTION_ID
+            header.source_connection_id_included != CONNECTION_ID_ABSENT
                 ? header.source_connection_id
                 : EmptyQuicConnectionId(),
             writer)) {
@@ -2303,10 +2301,10 @@
         set_detailed_error("Unable to read ConnectionId.");
         return false;
       }
-      header->destination_connection_id_length = PACKET_8BYTE_CONNECTION_ID;
+      header->destination_connection_id_included = CONNECTION_ID_PRESENT;
       break;
     case PACKET_PUBLIC_FLAGS_0BYTE_CONNECTION_ID:
-      header->destination_connection_id_length = PACKET_0BYTE_CONNECTION_ID;
+      header->destination_connection_id_included = CONNECTION_ID_ABSENT;
       header->destination_connection_id = last_serialized_connection_id_;
       break;
   }
@@ -2468,12 +2466,12 @@
     // Long header packets received by client must include 8-byte source
     // connection ID, and those received by server must include 8-byte
     // destination connection ID.
-    header->destination_connection_id_length =
-        perspective_ == Perspective::IS_CLIENT ? PACKET_0BYTE_CONNECTION_ID
-                                               : PACKET_8BYTE_CONNECTION_ID;
-    header->source_connection_id_length = perspective_ == Perspective::IS_CLIENT
-                                              ? PACKET_8BYTE_CONNECTION_ID
-                                              : PACKET_0BYTE_CONNECTION_ID;
+    header->destination_connection_id_included =
+        perspective_ == Perspective::IS_CLIENT ? CONNECTION_ID_ABSENT
+                                               : CONNECTION_ID_PRESENT;
+    header->source_connection_id_included =
+        perspective_ == Perspective::IS_CLIENT ? CONNECTION_ID_PRESENT
+                                               : CONNECTION_ID_ABSENT;
     // Read version tag.
     QuicVersionLabel version_label;
     if (!reader->ReadTag(&version_label)) {
@@ -2518,12 +2516,10 @@
   header->version_flag = false;
   // Connection ID length depends on the perspective. Client does not expect
   // destination connection ID, and server expects destination connection ID.
-  header->destination_connection_id_length =
-      perspective_ == Perspective::IS_CLIENT ? PACKET_0BYTE_CONNECTION_ID
-                                             : PACKET_8BYTE_CONNECTION_ID;
-  if (header->destination_connection_id_length == PACKET_0BYTE_CONNECTION_ID) {
-    header->destination_connection_id = last_serialized_connection_id_;
-  }
+  header->destination_connection_id_included =
+      perspective_ == Perspective::IS_CLIENT ? CONNECTION_ID_ABSENT
+                                             : CONNECTION_ID_PRESENT;
+  header->source_connection_id_included = CONNECTION_ID_ABSENT;
   if (infer_packet_header_type_from_version_ &&
       transport_version() > QUIC_VERSION_44 && !(type & FLAGS_FIXED_BIT)) {
     set_detailed_error("Fixed bit is 0 in short header.");
@@ -2544,22 +2540,33 @@
   if (!ProcessIetfHeaderTypeByte(reader, header)) {
     return false;
   }
+
+  uint8_t destination_connection_id_length =
+      header->destination_connection_id_included == CONNECTION_ID_PRESENT
+          ? kQuicDefaultConnectionIdLength
+          : 0;
+  uint8_t source_connection_id_length =
+      header->source_connection_id_included == CONNECTION_ID_PRESENT
+          ? kQuicDefaultConnectionIdLength
+          : 0;
   if (header->form == IETF_QUIC_LONG_HEADER_PACKET) {
     // Read and validate connection ID length.
-    uint8_t connection_id_length;
-    if (!reader->ReadBytes(&connection_id_length, 1)) {
+    uint8_t connection_id_lengths_byte;
+    if (!reader->ReadBytes(&connection_id_lengths_byte, 1)) {
       set_detailed_error("Unable to read ConnectionId length.");
       return false;
     }
     uint8_t dcil =
-        (connection_id_length & kDestinationConnectionIdLengthMask) >> 4;
-    uint8_t scil = connection_id_length & kSourceConnectionIdLengthMask;
-    if ((dcil != 0 &&
-         dcil != PACKET_8BYTE_CONNECTION_ID - kConnectionIdLengthAdjustment) ||
-        (scil != 0 &&
-         scil != PACKET_8BYTE_CONNECTION_ID - kConnectionIdLengthAdjustment) ||
-        dcil == scil || (perspective_ == Perspective::IS_CLIENT && scil == 0) ||
-        (perspective_ == Perspective::IS_SERVER && dcil == 0)) {
+        (connection_id_lengths_byte & kDestinationConnectionIdLengthMask) >> 4;
+    if (dcil != 0) {
+      dcil += kConnectionIdLengthAdjustment;
+    }
+    uint8_t scil = connection_id_lengths_byte & kSourceConnectionIdLengthMask;
+    if (scil != 0) {
+      scil += kConnectionIdLengthAdjustment;
+    }
+    if (dcil != destination_connection_id_length ||
+        scil != source_connection_id_length) {
       // Long header packets received by client must include 8-byte source
       // connection ID, and those received by server must include 8-byte
       // destination connection ID.
@@ -2568,27 +2575,30 @@
       set_detailed_error("Invalid ConnectionId length.");
       return false;
     }
+    destination_connection_id_length = dcil;
+    source_connection_id_length = scil;
   }
 
   // Read connection ID.
-  if (header->destination_connection_id_length == PACKET_8BYTE_CONNECTION_ID &&
-      !reader->ReadConnectionId(&header->destination_connection_id,
-                                kQuicDefaultConnectionIdLength)) {
+  if (!reader->ReadConnectionId(&header->destination_connection_id,
+                                destination_connection_id_length)) {
     set_detailed_error("Unable to read Destination ConnectionId.");
     return false;
   }
 
-  if (header->source_connection_id_length == PACKET_8BYTE_CONNECTION_ID &&
-      !reader->ReadConnectionId(&header->source_connection_id,
-                                kQuicDefaultConnectionIdLength)) {
+  if (!reader->ReadConnectionId(&header->source_connection_id,
+                                source_connection_id_length)) {
     set_detailed_error("Unable to read Source ConnectionId.");
     return false;
   }
 
-  if (header->source_connection_id_length == PACKET_8BYTE_CONNECTION_ID) {
+  if (header->source_connection_id_included == CONNECTION_ID_PRESENT) {
     // Set destination connection ID to source connection ID.
     DCHECK_EQ(EmptyQuicConnectionId(), header->destination_connection_id);
     header->destination_connection_id = header->source_connection_id;
+  } else if (header->destination_connection_id_included ==
+             CONNECTION_ID_ABSENT) {
+    header->destination_connection_id = last_serialized_connection_id_;
   }
 
   return true;
diff --git a/net/third_party/quic/core/quic_framer.h b/net/third_party/quic/core/quic_framer.h
index b17425e..8d45a4a0 100644
--- a/net/third_party/quic/core/quic_framer.h
+++ b/net/third_party/quic/core/quic_framer.h
@@ -344,9 +344,6 @@
   // Size in bytes for a serialized new token frame
   static size_t GetNewTokenFrameSize(const QuicNewTokenFrame& frame);
 
-  // Size in bytes required for a serialized version negotiation packet
-  static size_t GetVersionNegotiationPacketSize(size_t number_versions);
-
   // Size in bytes required for a serialized stop sending frame.
   static size_t GetStopSendingFrameSize(const QuicStopSendingFrame& frame);
 
@@ -539,6 +536,9 @@
   // Returns true if |header| is considered as an stateless reset packet.
   bool IsIetfStatelessResetPacket(const QuicPacketHeader& header) const;
 
+  // Returns true if encrypter of |level| is available.
+  bool HasEncrypterOfEncryptionLevel(EncryptionLevel level) const;
+
   void set_validate_flags(bool value) { validate_flags_ = value; }
 
   Perspective perspective() const { return perspective_; }
diff --git a/net/third_party/quic/core/quic_framer_test.cc b/net/third_party/quic/core/quic_framer_test.cc
index b64edfb..dc283ee8 100644
--- a/net/third_party/quic/core/quic_framer_test.cc
+++ b/net/third_party/quic/core/quic_framer_test.cc
@@ -5287,7 +5287,7 @@
       // type (long header)
       {"",
        {0x8F}},
-             // version tag
+      // version tag
       {"",
        {0x00, 0x00, 0x00, 0x00}},
       {"",
@@ -8571,7 +8571,8 @@
       header, buffer.get(), packet_size, ENCRYPTION_NONE);
 
   EXPECT_NE(0u, length);
-  QuicPacket data(buffer.release(), length, true, header);
+  QuicPacket data(framer_.transport_version(), buffer.release(), length, true,
+                  header);
 
   test::CompareCharArraysWithHexError("constructed packet", data.data(),
                                       data.length(), AsChars(p), packet_size);
@@ -8622,7 +8623,8 @@
   // above, before checking that the generated packet is correct.
   EXPECT_EQ(kQuicPathFrameBufferSize, payload.size());
 
-  QuicPacket data(buffer.release(), length, true, header);
+  QuicPacket data(framer_.transport_version(), buffer.release(), length, true,
+                  header);
 
   test::CompareCharArraysWithHexError("constructed packet", data.data(),
                                       data.length(), AsChars(packet),
@@ -8669,7 +8671,8 @@
       header, buffer.get(), QUIC_ARRAYSIZE(packet), payloads,
       /*is_padded=*/false, ENCRYPTION_NONE);
   EXPECT_EQ(length, QUIC_ARRAYSIZE(packet));
-  QuicPacket data(buffer.release(), length, true, header);
+  QuicPacket data(framer_.transport_version(), buffer.release(), length, true,
+                  header);
 
   test::CompareCharArraysWithHexError("constructed packet", data.data(),
                                       data.length(), AsChars(packet),
@@ -8714,7 +8717,8 @@
       header, buffer.get(), QUIC_ARRAYSIZE(packet), payloads,
       /*is_padded=*/true, ENCRYPTION_NONE);
   EXPECT_EQ(length, QUIC_ARRAYSIZE(packet));
-  QuicPacket data(buffer.release(), length, true, header);
+  QuicPacket data(framer_.transport_version(), buffer.release(), length, true,
+                  header);
 
   test::CompareCharArraysWithHexError("constructed packet", data.data(),
                                       data.length(), AsChars(packet),
@@ -8764,7 +8768,8 @@
       header, buffer.get(), QUIC_ARRAYSIZE(packet), payloads,
       /*is_padded=*/false, ENCRYPTION_NONE);
   EXPECT_EQ(length, QUIC_ARRAYSIZE(packet));
-  QuicPacket data(buffer.release(), length, true, header);
+  QuicPacket data(framer_.transport_version(), buffer.release(), length, true,
+                  header);
 
   test::CompareCharArraysWithHexError("constructed packet", data.data(),
                                       data.length(), AsChars(packet),
@@ -8816,7 +8821,8 @@
       header, buffer.get(), QUIC_ARRAYSIZE(packet), payloads,
       /*is_padded=*/true, ENCRYPTION_NONE);
   EXPECT_EQ(length, QUIC_ARRAYSIZE(packet));
-  QuicPacket data(buffer.release(), length, true, header);
+  QuicPacket data(framer_.transport_version(), buffer.release(), length, true,
+                  header);
 
   test::CompareCharArraysWithHexError("constructed packet", data.data(),
                                       data.length(), AsChars(packet),
@@ -9615,8 +9621,8 @@
   versions.push_back(framer_.version());
   std::unique_ptr<QuicEncryptedPacket> packet(ConstructEncryptedPacket(
       TestConnectionId(), EmptyQuicConnectionId(), false, false,
-      kTestQuicStreamId, kTestString, PACKET_8BYTE_CONNECTION_ID,
-      PACKET_0BYTE_CONNECTION_ID, PACKET_4BYTE_PACKET_NUMBER, &versions));
+      kTestQuicStreamId, kTestString, CONNECTION_ID_PRESENT,
+      CONNECTION_ID_ABSENT, PACKET_4BYTE_PACKET_NUMBER, &versions));
 
   MockFramerVisitor visitor;
   framer_.set_visitor(&visitor);
@@ -9655,8 +9661,8 @@
   versions.push_back(framer_.version());
   std::unique_ptr<QuicEncryptedPacket> packet(ConstructMisFramedEncryptedPacket(
       TestConnectionId(), EmptyQuicConnectionId(), false, false,
-      kTestQuicStreamId, kTestString, PACKET_8BYTE_CONNECTION_ID,
-      PACKET_0BYTE_CONNECTION_ID, PACKET_4BYTE_PACKET_NUMBER, &versions,
+      kTestQuicStreamId, kTestString, CONNECTION_ID_PRESENT,
+      CONNECTION_ID_ABSENT, PACKET_4BYTE_PACKET_NUMBER, &versions,
       Perspective::IS_CLIENT));
 
   MockFramerVisitor visitor;
diff --git a/net/third_party/quic/core/quic_interval.h b/net/third_party/quic/core/quic_interval.h
index 91ea7a0..bd0ef2ac 100644
--- a/net/third_party/quic/core/quic_interval.h
+++ b/net/third_party/quic/core/quic_interval.h
@@ -63,8 +63,6 @@
 #include <utility>
 #include <vector>
 
-#include "net/third_party/quic/platform/api/quic_string.h"
-
 namespace quic {
 
 template <typename T>
diff --git a/net/third_party/quic/core/quic_packet_creator.cc b/net/third_party/quic/core/quic_packet_creator.cc
index 00081cc..5715123 100644
--- a/net/third_party/quic/core/quic_packet_creator.cc
+++ b/net/third_party/quic/core/quic_packet_creator.cc
@@ -9,6 +9,7 @@
 
 #include "base/macros.h"
 #include "net/third_party/quic/core/crypto/crypto_protocol.h"
+#include "net/third_party/quic/core/quic_connection_id.h"
 #include "net/third_party/quic/core/quic_data_writer.h"
 #include "net/third_party/quic/core/quic_types.h"
 #include "net/third_party/quic/core/quic_utils.h"
@@ -23,9 +24,6 @@
 #include "net/third_party/quic/platform/api/quic_string_piece.h"
 #include "net/third_party/quic/platform/api/quic_text_utils.h"
 
-// If true, enforce that QUIC CHLOs fit in one packet.
-bool FLAGS_quic_enforce_single_packet_chlo = true;
-
 namespace quic {
 namespace {
 
@@ -70,7 +68,7 @@
       send_version_in_packet_(framer->perspective() == Perspective::IS_CLIENT),
       have_diversification_nonce_(false),
       max_packet_length_(0),
-      connection_id_length_(PACKET_8BYTE_CONNECTION_ID),
+      connection_id_included_(CONNECTION_ID_PRESENT),
       packet_size_(0),
       connection_id_(connection_id),
       packet_(QuicPacketNumber(),
@@ -79,14 +77,11 @@
               0,
               false,
               false),
-      long_header_type_(HANDSHAKE),
       pending_padding_bytes_(0),
       needs_full_padding_(false),
       can_set_transmission_type_(false),
       set_transmission_type_for_next_frame_(
-          GetQuicReloadableFlag(quic_set_transmission_type_for_next_frame)),
-      encryption_level_driven_long_header_type_(
-          GetQuicReloadableFlag(quic_encryption_driven_header_type)) {
+          GetQuicReloadableFlag(quic_set_transmission_type_for_next_frame)) {
   SetMaxPacketLength(kDefaultMaxPacketSize);
 }
 
@@ -737,24 +732,42 @@
                           nullptr, 0, false, false);
 }
 
-QuicConnectionIdLength QuicPacketCreator::GetDestinationConnectionIdLength()
+QuicConnectionIdIncluded QuicPacketCreator::GetDestinationConnectionIdIncluded()
     const {
   if (framer_->transport_version() > QUIC_VERSION_43) {
     // Packets sent by client always include destination connection ID, and
     // those sent by the server do not include destination connection ID.
     return framer_->perspective() == Perspective::IS_CLIENT
-               ? PACKET_8BYTE_CONNECTION_ID
-               : PACKET_0BYTE_CONNECTION_ID;
+               ? CONNECTION_ID_PRESENT
+               : CONNECTION_ID_ABSENT;
   }
-  return connection_id_length_;
+  return connection_id_included_;
+}
+
+QuicConnectionIdIncluded QuicPacketCreator::GetSourceConnectionIdIncluded()
+    const {
+  // Long header packets sent by server include source connection ID.
+  if (HasIetfLongHeader() && framer_->perspective() == Perspective::IS_SERVER) {
+    return CONNECTION_ID_PRESENT;
+  }
+  return CONNECTION_ID_ABSENT;
+}
+
+QuicConnectionIdLength QuicPacketCreator::GetDestinationConnectionIdLength()
+    const {
+  DCHECK(QuicUtils::IsConnectionIdValidForVersion(connection_id_,
+                                                  transport_version()));
+  return GetDestinationConnectionIdIncluded() == CONNECTION_ID_PRESENT
+             ? static_cast<QuicConnectionIdLength>(connection_id_.length())
+             : PACKET_0BYTE_CONNECTION_ID;
 }
 
 QuicConnectionIdLength QuicPacketCreator::GetSourceConnectionIdLength() const {
-  // Long header packets sent by server include source connection ID.
-  if (HasIetfLongHeader() && framer_->perspective() == Perspective::IS_SERVER) {
-    return PACKET_8BYTE_CONNECTION_ID;
-  }
-  return PACKET_0BYTE_CONNECTION_ID;
+  DCHECK(QuicUtils::IsConnectionIdValidForVersion(connection_id_,
+                                                  transport_version()));
+  return GetSourceConnectionIdIncluded() == CONNECTION_ID_PRESENT
+             ? static_cast<QuicConnectionIdLength>(connection_id_.length())
+             : PACKET_0BYTE_CONNECTION_ID;
 }
 
 QuicPacketNumberLength QuicPacketCreator::GetPacketNumberLength() const {
@@ -764,16 +777,11 @@
   return packet_.packet_number_length;
 }
 
-QuicLongHeaderType QuicPacketCreator::GetLongHeaderType() const {
-  return (encryption_level_driven_long_header_type_
-              ? EncryptionlevelToLongHeaderType(packet_.encryption_level)
-              : long_header_type_);
-}
-
 QuicVariableLengthIntegerLength QuicPacketCreator::GetRetryTokenLengthLength()
     const {
   if (QuicVersionHasLongHeaderLengths(framer_->transport_version()) &&
-      HasIetfLongHeader() && GetLongHeaderType() == INITIAL) {
+      HasIetfLongHeader() &&
+      EncryptionlevelToLongHeaderType(packet_.encryption_level) == INITIAL) {
     return QuicDataWriter::GetVarInt62Len(GetRetryToken().length());
   }
   return VARIABLE_LENGTH_INTEGER_LENGTH_0;
@@ -790,7 +798,8 @@
 QuicVariableLengthIntegerLength QuicPacketCreator::GetLengthLength() const {
   if (QuicVersionHasLongHeaderLengths(framer_->transport_version()) &&
       HasIetfLongHeader()) {
-    QuicLongHeaderType long_header_type = GetLongHeaderType();
+    QuicLongHeaderType long_header_type =
+        EncryptionlevelToLongHeaderType(packet_.encryption_level);
     if (long_header_type == INITIAL || long_header_type == ZERO_RTT_PROTECTED ||
         long_header_type == HANDSHAKE) {
       return VARIABLE_LENGTH_INTEGER_LENGTH_2;
@@ -801,9 +810,10 @@
 
 void QuicPacketCreator::FillPacketHeader(QuicPacketHeader* header) {
   header->destination_connection_id = connection_id_;
-  header->destination_connection_id_length = GetDestinationConnectionIdLength();
+  header->destination_connection_id_included =
+      GetDestinationConnectionIdIncluded();
   header->source_connection_id = connection_id_;
-  header->source_connection_id_length = GetSourceConnectionIdLength();
+  header->source_connection_id_included = GetSourceConnectionIdIncluded();
   header->reset_flag = false;
   header->version_flag = IncludeVersionInHeader();
   if (IncludeNonceInPublicHeader()) {
@@ -826,7 +836,8 @@
   if (!HasIetfLongHeader()) {
     return;
   }
-  header->long_packet_type = GetLongHeaderType();
+  header->long_packet_type =
+      EncryptionlevelToLongHeaderType(packet_.encryption_level);
 }
 
 bool QuicPacketCreator::AddFrame(const QuicFrame& frame,
@@ -951,10 +962,13 @@
   return framer_->StartsWithChlo(frame.stream_id, frame.offset);
 }
 
-void QuicPacketCreator::SetConnectionIdLength(QuicConnectionIdLength length) {
+void QuicPacketCreator::SetConnectionIdIncluded(
+    QuicConnectionIdIncluded connection_id_included) {
+  DCHECK(connection_id_included == CONNECTION_ID_PRESENT ||
+         connection_id_included == CONNECTION_ID_ABSENT);
   DCHECK(framer_->perspective() == Perspective::IS_SERVER ||
-         length != PACKET_0BYTE_CONNECTION_ID);
-  connection_id_length_ = length;
+         connection_id_included != CONNECTION_ID_ABSENT);
+  connection_id_included_ = connection_id_included;
 }
 
 void QuicPacketCreator::SetTransmissionType(TransmissionType type) {
@@ -969,10 +983,6 @@
   }
 }
 
-void QuicPacketCreator::SetLongHeaderType(QuicLongHeaderType type) {
-  long_header_type_ = type;
-}
-
 QuicPacketLength QuicPacketCreator::GetLargestMessagePayload() const {
   if (framer_->transport_version() <= QUIC_VERSION_44) {
     return 0;
diff --git a/net/third_party/quic/core/quic_packet_creator.h b/net/third_party/quic/core/quic_packet_creator.h
index 0a11818e..29bed4e 100644
--- a/net/third_party/quic/core/quic_packet_creator.h
+++ b/net/third_party/quic/core/quic_packet_creator.h
@@ -213,6 +213,9 @@
   // Returns length of source connection ID to send over the wire.
   QuicConnectionIdLength GetSourceConnectionIdLength() const;
 
+  // Sets whether the connection ID should be sent over the wire.
+  void SetConnectionIdIncluded(QuicConnectionIdIncluded connection_id_included);
+
   // Sets the encryption level that will be applied to new packets.
   void set_encryption_level(EncryptionLevel level) {
     packet_.encryption_level = level;
@@ -222,8 +225,6 @@
   // created.
   QuicPacketNumber packet_number() const { return packet_.packet_number; }
 
-  void SetConnectionIdLength(QuicConnectionIdLength length);
-
   QuicByteCount max_packet_length() const { return max_packet_length_; }
 
   bool has_ack() const { return packet_.has_ack; }
@@ -249,9 +250,6 @@
   // Sets transmission type of next constructed packets.
   void SetTransmissionType(TransmissionType type);
 
-  // Sets long header type of next constructed packets.
-  void SetLongHeaderType(QuicLongHeaderType type);
-
   // Sets the retry token to be sent over the wire in v99 IETF Initial packets.
   void SetRetryToken(QuicStringPiece retry_token);
 
@@ -335,8 +333,11 @@
   // function instead.
   QuicPacketNumberLength GetPacketNumberLength() const;
 
-  // Returns long header type of packet to send over the wire.
-  QuicLongHeaderType GetLongHeaderType() const;
+  // Returns whether the destination connection ID is sent over the wire.
+  QuicConnectionIdIncluded GetDestinationConnectionIdIncluded() const;
+
+  // Returns whether the source connection ID is sent over the wire.
+  QuicConnectionIdIncluded GetSourceConnectionIdIncluded() const;
 
   // Returns length of the retry token variable length integer to send over the
   // wire. Is non-zero for v99 IETF Initial packets.
@@ -373,9 +374,8 @@
   // Maximum length including headers and encryption (UDP payload length.)
   QuicByteCount max_packet_length_;
   size_t max_plaintext_size_;
-  // Length of connection_id to send over the wire. connection_id_length_ should
-  // never be read directly, use GetConnectionIdLength() instead.
-  QuicConnectionIdLength connection_id_length_;
+  // Whether the connection_id is sent over the wire.
+  QuicConnectionIdIncluded connection_id_included_;
 
   // Frames to be added to the next SerializedPacket
   QuicFrames queued_frames_;
@@ -389,11 +389,6 @@
   // Packet used to invoke OnSerializedPacket.
   SerializedPacket packet_;
 
-  // Long header type of next constructed packets.
-  // TODO(fayang): remove this variable when deprecating
-  // quic_encryption_driven_header_type.
-  QuicLongHeaderType long_header_type_;
-
   // Retry token to send over the wire in v99 IETF Initial packets.
   QuicString retry_token_;
 
@@ -414,9 +409,6 @@
   // Latched value of --quic_set_transmission_type_for_next_frame. Don't use
   // this variable directly, use ShouldSetTransmissionTypeForNextFrame instead.
   bool set_transmission_type_for_next_frame_;
-
-  // Latched value of quic_reloadable_flag_quic_encryption_driven_header_type.
-  const bool encryption_level_driven_long_header_type_;
 };
 
 }  // namespace quic
diff --git a/net/third_party/quic/core/quic_packet_generator.cc b/net/third_party/quic/core/quic_packet_generator.cc
index 19e22ac..30b87f0 100644
--- a/net/third_party/quic/core/quic_packet_generator.cc
+++ b/net/third_party/quic/core/quic_packet_generator.cc
@@ -7,6 +7,7 @@
 #include <cstdint>
 
 #include "net/third_party/quic/core/crypto/quic_random.h"
+#include "net/third_party/quic/core/quic_connection_id.h"
 #include "net/third_party/quic/core/quic_types.h"
 #include "net/third_party/quic/core/quic_utils.h"
 #include "net/third_party/quic/platform/api/quic_bug_tracker.h"
@@ -27,13 +28,16 @@
       should_send_ack_(false),
       should_send_stop_waiting_(false),
       random_generator_(random_generator),
-      fully_pad_crypto_handshake_packets_(true) {}
+      fully_pad_crypto_handshake_packets_(true),
+      deprecate_ack_bundling_mode_(
+          GetQuicReloadableFlag(quic_deprecate_ack_bundling_mode)) {}
 
 QuicPacketGenerator::~QuicPacketGenerator() {
   DeleteFrames(&queued_control_frames_);
 }
 
 void QuicPacketGenerator::SetShouldSendAck(bool also_send_stop_waiting) {
+  DCHECK(!deprecate_ack_bundling_mode_);
   if (packet_creator_.has_ack()) {
     // Ack already queued, nothing to do.
     return;
@@ -52,6 +56,9 @@
 void QuicPacketGenerator::AddControlFrame(const QuicFrame& frame) {
   QUIC_BUG_IF(IsControlFrame(frame.type) && !GetControlFrameId(frame))
       << "Adding a control frame with no control frame id: " << frame;
+  if (deprecate_ack_bundling_mode_) {
+    MaybeBundleAckOpportunistically();
+  }
   queued_control_frames_.push_back(frame);
   SendQueuedFrames(/*flush=*/false);
 }
@@ -61,6 +68,9 @@
                                               QuicStreamOffset offset) {
   QUIC_BUG_IF(!flusher_attached_) << "Packet flusher is not attached when "
                                      "generator tries to write crypto data.";
+  if (deprecate_ack_bundling_mode_) {
+    MaybeBundleAckOpportunistically();
+  }
   // To make reasoning about crypto frames easier, we don't combine them with
   // other retransmittable frames in a single packet.
   // TODO(nharper): Once we have separate packet number spaces, everything
@@ -102,6 +112,9 @@
                                      "generator tries to write stream data.";
   bool has_handshake =
       (id == QuicUtils::GetCryptoStreamId(packet_creator_.transport_version()));
+  if (deprecate_ack_bundling_mode_) {
+    MaybeBundleAckOpportunistically();
+  }
   bool fin = state != NO_FIN;
   QUIC_BUG_IF(has_handshake && fin)
       << "Handshake packets should never send a fin";
@@ -401,9 +414,9 @@
 
 void QuicPacketGenerator::SetConnectionIdLength(uint32_t length) {
   if (length == 0) {
-    packet_creator_.SetConnectionIdLength(PACKET_0BYTE_CONNECTION_ID);
+    packet_creator_.SetConnectionIdIncluded(CONNECTION_ID_ABSENT);
   } else {
-    packet_creator_.SetConnectionIdLength(PACKET_8BYTE_CONNECTION_ID);
+    packet_creator_.SetConnectionIdIncluded(CONNECTION_ID_PRESENT);
   }
 }
 
@@ -446,10 +459,6 @@
   }
 }
 
-void QuicPacketGenerator::SetLongHeaderType(QuicLongHeaderType type) {
-  packet_creator_.SetLongHeaderType(type);
-}
-
 void QuicPacketGenerator::SetCanSetTransmissionType(
     bool can_set_transmission_type) {
   packet_creator_.set_can_set_transmission_type(can_set_transmission_type);
@@ -459,6 +468,9 @@
                                                    QuicMemSliceSpan message) {
   QUIC_BUG_IF(!flusher_attached_) << "Packet flusher is not attached when "
                                      "generator tries to add message frame.";
+  if (deprecate_ack_bundling_mode_) {
+    MaybeBundleAckOpportunistically();
+  }
   const QuicByteCount message_length = message.total_length();
   if (message_length > GetLargestMessagePayload()) {
     return MESSAGE_STATUS_TOO_LARGE;
@@ -479,6 +491,46 @@
   return MESSAGE_STATUS_SUCCESS;
 }
 
+void QuicPacketGenerator::MaybeBundleAckOpportunistically() {
+  DCHECK(deprecate_ack_bundling_mode_);
+  if (packet_creator_.has_ack()) {
+    // Ack already queued, nothing to do.
+    return;
+  }
+  if (!delegate_->ShouldGeneratePacket(NO_RETRANSMITTABLE_DATA,
+                                       NOT_HANDSHAKE)) {
+    return;
+  }
+  const bool flushed =
+      FlushAckFrame(delegate_->MaybeBundleAckOpportunistically());
+  DCHECK(flushed);
+}
+
+bool QuicPacketGenerator::FlushAckFrame(const QuicFrames& frames) {
+  QUIC_BUG_IF(!flusher_attached_) << "Packet flusher is not attached when "
+                                     "generator tries to send ACK frame.";
+  for (const auto& frame : frames) {
+    DCHECK(frame.type == ACK_FRAME || frame.type == STOP_WAITING_FRAME);
+    if (packet_creator_.HasPendingFrames()) {
+      if (packet_creator_.AddSavedFrame(frame, next_transmission_type_)) {
+        // There is pending frames and current frame fits.
+        continue;
+      }
+    }
+    DCHECK(!packet_creator_.HasPendingFrames());
+    // There is no pending frames, consult the delegate whether a packet can be
+    // generated.
+    if (!delegate_->ShouldGeneratePacket(NO_RETRANSMITTABLE_DATA,
+                                         NOT_HANDSHAKE)) {
+      return false;
+    }
+    const bool success =
+        packet_creator_.AddSavedFrame(frame, next_transmission_type_);
+    QUIC_BUG_IF(!success) << "Failed to flush " << frame;
+  }
+  return true;
+}
+
 QuicPacketLength QuicPacketGenerator::GetLargestMessagePayload() const {
   return packet_creator_.GetLargestMessagePayload();
 }
diff --git a/net/third_party/quic/core/quic_packet_generator.h b/net/third_party/quic/core/quic_packet_generator.h
index 4a4aa8d..8394518 100644
--- a/net/third_party/quic/core/quic_packet_generator.h
+++ b/net/third_party/quic/core/quic_packet_generator.h
@@ -67,6 +67,11 @@
     // Consults delegate whether a packet should be generated.
     virtual bool ShouldGeneratePacket(HasRetransmittableData retransmittable,
                                       IsHandshake handshake) = 0;
+    // Called when there is data to be sent. Retrieves updated ACK frame from
+    // the delegate.
+    virtual const QuicFrames MaybeBundleAckOpportunistically() = 0;
+    // TODO(fayang): Remove these two interfaces when deprecating
+    // quic_deprecate_ack_bundling_mode.
     virtual const QuicFrame GetUpdatedAckFrame() = 0;
     virtual void PopulateStopWaitingFrame(
         QuicStopWaitingFrame* stop_waiting) = 0;
@@ -211,9 +216,6 @@
   // Set transmission type of next constructed packets.
   void SetTransmissionType(TransmissionType type);
 
-  // Set long header type of next constructed packets.
-  void SetLongHeaderType(QuicLongHeaderType type);
-
   // Allow/Disallow setting transmission type of next constructed packets.
   void SetCanSetTransmissionType(bool can_set_transmission_type);
 
@@ -221,6 +223,10 @@
   MessageStatus AddMessageFrame(QuicMessageId message_id,
                                 QuicMemSliceSpan message);
 
+  // Called to flush ACK and STOP_WAITING frames, returns false if the flush
+  // fails.
+  bool FlushAckFrame(const QuicFrames& frames);
+
   // Returns the largest payload that will fit into a single MESSAGE frame.
   QuicPacketLength GetLargestMessagePayload() const;
 
@@ -238,6 +244,10 @@
     return fully_pad_crypto_handshake_packets_;
   }
 
+  bool deprecate_ack_bundling_mode() const {
+    return deprecate_ack_bundling_mode_;
+  }
+
  private:
   friend class test::QuicPacketGeneratorPeer;
 
@@ -261,6 +271,10 @@
   // Pending paddings should only be sent when there is nothing else to send.
   void SendRemainingPendingPadding();
 
+  // Called when there is data to be sent, Retrieves updated ACK frame from
+  // delegate_ and flushes it.
+  void MaybeBundleAckOpportunistically();
+
   DelegateInterface* delegate_;
 
   QuicPacketCreator packet_creator_;
@@ -273,18 +287,25 @@
   bool flusher_attached_;
 
   // Flags to indicate the need for just-in-time construction of a frame.
+  // TODO(fayang): Remove these two booleans when deprecating
+  // quic_deprecate_ack_bundling_mode.
   bool should_send_ack_;
   bool should_send_stop_waiting_;
   // If we put a non-retransmittable frame in this packet, then we have to hold
   // a reference to it until we flush (and serialize it). Retransmittable frames
   // are referenced elsewhere so that they can later be (optionally)
   // retransmitted.
+  // TODO(fayang): Remove this when deprecating
+  // quic_deprecate_ack_bundling_mode.
   QuicStopWaitingFrame pending_stop_waiting_frame_;
 
   QuicRandom* random_generator_;
 
   // Whether crypto handshake packets should be fully padded.
   bool fully_pad_crypto_handshake_packets_;
+
+  // Latched value of quic_deprecate_ack_bundling_mode.
+  const bool deprecate_ack_bundling_mode_;
 };
 
 }  // namespace quic
diff --git a/net/third_party/quic/core/quic_packet_generator_test.cc b/net/third_party/quic/core/quic_packet_generator_test.cc
index c8fdc7d..c99d0b4 100644
--- a/net/third_party/quic/core/quic_packet_generator_test.cc
+++ b/net/third_party/quic/core/quic_packet_generator_test.cc
@@ -46,6 +46,7 @@
   MOCK_METHOD2(ShouldGeneratePacket,
                bool(HasRetransmittableData retransmittable,
                     IsHandshake handshake));
+  MOCK_METHOD0(MaybeBundleAckOpportunistically, const QuicFrames());
   MOCK_METHOD0(GetUpdatedAckFrame, const QuicFrame());
   MOCK_METHOD1(PopulateStopWaitingFrame, void(QuicStopWaitingFrame*));
   MOCK_METHOD0(GetPacketBuffer, char*());
@@ -113,8 +114,26 @@
                       DelegateInterface* delegate,
                       SimpleDataProducer* producer)
       : QuicPacketGenerator(connection_id, framer, random_generator, delegate),
+        ack_frame_(InitAckFrame(1)),
+        delegate_(static_cast<MockDelegate*>(delegate)),
         producer_(producer) {}
 
+  void AddControlFrame(const QuicFrame& frame, bool bundle_ack) {
+    if (GetQuicReloadableFlag(quic_deprecate_ack_bundling_mode) &&
+        !QuicPacketGeneratorPeer::GetPacketCreator(this)->has_ack()) {
+      QuicFrames frames;
+      if (bundle_ack) {
+        frames.push_back(QuicFrame(&ack_frame_));
+      }
+      if (delegate_->ShouldGeneratePacket(NO_RETRANSMITTABLE_DATA,
+                                          NOT_HANDSHAKE)) {
+        EXPECT_CALL(*delegate_, MaybeBundleAckOpportunistically())
+            .WillOnce(Return(frames));
+      }
+    }
+    QuicPacketGenerator::AddControlFrame(frame);
+  }
+
   QuicConsumedData ConsumeDataFastPath(QuicStreamId id,
                                        const struct iovec* iov,
                                        int iov_count,
@@ -139,16 +158,41 @@
     if (total_length > 0) {
       producer_->SaveStreamData(id, iov, iov_count, 0, total_length);
     }
+    if (GetQuicReloadableFlag(quic_deprecate_ack_bundling_mode) &&
+        !QuicPacketGeneratorPeer::GetPacketCreator(this)->has_ack() &&
+        delegate_->ShouldGeneratePacket(NO_RETRANSMITTABLE_DATA,
+                                        NOT_HANDSHAKE)) {
+      EXPECT_CALL(*delegate_, MaybeBundleAckOpportunistically()).Times(1);
+    }
     return QuicPacketGenerator::ConsumeData(id, total_length, offset, state);
   }
 
+  MessageStatus AddMessageFrame(QuicMessageId message_id,
+                                QuicMemSliceSpan message) {
+    if (GetQuicReloadableFlag(quic_deprecate_ack_bundling_mode) &&
+        !QuicPacketGeneratorPeer::GetPacketCreator(this)->has_ack() &&
+        delegate_->ShouldGeneratePacket(NO_RETRANSMITTABLE_DATA,
+                                        NOT_HANDSHAKE)) {
+      EXPECT_CALL(*delegate_, MaybeBundleAckOpportunistically()).Times(1);
+    }
+    return QuicPacketGenerator::AddMessageFrame(message_id, message);
+  }
+
   size_t ConsumeCryptoData(EncryptionLevel level,
                            QuicStringPiece data,
                            QuicStreamOffset offset) {
     producer_->SaveCryptoData(level, offset, data);
+    if (GetQuicReloadableFlag(quic_deprecate_ack_bundling_mode) &&
+        !QuicPacketGeneratorPeer::GetPacketCreator(this)->has_ack() &&
+        delegate_->ShouldGeneratePacket(NO_RETRANSMITTABLE_DATA,
+                                        NOT_HANDSHAKE)) {
+      EXPECT_CALL(*delegate_, MaybeBundleAckOpportunistically()).Times(1);
+    }
     return QuicPacketGenerator::ConsumeCryptoData(level, data.length(), offset);
   }
 
+  QuicAckFrame ack_frame_;
+  MockDelegate* delegate_;
   SimpleDataProducer* producer_;
 };
 
@@ -290,6 +334,9 @@
 };
 
 TEST_F(QuicPacketGeneratorTest, ShouldSendAck_NotWritable) {
+  if (GetQuicReloadableFlag(quic_deprecate_ack_bundling_mode)) {
+    return;
+  }
   delegate_.SetCanNotWrite();
 
   generator_.SetShouldSendAck(false);
@@ -298,6 +345,9 @@
 }
 
 TEST_F(QuicPacketGeneratorTest, ShouldSendAck_WritableAndShouldNotFlush) {
+  if (GetQuicReloadableFlag(quic_deprecate_ack_bundling_mode)) {
+    return;
+  }
   StrictMock<MockDebugDelegate> debug_delegate;
 
   generator_.set_debug_delegate(&debug_delegate);
@@ -313,6 +363,9 @@
 }
 
 TEST_F(QuicPacketGeneratorTest, ShouldSendAck_WritableAndShouldFlush) {
+  if (GetQuicReloadableFlag(quic_deprecate_ack_bundling_mode)) {
+    return;
+  }
   delegate_.SetCanWriteOnlyNonRetransmittable();
 
   EXPECT_CALL(delegate_, GetUpdatedAckFrame())
@@ -331,6 +384,9 @@
 }
 
 TEST_F(QuicPacketGeneratorTest, ShouldSendAck_MultipleCalls) {
+  if (GetQuicReloadableFlag(quic_deprecate_ack_bundling_mode)) {
+    return;
+  }
   // Make sure that calling SetShouldSendAck multiple times does not result in a
   // crash. Previously this would result in multiple QuicFrames queued in the
   // packet generator, with all but the last with internal pointers to freed
@@ -352,7 +408,8 @@
 TEST_F(QuicPacketGeneratorTest, AddControlFrame_NotWritable) {
   delegate_.SetCanNotWrite();
 
-  generator_.AddControlFrame(QuicFrame(CreateRstStreamFrame()));
+  generator_.AddControlFrame(QuicFrame(CreateRstStreamFrame()),
+                             /*bundle_ack=*/false);
   EXPECT_TRUE(generator_.HasQueuedFrames());
   EXPECT_TRUE(generator_.HasRetransmittableFrames());
 }
@@ -360,7 +417,8 @@
 TEST_F(QuicPacketGeneratorTest, AddControlFrame_OnlyAckWritable) {
   delegate_.SetCanWriteOnlyNonRetransmittable();
 
-  generator_.AddControlFrame(QuicFrame(CreateRstStreamFrame()));
+  generator_.AddControlFrame(QuicFrame(CreateRstStreamFrame()),
+                             /*bundle_ack=*/false);
   EXPECT_TRUE(generator_.HasQueuedFrames());
   EXPECT_TRUE(generator_.HasRetransmittableFrames());
 }
@@ -368,7 +426,8 @@
 TEST_F(QuicPacketGeneratorTest, AddControlFrame_WritableAndShouldNotFlush) {
   delegate_.SetCanWriteAnything();
 
-  generator_.AddControlFrame(QuicFrame(CreateRstStreamFrame()));
+  generator_.AddControlFrame(QuicFrame(CreateRstStreamFrame()),
+                             /*bundle_ack=*/false);
   EXPECT_TRUE(generator_.HasQueuedFrames());
   EXPECT_TRUE(generator_.HasRetransmittableFrames());
 }
@@ -376,7 +435,8 @@
 TEST_F(QuicPacketGeneratorTest, AddControlFrame_NotWritableBatchThenFlush) {
   delegate_.SetCanNotWrite();
 
-  generator_.AddControlFrame(QuicFrame(CreateRstStreamFrame()));
+  generator_.AddControlFrame(QuicFrame(CreateRstStreamFrame()),
+                             /*bundle_ack=*/false);
   EXPECT_TRUE(generator_.HasQueuedFrames());
   EXPECT_TRUE(generator_.HasRetransmittableFrames());
   generator_.Flush();
@@ -401,7 +461,8 @@
   EXPECT_CALL(delegate_, OnSerializedPacket(_))
       .WillOnce(Invoke(this, &QuicPacketGeneratorTest::SavePacket));
 
-  generator_.AddControlFrame(QuicFrame(CreateRstStreamFrame()));
+  generator_.AddControlFrame(QuicFrame(CreateRstStreamFrame()),
+                             /*bundle_ack=*/false);
   generator_.Flush();
   EXPECT_FALSE(generator_.HasQueuedFrames());
   EXPECT_FALSE(generator_.HasRetransmittableFrames());
@@ -412,6 +473,8 @@
 }
 
 TEST_F(QuicPacketGeneratorTest, ConsumeCryptoData) {
+  delegate_.SetCanWriteAnything();
+
   EXPECT_CALL(delegate_, OnSerializedPacket(_))
       .WillOnce(Invoke(this, &QuicPacketGeneratorTest::SavePacket));
   QuicString data = "crypto data";
@@ -530,6 +593,8 @@
 }
 
 TEST_F(QuicPacketGeneratorTest, ConsumeData_EmptyData) {
+  delegate_.SetCanWriteAnything();
+
   EXPECT_QUIC_BUG(generator_.ConsumeData(QuicUtils::GetHeadersStreamId(
                                              framer_.transport_version()),
                                          nullptr, 0, 0, 0, NO_FIN),
@@ -704,15 +769,20 @@
 TEST_F(QuicPacketGeneratorTest, ConsumeDataLargeSendAckFalse) {
   delegate_.SetCanNotWrite();
 
-  generator_.SetShouldSendAck(false);
-  generator_.AddControlFrame(QuicFrame(CreateRstStreamFrame()));
+  if (!GetQuicReloadableFlag(quic_deprecate_ack_bundling_mode)) {
+    generator_.SetShouldSendAck(false);
+  }
+  generator_.AddControlFrame(QuicFrame(CreateRstStreamFrame()),
+                             /*bundle_ack=*/true);
   EXPECT_TRUE(generator_.HasQueuedFrames());
   EXPECT_TRUE(generator_.HasRetransmittableFrames());
 
   delegate_.SetCanWriteAnything();
 
-  EXPECT_CALL(delegate_, GetUpdatedAckFrame())
-      .WillOnce(Return(QuicFrame(&ack_frame_)));
+  if (!GetQuicReloadableFlag(quic_deprecate_ack_bundling_mode)) {
+    EXPECT_CALL(delegate_, GetUpdatedAckFrame())
+        .WillOnce(Return(QuicFrame(&ack_frame_)));
+  }
 
   // Create a 10000 byte IOVector.
   CreateData(10000);
@@ -742,17 +812,21 @@
     return;
   }
   delegate_.SetCanNotWrite();
-  generator_.SetShouldSendAck(true /* stop_waiting */);
+  if (!GetQuicReloadableFlag(quic_deprecate_ack_bundling_mode)) {
+    generator_.SetShouldSendAck(true /* stop_waiting */);
+  }
   delegate_.SetCanWriteAnything();
 
-  // Set up frames to write into the creator when control frames are written.
-  EXPECT_CALL(delegate_, GetUpdatedAckFrame())
-      .WillOnce(Return(QuicFrame(&ack_frame_)));
-  EXPECT_CALL(delegate_, PopulateStopWaitingFrame(_));
-  // Generator should have queued control frames, and creator should be empty.
-  EXPECT_TRUE(generator_.HasQueuedFrames());
-  EXPECT_FALSE(generator_.HasRetransmittableFrames());
-  EXPECT_FALSE(creator_->HasPendingFrames());
+  if (!GetQuicReloadableFlag(quic_deprecate_ack_bundling_mode)) {
+    // Set up frames to write into the creator when control frames are written.
+    EXPECT_CALL(delegate_, GetUpdatedAckFrame())
+        .WillOnce(Return(QuicFrame(&ack_frame_)));
+    EXPECT_CALL(delegate_, PopulateStopWaitingFrame(_));
+    // Generator should have queued control frames, and creator should be empty.
+    EXPECT_TRUE(generator_.HasQueuedFrames());
+    EXPECT_FALSE(generator_.HasRetransmittableFrames());
+    EXPECT_FALSE(creator_->HasPendingFrames());
+  }
 
   // Create a 10000 byte IOVector.
   CreateData(10000);
@@ -780,23 +854,30 @@
 TEST_F(QuicPacketGeneratorTest, NotWritableThenBatchOperations) {
   delegate_.SetCanNotWrite();
 
-  generator_.SetShouldSendAck(false);
-  generator_.AddControlFrame(QuicFrame(CreateRstStreamFrame()));
+  if (!GetQuicReloadableFlag(quic_deprecate_ack_bundling_mode)) {
+    generator_.SetShouldSendAck(false);
+  }
+  generator_.AddControlFrame(QuicFrame(CreateRstStreamFrame()),
+                             /*bundle_ack=*/true);
   EXPECT_TRUE(generator_.HasQueuedFrames());
   EXPECT_TRUE(generator_.HasRetransmittableFrames());
   EXPECT_FALSE(generator_.HasPendingStreamFramesOfStream(3));
 
   delegate_.SetCanWriteAnything();
 
-  // When the first write operation is invoked, the ack frame will be returned.
-  EXPECT_CALL(delegate_, GetUpdatedAckFrame())
-      .WillOnce(Return(QuicFrame(&ack_frame_)));
+  if (!GetQuicReloadableFlag(quic_deprecate_ack_bundling_mode)) {
+    // When the first write operation is invoked, the ack frame will be
+    // returned.
+    EXPECT_CALL(delegate_, GetUpdatedAckFrame())
+        .WillOnce(Return(QuicFrame(&ack_frame_)));
+  }
 
   // Send some data and a control frame
   MakeIOVector("quux", &iov_);
   generator_.ConsumeData(3, &iov_, 1u, iov_.iov_len, 0, NO_FIN);
   if (framer_.transport_version() != QUIC_VERSION_99) {
-    generator_.AddControlFrame(QuicFrame(CreateGoAwayFrame()));
+    generator_.AddControlFrame(QuicFrame(CreateGoAwayFrame()),
+                               /*bundle_ack=*/false);
   }
   EXPECT_TRUE(generator_.HasPendingStreamFramesOfStream(3));
 
@@ -809,7 +890,12 @@
   EXPECT_FALSE(generator_.HasPendingStreamFramesOfStream(3));
 
   PacketContents contents;
-  contents.num_ack_frames = 1;
+  if (GetQuicReloadableFlag(quic_deprecate_ack_bundling_mode)) {
+    // ACK will be flushed by connection.
+    contents.num_ack_frames = 0;
+  } else {
+    contents.num_ack_frames = 1;
+  }
   if (framer_.transport_version() != QUIC_VERSION_99) {
     contents.num_goaway_frames = 1;
   } else {
@@ -823,16 +909,22 @@
 TEST_F(QuicPacketGeneratorTest, NotWritableThenBatchOperations2) {
   delegate_.SetCanNotWrite();
 
-  generator_.SetShouldSendAck(false);
-  generator_.AddControlFrame(QuicFrame(CreateRstStreamFrame()));
+  if (!GetQuicReloadableFlag(quic_deprecate_ack_bundling_mode)) {
+    generator_.SetShouldSendAck(false);
+  }
+  generator_.AddControlFrame(QuicFrame(CreateRstStreamFrame()),
+                             /*bundle_ack=*/true);
   EXPECT_TRUE(generator_.HasQueuedFrames());
   EXPECT_TRUE(generator_.HasRetransmittableFrames());
 
   delegate_.SetCanWriteAnything();
 
-  // When the first write operation is invoked, the ack frame will be returned.
-  EXPECT_CALL(delegate_, GetUpdatedAckFrame())
-      .WillOnce(Return(QuicFrame(&ack_frame_)));
+  if (!GetQuicReloadableFlag(quic_deprecate_ack_bundling_mode)) {
+    // When the first write operation is invoked, the ack frame will be
+    // returned.
+    EXPECT_CALL(delegate_, GetUpdatedAckFrame())
+        .WillOnce(Return(QuicFrame(&ack_frame_)));
+  }
 
   {
     InSequence dummy;
@@ -851,7 +943,8 @@
   EXPECT_EQ(data_len, consumed.bytes_consumed);
   EXPECT_TRUE(consumed.fin_consumed);
   if (framer_.transport_version() != QUIC_VERSION_99) {
-    generator_.AddControlFrame(QuicFrame(CreateGoAwayFrame()));
+    generator_.AddControlFrame(QuicFrame(CreateGoAwayFrame()),
+                               /*bundle_ack=*/false);
   }
 
   generator_.Flush();
@@ -860,7 +953,12 @@
 
   // The first packet should have the queued data and part of the stream data.
   PacketContents contents;
-  contents.num_ack_frames = 1;
+  if (GetQuicReloadableFlag(quic_deprecate_ack_bundling_mode)) {
+    // ACK will be sent by connection.
+    contents.num_ack_frames = 0;
+  } else {
+    contents.num_ack_frames = 1;
+  }
   contents.num_rst_stream_frames = 1;
   contents.num_stream_frames = 1;
   CheckPacketContains(contents, 0);
@@ -1219,17 +1317,21 @@
   QuicPacketCreatorPeer::SetPacketNumber(creator_, 1000);
 
   delegate_.SetCanNotWrite();
-  generator_.SetShouldSendAck(true);
+  if (!GetQuicReloadableFlag(quic_deprecate_ack_bundling_mode)) {
+    generator_.SetShouldSendAck(true);
+  }
   delegate_.SetCanWriteAnything();
 
-  // Set up frames to write into the creator when control frames are written.
-  EXPECT_CALL(delegate_, GetUpdatedAckFrame())
-      .WillOnce(Return(QuicFrame(&ack_frame_)));
-  EXPECT_CALL(delegate_, PopulateStopWaitingFrame(_));
-  // Generator should have queued control frames, and creator should be empty.
-  EXPECT_TRUE(generator_.HasQueuedFrames());
-  EXPECT_FALSE(generator_.HasRetransmittableFrames());
-  EXPECT_FALSE(creator_->HasPendingFrames());
+  if (!GetQuicReloadableFlag(quic_deprecate_ack_bundling_mode)) {
+    // Set up frames to write into the creator when control frames are written.
+    EXPECT_CALL(delegate_, GetUpdatedAckFrame())
+        .WillOnce(Return(QuicFrame(&ack_frame_)));
+    EXPECT_CALL(delegate_, PopulateStopWaitingFrame(_));
+    // Generator should have queued control frames, and creator should be empty.
+    EXPECT_TRUE(generator_.HasQueuedFrames());
+    EXPECT_FALSE(generator_.HasRetransmittableFrames());
+    EXPECT_FALSE(creator_->HasPendingFrames());
+  }
 
   // This will not serialize any packets, because of the invalid frame.
   EXPECT_CALL(delegate_,
@@ -1248,7 +1350,7 @@
   char buf[2000] = {};
   QuicStringPiece error_details(buf, 2000);
   frame->error_details = QuicString(error_details);
-  generator_.AddControlFrame(QuicFrame(frame));
+  generator_.AddControlFrame(QuicFrame(frame), /*bundle_ack=*/false);
   EXPECT_TRUE(generator_.HasQueuedFrames());
   EXPECT_TRUE(generator_.HasRetransmittableFrames());
 }
diff --git a/net/third_party/quic/core/quic_packets.cc b/net/third_party/quic/core/quic_packets.cc
index a8f887a1..8e10dd2 100644
--- a/net/third_party/quic/core/quic_packets.cc
+++ b/net/third_party/quic/core/quic_packets.cc
@@ -4,6 +4,7 @@
 
 #include "net/third_party/quic/core/quic_packets.h"
 
+#include "net/third_party/quic/core/quic_connection_id.h"
 #include "net/third_party/quic/core/quic_types.h"
 #include "net/third_party/quic/core/quic_utils.h"
 #include "net/third_party/quic/core/quic_versions.h"
@@ -15,14 +16,37 @@
 
 namespace quic {
 
+QuicConnectionIdLength GetIncludedConnectionIdLength(
+    QuicConnectionId connection_id,
+    QuicConnectionIdIncluded connection_id_included) {
+  DCHECK(connection_id_included == CONNECTION_ID_PRESENT ||
+         connection_id_included == CONNECTION_ID_ABSENT);
+  return connection_id_included == CONNECTION_ID_PRESENT
+             ? static_cast<QuicConnectionIdLength>(connection_id.length())
+             : PACKET_0BYTE_CONNECTION_ID;
+}
+
+QuicConnectionIdLength GetIncludedDestinationConnectionIdLength(
+    const QuicPacketHeader& header) {
+  return GetIncludedConnectionIdLength(
+      header.destination_connection_id,
+      header.destination_connection_id_included);
+}
+
+QuicConnectionIdLength GetIncludedSourceConnectionIdLength(
+    const QuicPacketHeader& header) {
+  return GetIncludedConnectionIdLength(header.source_connection_id,
+                                       header.source_connection_id_included);
+}
+
 size_t GetPacketHeaderSize(QuicTransportVersion version,
                            const QuicPacketHeader& header) {
-  return GetPacketHeaderSize(version, header.destination_connection_id_length,
-                             header.source_connection_id_length,
-                             header.version_flag, header.nonce != nullptr,
-                             header.packet_number_length,
-                             header.retry_token_length_length,
-                             header.retry_token.length(), header.length_length);
+  return GetPacketHeaderSize(
+      version, GetIncludedDestinationConnectionIdLength(header),
+      GetIncludedSourceConnectionIdLength(header), header.version_flag,
+      header.nonce != nullptr, header.packet_number_length,
+      header.retry_token_length_length, header.retry_token.length(),
+      header.length_length);
 }
 
 size_t GetPacketHeaderSize(
@@ -79,9 +103,9 @@
 
 QuicPacketHeader::QuicPacketHeader()
     : destination_connection_id(EmptyQuicConnectionId()),
-      destination_connection_id_length(PACKET_8BYTE_CONNECTION_ID),
+      destination_connection_id_included(CONNECTION_ID_PRESENT),
       source_connection_id(EmptyQuicConnectionId()),
-      source_connection_id_length(PACKET_0BYTE_CONNECTION_ID),
+      source_connection_id_included(CONNECTION_ID_ABSENT),
       reset_flag(false),
       version_flag(false),
       has_possible_stateless_reset_token(false),
@@ -133,11 +157,15 @@
 
 std::ostream& operator<<(std::ostream& os, const QuicPacketHeader& header) {
   os << "{ destination_connection_id: " << header.destination_connection_id
-     << ", destination_connection_id_length: "
-     << header.destination_connection_id_length
-     << ", source_connection_id: " << header.source_connection_id
-     << ", source_connection_id_length: " << header.source_connection_id_length
-     << ", packet_number_length: " << header.packet_number_length
+     << " ("
+     << (header.destination_connection_id_included == CONNECTION_ID_PRESENT
+             ? "present"
+             : "absent")
+     << "), source_connection_id: " << header.source_connection_id << " ("
+     << (header.source_connection_id_included == CONNECTION_ID_PRESENT
+             ? "present"
+             : "absent")
+     << "), packet_number_length: " << header.packet_number_length
      << ", reset_flag: " << header.reset_flag
      << ", version_flag: " << header.version_flag;
   if (header.version_flag) {
@@ -204,15 +232,16 @@
       retry_token_length_(retry_token_length),
       length_length_(length_length) {}
 
-QuicPacket::QuicPacket(char* buffer,
+QuicPacket::QuicPacket(QuicTransportVersion version,
+                       char* buffer,
                        size_t length,
                        bool owns_buffer,
                        const QuicPacketHeader& header)
     : QuicPacket(buffer,
                  length,
                  owns_buffer,
-                 header.destination_connection_id_length,
-                 header.source_connection_id_length,
+                 GetIncludedDestinationConnectionIdLength(header),
+                 GetIncludedSourceConnectionIdLength(header),
                  header.version_flag,
                  header.nonce != nullptr,
                  header.packet_number_length,
diff --git a/net/third_party/quic/core/quic_packets.h b/net/third_party/quic/core/quic_packets.h
index af4cbd1..2ec96aa 100644
--- a/net/third_party/quic/core/quic_packets.h
+++ b/net/third_party/quic/core/quic_packets.h
@@ -33,6 +33,21 @@
 class QuicPacket;
 struct QuicPacketHeader;
 
+// Number of connection ID bytes that are actually included over the wire.
+QUIC_EXPORT_PRIVATE QuicConnectionIdLength
+GetIncludedConnectionIdLength(QuicConnectionId connection_id,
+                              QuicConnectionIdIncluded connection_id_included);
+
+// Number of destination connection ID bytes that are actually included over the
+// wire for this particular header.
+QUIC_EXPORT_PRIVATE QuicConnectionIdLength
+GetIncludedDestinationConnectionIdLength(const QuicPacketHeader& header);
+
+// Number of source connection ID bytes that are actually included over the
+// wire for this particular header.
+QUIC_EXPORT_PRIVATE QuicConnectionIdLength
+GetIncludedSourceConnectionIdLength(const QuicPacketHeader& header);
+
 // Size in bytes of the data packet header.
 QUIC_EXPORT_PRIVATE size_t GetPacketHeaderSize(QuicTransportVersion version,
                                                const QuicPacketHeader& header);
@@ -76,9 +91,9 @@
   // Universal header. All QuicPacket headers will have a connection_id and
   // public flags.
   QuicConnectionId destination_connection_id;
-  QuicConnectionIdLength destination_connection_id_length;
+  QuicConnectionIdIncluded destination_connection_id_included;
   QuicConnectionId source_connection_id;
-  QuicConnectionIdLength source_connection_id_length;
+  QuicConnectionIdIncluded source_connection_id_included;
   // This is only used for Google QUIC.
   bool reset_flag;
   // For Google QUIC, version flag in packets from the server means version
@@ -183,7 +198,8 @@
              QuicVariableLengthIntegerLength retry_token_length_length,
              QuicByteCount retry_token_length,
              QuicVariableLengthIntegerLength length_length);
-  QuicPacket(char* buffer,
+  QuicPacket(QuicTransportVersion version,
+             char* buffer,
              size_t length,
              bool owns_buffer,
              const QuicPacketHeader& header);
diff --git a/net/third_party/quic/core/quic_sent_packet_manager_test.cc b/net/third_party/quic/core/quic_sent_packet_manager_test.cc
index 6bf238b..9e0fcea88 100644
--- a/net/third_party/quic/core/quic_sent_packet_manager_test.cc
+++ b/net/third_party/quic/core/quic_sent_packet_manager_test.cc
@@ -2046,8 +2046,7 @@
   manager_.SetNumOpenStreams(5);
 }
 
-TEST_F(QuicSentPacketManagerTest,
-       DISABLED_NegotiateNoMinTLPFromOptionsAtServer) {
+TEST_P(QuicSentPacketManagerTest, NegotiateNoMinTLPFromOptionsAtServer) {
   QuicConfig config;
   QuicTagVector options;
 
@@ -2078,8 +2077,7 @@
             QuicSentPacketManagerPeer::GetTailLossProbeDelay(&manager_, 0));
 }
 
-TEST_F(QuicSentPacketManagerTest,
-       DISABLED_NegotiateNoMinTLPFromOptionsAtClient) {
+TEST_P(QuicSentPacketManagerTest, NegotiateNoMinTLPFromOptionsAtClient) {
   QuicConfig client_config;
   QuicTagVector options;
 
@@ -2110,8 +2108,7 @@
             QuicSentPacketManagerPeer::GetTailLossProbeDelay(&manager_, 0));
 }
 
-TEST_F(QuicSentPacketManagerTest,
-       DISABLED_NegotiateIETFTLPFromOptionsAtServer) {
+TEST_P(QuicSentPacketManagerTest, NegotiateIETFTLPFromOptionsAtServer) {
   QuicConfig config;
   QuicTagVector options;
 
@@ -2139,8 +2136,7 @@
             QuicSentPacketManagerPeer::GetTailLossProbeDelay(&manager_, 0));
 }
 
-TEST_F(QuicSentPacketManagerTest,
-       DISABLED_NegotiateIETFTLPFromOptionsAtClient) {
+TEST_P(QuicSentPacketManagerTest, NegotiateIETFTLPFromOptionsAtClient) {
   QuicConfig client_config;
   QuicTagVector options;
 
@@ -2169,8 +2165,7 @@
             QuicSentPacketManagerPeer::GetTailLossProbeDelay(&manager_, 0));
 }
 
-TEST_F(QuicSentPacketManagerTest,
-       DISABLED_NegotiateNoMinRTOFromOptionsAtServer) {
+TEST_P(QuicSentPacketManagerTest, NegotiateNoMinRTOFromOptionsAtServer) {
   QuicConfig config;
   QuicTagVector options;
 
@@ -2194,8 +2189,7 @@
             QuicSentPacketManagerPeer::GetTailLossProbeDelay(&manager_, 0));
 }
 
-TEST_F(QuicSentPacketManagerTest,
-       DISABLED_NegotiateNoMinRTOFromOptionsAtClient) {
+TEST_P(QuicSentPacketManagerTest, NegotiateNoMinRTOFromOptionsAtClient) {
   QuicConfig client_config;
   QuicTagVector options;
 
@@ -2220,7 +2214,7 @@
             QuicSentPacketManagerPeer::GetTailLossProbeDelay(&manager_, 0));
 }
 
-TEST_F(QuicSentPacketManagerTest, DISABLED_NegotiateNoTLPFromOptionsAtServer) {
+TEST_P(QuicSentPacketManagerTest, NegotiateNoTLPFromOptionsAtServer) {
   QuicConfig config;
   QuicTagVector options;
 
diff --git a/net/third_party/quic/core/quic_session.cc b/net/third_party/quic/core/quic_session.cc
index e83ef93..281e28c9 100644
--- a/net/third_party/quic/core/quic_session.cc
+++ b/net/third_party/quic/core/quic_session.cc
@@ -550,14 +550,6 @@
     // up write blocked until OnCanWrite is next called.
     return QuicConsumedData(0, false);
   }
-  if (connection_->encryption_level() != ENCRYPTION_FORWARD_SECURE) {
-    // Set the next sending packets' long header type.
-    QuicLongHeaderType type = ZERO_RTT_PROTECTED;
-    if (id == QuicUtils::GetCryptoStreamId(connection_->transport_version())) {
-      type = GetCryptoStream()->GetLongHeaderType(offset);
-    }
-    connection_->SetLongHeaderType(type);
-  }
 
   QuicConsumedData data =
       connection_->SendStreamData(id, write_length, offset, state);
@@ -1614,9 +1606,6 @@
   if (!IsEncryptionEstablished()) {
     return {MESSAGE_STATUS_ENCRYPTION_NOT_ESTABLISHED, 0};
   }
-  if (connection_->encryption_level() != ENCRYPTION_FORWARD_SECURE) {
-    connection_->SetLongHeaderType(ZERO_RTT_PROTECTED);
-  }
   MessageStatus result =
       connection_->SendMessage(last_message_id_ + 1, message);
   if (result == MESSAGE_STATUS_SUCCESS) {
diff --git a/net/third_party/quic/core/quic_session_test.cc b/net/third_party/quic/core/quic_session_test.cc
index d3a73c7..830ab0e 100644
--- a/net/third_party/quic/core/quic_session_test.cc
+++ b/net/third_party/quic/core/quic_session_test.cc
@@ -81,10 +81,6 @@
   }
 
   // QuicCryptoStream implementation
-  QuicLongHeaderType GetLongHeaderType(
-      QuicStreamOffset /*offset*/) const override {
-    return HANDSHAKE;
-  }
   bool encryption_established() const override {
     return encryption_established_;
   }
diff --git a/net/third_party/quic/core/quic_stream_sequencer_buffer.cc b/net/third_party/quic/core/quic_stream_sequencer_buffer.cc
index dc3c33e..1d37b76 100644
--- a/net/third_party/quic/core/quic_stream_sequencer_buffer.cc
+++ b/net/third_party/quic/core/quic_stream_sequencer_buffer.cc
@@ -240,7 +240,7 @@
   *bytes_read = 0;
   for (size_t i = 0; i < dest_count && ReadableBytes() > 0; ++i) {
     char* dest = reinterpret_cast<char*>(dest_iov[i].iov_base);
-    CHECK_NE(dest, nullptr);
+    DCHECK(dest != nullptr);
     size_t dest_remaining = dest_iov[i].iov_len;
     while (dest_remaining > 0 && ReadableBytes() > 0) {
       size_t block_idx = NextBlockToRead();
@@ -332,7 +332,7 @@
   int iov_used = 1;
   size_t block_idx = (start_block_idx + iov_used) % blocks_count_;
   while (block_idx != end_block_idx && iov_used < iov_count) {
-    DCHECK_NE(static_cast<BufferBlock*>(nullptr), blocks_[block_idx]);
+    DCHECK(nullptr != blocks_[block_idx]);
     iov[iov_used].iov_base = blocks_[block_idx]->buffer;
     iov[iov_used].iov_len = GetBlockCapacity(block_idx);
     QUIC_DVLOG(1) << "Got block with index: " << block_idx;
@@ -342,7 +342,7 @@
 
   // Deal with last block if |iov| can hold more.
   if (iov_used < iov_count) {
-    DCHECK_NE(static_cast<BufferBlock*>(nullptr), blocks_[block_idx]);
+    DCHECK(nullptr != blocks_[block_idx]);
     iov[iov_used].iov_base = blocks_[end_block_idx]->buffer;
     iov[iov_used].iov_len = end_block_offset + 1;
     QUIC_DVLOG(1) << "Got last block with index: " << end_block_idx;
diff --git a/net/third_party/quic/core/quic_time_wait_list_manager.cc b/net/third_party/quic/core/quic_time_wait_list_manager.cc
index 6908af8..8c5be034 100644
--- a/net/third_party/quic/core/quic_time_wait_list_manager.cc
+++ b/net/third_party/quic/core/quic_time_wait_list_manager.cc
@@ -69,6 +69,7 @@
     QuicConnectionId connection_id,
     bool ietf_quic,
     TimeWaitAction action,
+    EncryptionLevel encryption_level,
     std::vector<std::unique_ptr<QuicEncryptedPacket>>* termination_packets) {
   DCHECK(action != SEND_TERMINATION_PACKETS || termination_packets != nullptr);
   DCHECK(action != DO_NOTHING || ietf_quic);
@@ -85,6 +86,7 @@
   ConnectionIdData data(num_packets, ietf_quic, clock_->ApproximateNow(),
                         action);
   if (termination_packets != nullptr) {
+    data.encryption_level = encryption_level;
     data.termination_packets.swap(*termination_packets);
   }
   connection_id_map_.emplace(std::make_pair(connection_id, std::move(data)));
@@ -99,10 +101,7 @@
 }
 
 void QuicTimeWaitListManager::OnBlockedWriterCanWrite() {
-  if (GetQuicRestartFlag(quic_check_blocked_writer_for_blockage)) {
-    QUIC_RESTART_FLAG_COUNT_N(quic_check_blocked_writer_for_blockage, 4, 6);
-    writer_->SetWritable();
-  }
+  writer_->SetWritable();
   while (!pending_packets_queue_.empty()) {
     QueuedPacket* queued_packet = pending_packets_queue_.front().get();
     if (!WriteToWire(queued_packet)) {
@@ -116,6 +115,7 @@
     const QuicSocketAddress& self_address,
     const QuicSocketAddress& peer_address,
     QuicConnectionId connection_id,
+    PacketHeaderFormat header_format,
     std::unique_ptr<QuicPerPacketContext> packet_context) {
   DCHECK(IsConnectionIdInTimeWait(connection_id));
   // TODO(satyamshekhar): Think about handling packets from different peer
@@ -133,16 +133,46 @@
   }
 
   QUIC_DLOG(INFO) << "Processing " << connection_id << " in time wait state: "
-                  << "ietf=" << connection_data->ietf_quic
+                  << "header format=" << header_format
+                  << " ietf=" << connection_data->ietf_quic
                   << ", action=" << connection_data->action
                   << ", number termination packets="
-                  << connection_data->termination_packets.size();
+                  << connection_data->termination_packets.size()
+                  << ", encryption level=" << connection_data->encryption_level;
   switch (connection_data->action) {
     case SEND_TERMINATION_PACKETS:
       if (connection_data->termination_packets.empty()) {
         QUIC_BUG << "There are no termination packets.";
         return;
       }
+      switch (header_format) {
+        case IETF_QUIC_LONG_HEADER_PACKET:
+          if (!connection_data->ietf_quic) {
+            QUIC_CODE_COUNT(quic_received_long_header_packet_for_gquic);
+          }
+          if (connection_data->encryption_level == ENCRYPTION_FORWARD_SECURE) {
+            QUIC_CODE_COUNT(
+                quic_forward_secure_termination_packets_for_long_header);
+          }
+          break;
+        case IETF_QUIC_SHORT_HEADER_PACKET:
+          if (!connection_data->ietf_quic) {
+            QUIC_CODE_COUNT(quic_received_short_header_packet_for_gquic);
+          }
+          if (connection_data->encryption_level == ENCRYPTION_NONE) {
+            QUIC_CODE_COUNT(
+                quic_encryption_none_termination_packets_for_short_header);
+          } else if (connection_data->encryption_level == ENCRYPTION_ZERO_RTT) {
+            QUIC_CODE_COUNT(quic_zero_rtt_termination_packets_for_short_header);
+          }
+          break;
+        case GOOGLE_QUIC_PACKET:
+          if (connection_data->ietf_quic) {
+            QUIC_CODE_COUNT(quic_received_gquic_packet_for_ietf_quic);
+          }
+          break;
+      }
+
       for (const auto& packet : connection_data->termination_packets) {
         SendOrQueuePacket(QuicMakeUnique<QueuedPacket>(
                               self_address, peer_address, packet->Clone()),
@@ -150,6 +180,9 @@
       }
       return;
     case SEND_STATELESS_RESET:
+      if (header_format == IETF_QUIC_LONG_HEADER_PACKET) {
+        QUIC_CODE_COUNT(quic_stateless_reset_long_header_packet);
+      }
       SendPublicReset(self_address, peer_address, connection_id,
                       connection_data->ietf_quic, std::move(packet_context));
       return;
@@ -327,6 +360,7 @@
     : num_packets(num_packets),
       ietf_quic(ietf_quic),
       time_added(time_added),
+      encryption_level(ENCRYPTION_NONE),
       action(action) {}
 
 QuicTimeWaitListManager::ConnectionIdData::ConnectionIdData(
diff --git a/net/third_party/quic/core/quic_time_wait_list_manager.h b/net/third_party/quic/core/quic_time_wait_list_manager.h
index 716078c..33ea123 100644
--- a/net/third_party/quic/core/quic_time_wait_list_manager.h
+++ b/net/third_party/quic/core/quic_time_wait_list_manager.h
@@ -79,6 +79,7 @@
       QuicConnectionId connection_id,
       bool ietf_quic,
       TimeWaitAction action,
+      EncryptionLevel encryption_level,
       std::vector<std::unique_ptr<QuicEncryptedPacket>>* termination_packets);
 
   // Returns true if the connection_id is in time wait state, false otherwise.
@@ -95,6 +96,7 @@
       const QuicSocketAddress& self_address,
       const QuicSocketAddress& peer_address,
       QuicConnectionId connection_id,
+      PacketHeaderFormat header_format,
       std::unique_ptr<QuicPerPacketContext> packet_context);
 
   // Called by the dispatcher when the underlying socket becomes writable again,
@@ -234,6 +236,8 @@
     int num_packets;
     bool ietf_quic;
     QuicTime time_added;
+    // Encryption level of termination_packets.
+    EncryptionLevel encryption_level;
     // These packets may contain CONNECTION_CLOSE frames, or SREJ messages.
     std::vector<std::unique_ptr<QuicEncryptedPacket>> termination_packets;
     TimeWaitAction action;
diff --git a/net/third_party/quic/core/quic_time_wait_list_manager_test.cc b/net/third_party/quic/core/quic_time_wait_list_manager_test.cc
index 3f2b34d..7e7190be 100644
--- a/net/third_party/quic/core/quic_time_wait_list_manager_test.cc
+++ b/net/third_party/quic/core/quic_time_wait_list_manager_test.cc
@@ -154,7 +154,7 @@
         new QuicEncryptedPacket(nullptr, 0, false)));
     time_wait_list_manager_.AddConnectionIdToTimeWait(
         connection_id, false, QuicTimeWaitListManager::SEND_TERMINATION_PACKETS,
-        &termination_packets);
+        ENCRYPTION_NONE, &termination_packets);
   }
 
   void AddConnectionId(
@@ -164,7 +164,7 @@
       std::vector<std::unique_ptr<QuicEncryptedPacket>>* packets) {
     time_wait_list_manager_.AddConnectionIdToTimeWait(
         connection_id, version.transport_version > QUIC_VERSION_43, action,
-        packets);
+        ENCRYPTION_NONE, packets);
   }
 
   bool IsConnectionIdInTimeWait(QuicConnectionId connection_id) {
@@ -173,7 +173,7 @@
 
   void ProcessPacket(QuicConnectionId connection_id) {
     time_wait_list_manager_.ProcessPacket(
-        self_address_, peer_address_, connection_id,
+        self_address_, peer_address_, connection_id, GOOGLE_QUIC_PACKET,
         QuicMakeUnique<QuicPerPacketContext>());
   }
 
diff --git a/net/third_party/quic/core/stateless_rejector.cc b/net/third_party/quic/core/stateless_rejector.cc
index e91ea78..2c8e56d6 100644
--- a/net/third_party/quic/core/stateless_rejector.cc
+++ b/net/third_party/quic/core/stateless_rejector.cc
@@ -133,8 +133,8 @@
       version_, versions_,
       /*use_stateless_rejects=*/true, server_designated_connection_id_, clock_,
       random_, compressed_certs_cache_, params_, signed_config_,
-      QuicCryptoStream::CryptoMessageFramingOverhead(
-          version_.transport_version),
+      QuicCryptoStream::CryptoMessageFramingOverhead(version_.transport_version,
+                                                     connection_id_),
       chlo_packet_size_, std::move(cb));
 }
 
diff --git a/net/third_party/quic/core/tls_client_handshaker.cc b/net/third_party/quic/core/tls_client_handshaker.cc
index bfced05..3eca0b01 100644
--- a/net/third_party/quic/core/tls_client_handshaker.cc
+++ b/net/third_party/quic/core/tls_client_handshaker.cc
@@ -166,13 +166,6 @@
   return false;
 }
 
-QuicLongHeaderType TlsClientHandshaker::GetLongHeaderType(
-    QuicStreamOffset offset) const {
-  // TODO(fayang): Returns the right header type when actually using TLS
-  // handshaker.
-  return offset == 0 ? INITIAL : HANDSHAKE;
-}
-
 QuicString TlsClientHandshaker::chlo_hash() const {
   return "";
 }
diff --git a/net/third_party/quic/core/tls_client_handshaker.h b/net/third_party/quic/core/tls_client_handshaker.h
index 43fdd742..7e4d7a1 100644
--- a/net/third_party/quic/core/tls_client_handshaker.h
+++ b/net/third_party/quic/core/tls_client_handshaker.h
@@ -46,7 +46,6 @@
   QuicString chlo_hash() const override;
 
   // From QuicCryptoClientStream::HandshakerDelegate and TlsHandshaker
-  QuicLongHeaderType GetLongHeaderType(QuicStreamOffset offset) const override;
   bool encryption_established() const override;
   bool handshake_confirmed() const override;
   const QuicCryptoNegotiatedParameters& crypto_negotiated_params()
diff --git a/net/third_party/quic/core/tls_handshaker.h b/net/third_party/quic/core/tls_handshaker.h
index 7f75464..5daa8726 100644
--- a/net/third_party/quic/core/tls_handshaker.h
+++ b/net/third_party/quic/core/tls_handshaker.h
@@ -43,8 +43,6 @@
   }
 
   // From QuicCryptoStream
-  virtual QuicLongHeaderType GetLongHeaderType(
-      QuicStreamOffset offset) const = 0;
   virtual bool encryption_established() const = 0;
   virtual bool handshake_confirmed() const = 0;
   virtual const QuicCryptoNegotiatedParameters& crypto_negotiated_params()
diff --git a/net/third_party/quic/core/tls_handshaker_test.cc b/net/third_party/quic/core/tls_handshaker_test.cc
index bfc125a..9b15ce0 100644
--- a/net/third_party/quic/core/tls_handshaker_test.cc
+++ b/net/third_party/quic/core/tls_handshaker_test.cc
@@ -140,10 +140,6 @@
 
   virtual TlsHandshaker* handshaker() const = 0;
 
-  QuicLongHeaderType GetLongHeaderType(QuicStreamOffset offset) const override {
-    return handshaker()->GetLongHeaderType(offset);
-  }
-
   bool encryption_established() const override {
     return handshaker()->encryption_established();
   }
diff --git a/net/third_party/quic/core/tls_server_handshaker.cc b/net/third_party/quic/core/tls_server_handshaker.cc
index fa3e219e..bc464ad2 100644
--- a/net/third_party/quic/core/tls_server_handshaker.cc
+++ b/net/third_party/quic/core/tls_server_handshaker.cc
@@ -133,12 +133,6 @@
   return false;
 }
 
-QuicLongHeaderType TlsServerHandshaker::GetLongHeaderType(
-    QuicStreamOffset /*offset*/) const {
-  // TODO(fayang): Returns the right value when actually using TLS handshaker.
-  return HANDSHAKE;
-}
-
 bool TlsServerHandshaker::encryption_established() const {
   return encryption_established_;
 }
diff --git a/net/third_party/quic/core/tls_server_handshaker.h b/net/third_party/quic/core/tls_server_handshaker.h
index b568797e..7e5ed90b 100644
--- a/net/third_party/quic/core/tls_server_handshaker.h
+++ b/net/third_party/quic/core/tls_server_handshaker.h
@@ -50,7 +50,6 @@
   bool ShouldSendExpectCTHeader() const override;
 
   // From QuicCryptoServerStream::HandshakerDelegate and TlsHandshaker
-  QuicLongHeaderType GetLongHeaderType(QuicStreamOffset offset) const override;
   bool encryption_established() const override;
   bool handshake_confirmed() const override;
   const QuicCryptoNegotiatedParameters& crypto_negotiated_params()
diff --git a/net/third_party/quic/platform/api/quic_epoll.h b/net/third_party/quic/platform/api/quic_epoll.h
index f64a95be..11b3be8 100644
--- a/net/third_party/quic/platform/api/quic_epoll.h
+++ b/net/third_party/quic/platform/api/quic_epoll.h
@@ -2,11 +2,6 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-// A toy server, which listens on a specified address for QUIC traffic and
-// handles incoming responses.
-//
-// Note that this server is intended to verify correctness of the client and is
-// in no way expected to be performant.
 #ifndef NET_THIRD_PARTY_QUIC_PLATFORM_API_QUIC_EPOLL_H_
 #define NET_THIRD_PARTY_QUIC_PLATFORM_API_QUIC_EPOLL_H_
 
diff --git a/net/third_party/quic/platform/api/quic_epoll_test_tools.h b/net/third_party/quic/platform/api/quic_epoll_test_tools.h
new file mode 100644
index 0000000..ed539501
--- /dev/null
+++ b/net/third_party/quic/platform/api/quic_epoll_test_tools.h
@@ -0,0 +1,12 @@
+// 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_THIRD_PARTY_QUIC_PLATFORM_API_QUIC_EPOLL_TEST_TOOLS_H_
+#define NET_THIRD_PARTY_QUIC_PLATFORM_API_QUIC_EPOLL_TEST_TOOLS_H_
+
+#include "net/third_party/quic/platform/impl/quic_epoll_test_tools_impl.h"
+
+using QuicFakeEpollServer = QuicFakeEpollServerImpl;
+
+#endif  // NET_THIRD_PARTY_QUIC_PLATFORM_API_QUIC_EPOLL_TEST_TOOLS_H_
diff --git a/net/third_party/quic/platform/api/quic_macros.h b/net/third_party/quic/platform/api/quic_macros.h
index d786df5..21a7091 100644
--- a/net/third_party/quic/platform/api/quic_macros.h
+++ b/net/third_party/quic/platform/api/quic_macros.h
@@ -7,7 +7,7 @@
 
 #include "net/third_party/quic/platform/impl/quic_macros_impl.h"
 
-#define QUIC_WARN_UNUSED_RESULT QUIC_MUST_USE_RESULT_IMPL
+#define QUIC_MUST_USE_RESULT QUIC_MUST_USE_RESULT_IMPL
 #define QUIC_UNUSED QUIC_UNUSED_IMPL
 
 #endif  // NET_THIRD_PARTY_QUIC_PLATFORM_API_QUIC_MACROS_H_
diff --git a/net/third_party/quic/platform/api/quic_system_event_loop.h b/net/third_party/quic/platform/api/quic_system_event_loop.h
new file mode 100644
index 0000000..3a3c6f4
--- /dev/null
+++ b/net/third_party/quic/platform/api/quic_system_event_loop.h
@@ -0,0 +1,14 @@
+// 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_THIRD_PARTY_QUIC_PLATFORM_API_QUIC_SYSTEM_EVENT_LOOP_H_
+#define NET_THIRD_PARTY_QUIC_PLATFORM_API_QUIC_SYSTEM_EVENT_LOOP_H_
+
+#include "net/third_party/quic/platform/impl/quic_system_event_loop_impl.h"
+
+inline void QuicRunSystemEventLoopIteration() {
+  QuicRunSystemEventLoopIterationImpl();
+}
+
+#endif  // NET_THIRD_PARTY_QUIC_PLATFORM_API_QUIC_SYSTEM_EVENT_LOOP_H_
diff --git a/net/third_party/quic/platform/api/quic_test.h b/net/third_party/quic/platform/api/quic_test.h
index 979c785..4ebe1de6 100644
--- a/net/third_party/quic/platform/api/quic_test.h
+++ b/net/third_party/quic/platform/api/quic_test.h
@@ -17,4 +17,11 @@
 // Class which needs to be instantiated in tests which use threads.
 using ScopedEnvironmentForThreads = ScopedEnvironmentForThreadsImpl;
 
+#define QUIC_TEST_DISABLED_IN_CHROME(name) \
+  QUIC_TEST_DISABLED_IN_CHROME_IMPL(name)
+
+inline std::string QuicGetTestMemoryCachePath() {
+  return QuicGetTestMemoryCachePathImpl();
+}
+
 #endif  // NET_THIRD_PARTY_QUIC_PLATFORM_API_QUIC_TEST_H_
diff --git a/net/third_party/quic/platform/impl/quic_epoll_test_tools_impl.h b/net/third_party/quic/platform/impl/quic_epoll_test_tools_impl.h
new file mode 100644
index 0000000..2670140
--- /dev/null
+++ b/net/third_party/quic/platform/impl/quic_epoll_test_tools_impl.h
@@ -0,0 +1,12 @@
+// 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_THIRD_PARTY_QUIC_PLATFORM_IMPL_QUIC_EPOLL_TEST_TOOLS_IMPL_H_
+#define NET_THIRD_PARTY_QUIC_PLATFORM_IMPL_QUIC_EPOLL_TEST_TOOLS_IMPL_H_
+
+#include "net/third_party/quic/test_tools/fake_epoll_server.h"
+
+using QuicFakeEpollServerImpl = quic::test::FakeEpollServer;
+
+#endif  // NET_THIRD_PARTY_QUIC_PLATFORM_IMPL_QUIC_EPOLL_TEST_TOOLS_IMPL_H_
diff --git a/net/third_party/quic/platform/impl/quic_system_event_loop_impl.h b/net/third_party/quic/platform/impl/quic_system_event_loop_impl.h
new file mode 100644
index 0000000..0d35bf9
--- /dev/null
+++ b/net/third_party/quic/platform/impl/quic_system_event_loop_impl.h
@@ -0,0 +1,14 @@
+// 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_THIRD_PARTY_QUIC_PLATFORM_IMPL_QUIC_SYSTEM_EVENT_LOOP_IMPL_H_
+#define NET_THIRD_PARTY_QUIC_PLATFORM_IMPL_QUIC_SYSTEM_EVENT_LOOP_IMPL_H_
+
+#include "base/run_loop.h"
+
+inline void QuicRunSystemEventLoopIterationImpl() {
+  base::RunLoop().RunUntilIdle();
+}
+
+#endif  // NET_THIRD_PARTY_QUIC_PLATFORM_IMPL_QUIC_SYSTEM_EVENT_LOOP_IMPL_H_
diff --git a/net/third_party/quic/platform/impl/quic_test_impl.cc b/net/third_party/quic/platform/impl/quic_test_impl.cc
index 06607e0..ce7b042b 100644
--- a/net/third_party/quic/platform/impl/quic_test_impl.cc
+++ b/net/third_party/quic/platform/impl/quic_test_impl.cc
@@ -4,6 +4,9 @@
 
 #include "net/third_party/quic/platform/impl/quic_test_impl.h"
 
+#include "base/files/file_path.h"
+#include "base/path_service.h"
+
 QuicFlagSaverImpl::QuicFlagSaverImpl() {
 #define QUIC_FLAG(type, flag, value) saved_##flag##_ = flag;
 #include "net/quic/quic_flags_list.h"
@@ -15,3 +18,12 @@
 #include "net/quic/quic_flags_list.h"
 #undef QUIC_FLAG
 }
+
+std::string QuicGetTestMemoryCachePathImpl() {
+  base::FilePath path;
+  base::PathService::Get(base::DIR_SOURCE_ROOT, &path);
+  path = path.AppendASCII("net").AppendASCII("data").AppendASCII(
+      "quic_http_response_cache_data");
+  // The file path is known to be an ascii string.
+  return path.MaybeAsASCII();
+}
diff --git a/net/third_party/quic/platform/impl/quic_test_impl.h b/net/third_party/quic/platform/impl/quic_test_impl.h
index ca40fcc..e553f65 100644
--- a/net/third_party/quic/platform/impl/quic_test_impl.h
+++ b/net/third_party/quic/platform/impl/quic_test_impl.h
@@ -61,4 +61,8 @@
   base::test::ScopedTaskEnvironment scoped_task_environment_;
 };
 
+#define QUIC_TEST_DISABLED_IN_CHROME_IMPL(name) DISABLED_##name
+
+std::string QuicGetTestMemoryCachePathImpl();
+
 #endif  // NET_THIRD_PARTY_QUIC_PLATFORM_IMPL_QUIC_TEST_IMPL_H_
diff --git a/net/third_party/quic/quartc/quartc_dispatcher.cc b/net/third_party/quic/quartc/quartc_dispatcher.cc
index 024006fe..fa7b0ac 100644
--- a/net/third_party/quic/quartc/quartc_dispatcher.cc
+++ b/net/third_party/quic/quartc/quartc_dispatcher.cc
@@ -14,7 +14,7 @@
     std::unique_ptr<QuicConfig> config,
     std::unique_ptr<QuicCryptoServerConfig> crypto_config,
     QuicStringPiece crypto_config_serialized,
-    std::unique_ptr<QuicVersionManager> version_manager,
+    QuicVersionManager* version_manager,
     std::unique_ptr<QuicConnectionHelperInterface> helper,
     std::unique_ptr<QuicCryptoServerStream::Helper> session_helper,
     std::unique_ptr<QuicAlarmFactory> alarm_factory,
@@ -22,14 +22,13 @@
     Delegate* delegate)
     : QuicDispatcher(config.get(),
                      crypto_config.get(),
-                     version_manager.get(),
+                     version_manager,
                      std::move(helper),
                      std::move(session_helper),
                      std::move(alarm_factory)),
       owned_quic_config_(std::move(config)),
       owned_crypto_config_(std::move(crypto_config)),
       crypto_config_(crypto_config_serialized),
-      owned_version_manager_(std::move(version_manager)),
       delegate_(delegate),
       packet_writer_(packet_writer.get()) {
   // QuicDispatcher takes ownership of the writer.
diff --git a/net/third_party/quic/quartc/quartc_dispatcher.h b/net/third_party/quic/quartc/quartc_dispatcher.h
index 82f41853..c68fea4 100644
--- a/net/third_party/quic/quartc/quartc_dispatcher.h
+++ b/net/third_party/quic/quartc/quartc_dispatcher.h
@@ -33,7 +33,7 @@
       std::unique_ptr<QuicConfig> config,
       std::unique_ptr<QuicCryptoServerConfig> crypto_config,
       QuicStringPiece crypto_config_serialized,
-      std::unique_ptr<QuicVersionManager> version_manager,
+      QuicVersionManager* version_manager,
       std::unique_ptr<QuicConnectionHelperInterface> helper,
       std::unique_ptr<QuicCryptoServerStream::Helper> session_helper,
       std::unique_ptr<QuicAlarmFactory> alarm_factory,
@@ -58,7 +58,6 @@
   std::unique_ptr<QuicConfig> owned_quic_config_;
   std::unique_ptr<QuicCryptoServerConfig> owned_crypto_config_;
   QuicString crypto_config_;
-  std::unique_ptr<QuicVersionManager> owned_version_manager_;
 
   // Delegate invoked when the dispatcher creates a new session.
   Delegate* delegate_;
diff --git a/net/third_party/quic/quartc/quartc_endpoint.cc b/net/third_party/quic/quartc/quartc_endpoint.cc
index dae52db..8d7e0882 100644
--- a/net/third_party/quic/quartc/quartc_endpoint.cc
+++ b/net/third_party/quic/quartc/quartc_endpoint.cc
@@ -57,11 +57,15 @@
     const QuicClock* clock,
     QuartcEndpoint::Delegate* delegate,
     const QuartcSessionConfig& config,
-    QuicStringPiece serialized_server_config)
+    QuicStringPiece serialized_server_config,
+    std::unique_ptr<QuicVersionManager> version_manager)
     : alarm_factory_(alarm_factory),
       clock_(clock),
       delegate_(delegate),
       serialized_server_config_(serialized_server_config),
+      version_manager_(version_manager ? std::move(version_manager)
+                                       : QuicMakeUnique<QuicVersionManager>(
+                                             AllSupportedVersions())),
       create_session_alarm_(QuicWrapUnique(
           alarm_factory_->CreateAlarm(new CreateSessionDelegate(this)))),
       factory_(QuicMakeUnique<QuartcFactory>(
@@ -75,17 +79,23 @@
 
 void QuartcClientEndpoint::OnCreateSessionAlarm() {
   session_ = factory_->CreateQuartcClientSession(
-      config_, serialized_server_config_, packet_transport_);
+      config_, version_manager_->GetSupportedVersions(),
+      serialized_server_config_, packet_transport_);
   delegate_->OnSessionCreated(session_.get());
 }
 
-QuartcServerEndpoint::QuartcServerEndpoint(QuicAlarmFactory* alarm_factory,
-                                           const QuicClock* clock,
-                                           QuartcEndpoint::Delegate* delegate,
-                                           const QuartcSessionConfig& config)
+QuartcServerEndpoint::QuartcServerEndpoint(
+    QuicAlarmFactory* alarm_factory,
+    const QuicClock* clock,
+    QuartcEndpoint::Delegate* delegate,
+    const QuartcSessionConfig& config,
+    std::unique_ptr<QuicVersionManager> version_manager)
     : alarm_factory_(alarm_factory),
       delegate_(delegate),
       config_(config),
+      version_manager_(version_manager ? std::move(version_manager)
+                                       : QuicMakeUnique<QuicVersionManager>(
+                                             AllSupportedVersions())),
       pre_connection_helper_(QuicMakeUnique<QuartcConnectionHelper>(clock)),
       crypto_config_(
           CreateCryptoServerConfig(pre_connection_helper_->GetRandomGenerator(),
@@ -97,8 +107,7 @@
   dispatcher_ = QuicMakeUnique<QuartcDispatcher>(
       QuicMakeUnique<QuicConfig>(CreateQuicConfig(config_)),
       std::move(crypto_config_.config), crypto_config_.serialized_crypto_config,
-      QuicMakeUnique<QuicVersionManager>(AllSupportedVersions()),
-      std::move(pre_connection_helper_),
+      version_manager_.get(), std::move(pre_connection_helper_),
       QuicMakeUnique<QuartcCryptoServerStreamHelper>(),
       QuicMakeUnique<QuartcAlarmFactoryWrapper>(alarm_factory_),
       QuicMakeUnique<QuartcPacketWriter>(packet_transport,
diff --git a/net/third_party/quic/quartc/quartc_endpoint.h b/net/third_party/quic/quartc/quartc_endpoint.h
index 8d1b834..0cedad2 100644
--- a/net/third_party/quic/quartc/quartc_endpoint.h
+++ b/net/third_party/quic/quartc/quartc_endpoint.h
@@ -61,11 +61,13 @@
  public:
   // |alarm_factory|, |clock|, and |delegate| are owned by the caller and must
   // outlive the endpoint.
-  QuartcClientEndpoint(QuicAlarmFactory* alarm_factory,
-                       const QuicClock* clock,
-                       Delegate* delegate,
-                       const QuartcSessionConfig& config,
-                       QuicStringPiece serialized_server_config);
+  QuartcClientEndpoint(
+      QuicAlarmFactory* alarm_factory,
+      const QuicClock* clock,
+      Delegate* delegate,
+      const QuartcSessionConfig& config,
+      QuicStringPiece serialized_server_config,
+      std::unique_ptr<QuicVersionManager> version_manager = nullptr);
 
   void Connect(QuartcPacketTransport* packet_transport) override;
 
@@ -97,6 +99,9 @@
   // Server config.  If valid, used to perform a 0-RTT connection.
   const QuicString serialized_server_config_;
 
+  // Version manager.  May be injected to control version negotiation in tests.
+  std::unique_ptr<QuicVersionManager> version_manager_;
+
   // Alarm for creating sessions asynchronously.  The alarm is set when
   // Connect() is called.  When it fires, the endpoint creates a session and
   // calls the delegate.
@@ -124,10 +129,12 @@
 class QuartcServerEndpoint : public QuartcEndpoint,
                              public QuartcDispatcher::Delegate {
  public:
-  QuartcServerEndpoint(QuicAlarmFactory* alarm_factory,
-                       const QuicClock* clock,
-                       QuartcEndpoint::Delegate* delegate,
-                       const QuartcSessionConfig& config);
+  QuartcServerEndpoint(
+      QuicAlarmFactory* alarm_factory,
+      const QuicClock* clock,
+      QuartcEndpoint::Delegate* delegate,
+      const QuartcSessionConfig& config,
+      std::unique_ptr<QuicVersionManager> version_manager = nullptr);
 
   // Implements QuartcEndpoint.
   void Connect(QuartcPacketTransport* packet_transport) override;
@@ -151,6 +158,9 @@
   // Config to be used for new sessions.
   QuartcSessionConfig config_;
 
+  // Version manager.  May be injected to control version negotiation in tests.
+  std::unique_ptr<QuicVersionManager> version_manager_;
+
   // QuartcDispatcher waits for an incoming CHLO, then either rejects it or
   // creates a session to respond to it.  The dispatcher owns all sessions it
   // creates.
diff --git a/net/third_party/quic/quartc/quartc_endpoint_test.cc b/net/third_party/quic/quartc/quartc_endpoint_test.cc
index 4a8ae13b..4a1aaa18 100644
--- a/net/third_party/quic/quartc/quartc_endpoint_test.cc
+++ b/net/third_party/quic/quartc/quartc_endpoint_test.cc
@@ -4,59 +4,207 @@
 
 #include "net/third_party/quic/quartc/quartc_endpoint.h"
 
+#include "net/third_party/quic/core/quic_versions.h"
 #include "net/third_party/quic/platform/api/quic_test.h"
+#include "net/third_party/quic/quartc/quartc_crypto_helpers.h"
+#include "net/third_party/quic/quartc/quartc_fakes.h"
 #include "net/third_party/quic/quartc/simulated_packet_transport.h"
+#include "net/third_party/quic/test_tools/simulator/link.h"
 #include "net/third_party/quic/test_tools/simulator/simulator.h"
-#include "testing/gmock/include/gmock/gmock.h"
-#include "testing/gtest/include/gtest/gtest.h"
 
 namespace quic {
 namespace {
 
-static QuicByteCount kDefaultMaxPacketSize = 1200;
-
-class FakeEndpointDelegate : public QuartcEndpoint::Delegate {
- public:
-  void OnSessionCreated(QuartcSession* session) override {
-    last_session_ = session;
-  }
-
-  void OnConnectError(QuicErrorCode /*error*/,
-                      const QuicString& /*error_details*/) override {}
-
-  QuartcSession* last_session() { return last_session_; }
-
- private:
-  QuartcSession* last_session_ = nullptr;
-};
-
 class QuartcEndpointTest : public QuicTest {
  protected:
   QuartcEndpointTest()
-      : transport_(&simulator_,
-                   "client_transport",
-                   "server_transport",
-                   10 * kDefaultMaxPacketSize) {}
+      : client_transport_(&simulator_,
+                          "client_transport",
+                          "server_transport",
+                          10 * kDefaultMaxPacketSize),
+        server_transport_(&simulator_,
+                          "server_transport",
+                          "client_transport",
+                          10 * kDefaultMaxPacketSize),
+        client_server_link_(&client_transport_,
+                            &server_transport_,
+                            QuicBandwidth::FromKBitsPerSecond(10000),
+                            QuicTime::Delta::FromMilliseconds(1)),
+        server_session_delegate_(&server_stream_delegate_,
+                                 simulator_.GetClock()),
+        server_endpoint_delegate_(&server_session_delegate_),
+        server_endpoint_(
+            QuicMakeUnique<QuartcServerEndpoint>(simulator_.GetAlarmFactory(),
+                                                 simulator_.GetClock(),
+                                                 &server_endpoint_delegate_,
+                                                 QuartcSessionConfig())),
+        client_session_delegate_(&client_stream_delegate_,
+                                 simulator_.GetClock()),
+        client_endpoint_delegate_(&client_session_delegate_),
+        client_endpoint_(QuicMakeUnique<QuartcClientEndpoint>(
+            simulator_.GetAlarmFactory(),
+            simulator_.GetClock(),
+            &client_endpoint_delegate_,
+            QuartcSessionConfig(),
+            /*serialized_server_config=*/"")) {}
 
   simulator::Simulator simulator_;
-  simulator::SimulatedQuartcPacketTransport transport_;
-  FakeEndpointDelegate delegate_;
+
+  simulator::SimulatedQuartcPacketTransport client_transport_;
+  simulator::SimulatedQuartcPacketTransport server_transport_;
+  simulator::SymmetricLink client_server_link_;
+
+  FakeQuartcStreamDelegate server_stream_delegate_;
+  FakeQuartcSessionDelegate server_session_delegate_;
+  FakeQuartcEndpointDelegate server_endpoint_delegate_;
+
+  std::unique_ptr<QuartcServerEndpoint> server_endpoint_;
+
+  FakeQuartcStreamDelegate client_stream_delegate_;
+  FakeQuartcSessionDelegate client_session_delegate_;
+  FakeQuartcEndpointDelegate client_endpoint_delegate_;
+
+  std::unique_ptr<QuartcClientEndpoint> client_endpoint_;
 };
 
 // After calling Connect, the client endpoint must wait for an async callback.
 // The callback occurs after a finite amount of time and produces a session.
 TEST_F(QuartcEndpointTest, ClientCreatesSessionAsynchronously) {
-  QuartcSessionConfig config;
-  config.max_packet_size = kDefaultMaxPacketSize;
-  QuartcClientEndpoint endpoint_(simulator_.GetAlarmFactory(),
-                                 simulator_.GetClock(), &delegate_, config,
-                                 /*serialized_server_config=*/"");
-  endpoint_.Connect(&transport_);
+  client_endpoint_->Connect(&client_transport_);
 
-  EXPECT_EQ(delegate_.last_session(), nullptr);
+  EXPECT_EQ(client_endpoint_delegate_.session(), nullptr);
 
   EXPECT_TRUE(simulator_.RunUntil(
-      [this] { return delegate_.last_session() != nullptr; }));
+      [this] { return client_endpoint_delegate_.session() != nullptr; }));
+}
+
+// Tests that the server can negotiate for an older QUIC version if the client
+// attempts to connect using a newer version.
+TEST_F(QuartcEndpointTest, DISABLED_ServerNegotiatesForOldVersion) {
+  // Note: for this test, we need support for two versions.  Which two shouldn't
+  // matter, but they must be enabled so that the version manager doesn't filter
+  // them out.
+  SetQuicReloadableFlag(quic_enable_version_46, true);
+  SetQuicReloadableFlag(quic_enable_version_43, true);
+
+  // Reset the client endpoint to prefer version 46 but also be capable of
+  // speaking version 43.
+  ParsedQuicVersionVector client_versions;
+  client_versions.push_back({PROTOCOL_QUIC_CRYPTO, QUIC_VERSION_46});
+  client_versions.push_back({PROTOCOL_QUIC_CRYPTO, QUIC_VERSION_43});
+  client_endpoint_ = QuicMakeUnique<QuartcClientEndpoint>(
+      simulator_.GetAlarmFactory(), simulator_.GetClock(),
+      &client_endpoint_delegate_, QuartcSessionConfig(),
+      /*serialized_server_config=*/"",
+      QuicMakeUnique<QuicVersionManager>(client_versions));
+
+  // Reset the server endpoint to only speak version 43.
+  ParsedQuicVersionVector server_versions;
+  server_versions.push_back({PROTOCOL_QUIC_CRYPTO, QUIC_VERSION_43});
+  server_endpoint_ = QuicMakeUnique<QuartcServerEndpoint>(
+      simulator_.GetAlarmFactory(), simulator_.GetClock(),
+      &server_endpoint_delegate_, QuartcSessionConfig(),
+      QuicMakeUnique<QuicVersionManager>(server_versions));
+
+  // The endpoints should be able to establish a connection using version 46.
+  server_endpoint_->Connect(&server_transport_);
+  client_endpoint_->Connect(&client_transport_);
+
+  ASSERT_TRUE(simulator_.RunUntil([this] {
+    return client_endpoint_delegate_.session() != nullptr &&
+           client_endpoint_delegate_.session()->IsEncryptionEstablished() &&
+           server_endpoint_delegate_.session() != nullptr &&
+           server_endpoint_delegate_.session()->IsEncryptionEstablished();
+  }));
+  EXPECT_EQ(client_endpoint_delegate_.session()->connection()->version(),
+            server_versions[0]);
+  EXPECT_EQ(server_endpoint_delegate_.session()->connection()->version(),
+            server_versions[0]);
+}
+
+// Tests that the server can accept connections from clients that use older
+// QUIC versions.
+TEST_F(QuartcEndpointTest, DISABLED_ServerAcceptsOldVersion) {
+  // Note: for this test, we need support for two versions.  Which two shouldn't
+  // matter, but they must be enabled so that the version manager doesn't filter
+  // them out.
+  SetQuicReloadableFlag(quic_enable_version_46, true);
+  SetQuicReloadableFlag(quic_enable_version_43, true);
+
+  // Reset the client endpoint to only speak version 43.
+  ParsedQuicVersionVector client_versions;
+  client_versions.push_back({PROTOCOL_QUIC_CRYPTO, QUIC_VERSION_43});
+  client_endpoint_ = QuicMakeUnique<QuartcClientEndpoint>(
+      simulator_.GetAlarmFactory(), simulator_.GetClock(),
+      &client_endpoint_delegate_, QuartcSessionConfig(),
+      /*serialized_server_config=*/"",
+      QuicMakeUnique<QuicVersionManager>(client_versions));
+
+  // Reset the server endpoint to prefer version 46 but also be capable of
+  // speaking version 43.
+  ParsedQuicVersionVector server_versions;
+  server_versions.push_back({PROTOCOL_QUIC_CRYPTO, QUIC_VERSION_46});
+  server_versions.push_back({PROTOCOL_QUIC_CRYPTO, QUIC_VERSION_43});
+  server_endpoint_ = QuicMakeUnique<QuartcServerEndpoint>(
+      simulator_.GetAlarmFactory(), simulator_.GetClock(),
+      &server_endpoint_delegate_, QuartcSessionConfig(),
+      QuicMakeUnique<QuicVersionManager>(server_versions));
+
+  // The endpoints should be able to establish a connection using version 46.
+  server_endpoint_->Connect(&server_transport_);
+  client_endpoint_->Connect(&client_transport_);
+
+  ASSERT_TRUE(simulator_.RunUntil([this] {
+    return client_endpoint_delegate_.session() != nullptr &&
+           client_endpoint_delegate_.session()->IsEncryptionEstablished() &&
+           server_endpoint_delegate_.session() != nullptr &&
+           server_endpoint_delegate_.session()->IsEncryptionEstablished();
+  }));
+  EXPECT_EQ(client_endpoint_delegate_.session()->connection()->version(),
+            client_versions[0]);
+  EXPECT_EQ(server_endpoint_delegate_.session()->connection()->version(),
+            client_versions[0]);
+}
+
+// Tests that version negotiation fails when the client and server support
+// completely disjoint sets of versions.
+TEST_F(QuartcEndpointTest, VersionNegotiationWithDisjointVersions) {
+  // Note: for this test, we need support for two versions.  Which two shouldn't
+  // matter, but they must be enabled so that the version manager doesn't filter
+  // them out.
+  SetQuicReloadableFlag(quic_enable_version_46, true);
+  SetQuicReloadableFlag(quic_enable_version_43, true);
+
+  // Reset the client endpoint to only speak version 43.
+  ParsedQuicVersionVector client_versions;
+  client_versions.push_back({PROTOCOL_QUIC_CRYPTO, QUIC_VERSION_43});
+  client_endpoint_ = QuicMakeUnique<QuartcClientEndpoint>(
+      simulator_.GetAlarmFactory(), simulator_.GetClock(),
+      &client_endpoint_delegate_, QuartcSessionConfig(),
+      /*serialized_server_config=*/"",
+      QuicMakeUnique<QuicVersionManager>(client_versions));
+
+  // Reset the server endpoint to only speak version 46.
+  ParsedQuicVersionVector server_versions;
+  server_versions.push_back({PROTOCOL_QUIC_CRYPTO, QUIC_VERSION_46});
+  server_endpoint_ = QuicMakeUnique<QuartcServerEndpoint>(
+      simulator_.GetAlarmFactory(), simulator_.GetClock(),
+      &server_endpoint_delegate_, QuartcSessionConfig(),
+      QuicMakeUnique<QuicVersionManager>(server_versions));
+
+  // The endpoints should be unable to establish a connection.
+  server_endpoint_->Connect(&server_transport_);
+  client_endpoint_->Connect(&client_transport_);
+
+  // Note that the error is reported from the client and *not* the server.  The
+  // server sees an invalid version, sends a version negotiation packet, and
+  // never gets a response, because the client stops sending when it can't find
+  // a mutually supported versions.
+  ASSERT_TRUE(simulator_.RunUntil([this] {
+    return client_endpoint_delegate_.session() != nullptr &&
+           client_endpoint_delegate_.session()->error() != QUIC_NO_ERROR;
+  }));
+  EXPECT_EQ(client_endpoint_delegate_.session()->error(), QUIC_INVALID_VERSION);
 }
 
 }  // namespace
diff --git a/net/third_party/quic/quartc/quartc_factory.cc b/net/third_party/quic/quartc/quartc_factory.cc
index 0dea74b..e0f148939 100644
--- a/net/third_party/quic/quartc/quartc_factory.cc
+++ b/net/third_party/quic/quartc/quartc_factory.cc
@@ -26,6 +26,7 @@
 
 std::unique_ptr<QuartcSession> QuartcFactory::CreateQuartcClientSession(
     const QuartcSessionConfig& quartc_session_config,
+    const ParsedQuicVersionVector& supported_versions,
     QuicStringPiece server_crypto_config,
     QuartcPacketTransport* packet_transport) {
   DCHECK(packet_transport);
@@ -37,12 +38,12 @@
   // While the QuicConfig is not directly used by the connection, creating it
   // also sets flag values which must be set before creating the connection.
   QuicConfig quic_config = CreateQuicConfig(quartc_session_config);
-  std::unique_ptr<QuicConnection> quic_connection =
-      CreateQuicConnection(Perspective::IS_CLIENT, writer.get());
+  std::unique_ptr<QuicConnection> quic_connection = CreateQuicConnection(
+      Perspective::IS_CLIENT, supported_versions, writer.get());
 
   return QuicMakeUnique<QuartcClientSession>(
-      std::move(quic_connection), quic_config, CurrentSupportedVersions(),
-      clock_, std::move(writer),
+      std::move(quic_connection), quic_config, supported_versions, clock_,
+      std::move(writer),
       CreateCryptoClientConfig(quartc_session_config.pre_shared_key),
       server_crypto_config);
 }
@@ -79,6 +80,7 @@
   // Forcing flag to false to pass blueprint tests, but eventually we'll have
   // to implement negotiation outside of QuicConnection.
   SetQuicRestartFlag(quic_no_server_conn_ver_negotiation2, false);
+  SetQuicReloadableFlag(quic_no_client_conn_ver_negotiation, false);
 
   QuicTagVector copt;
   copt.push_back(kNSTP);
@@ -176,6 +178,7 @@
 
 std::unique_ptr<QuicConnection> QuartcFactory::CreateQuicConnection(
     Perspective perspective,
+    const ParsedQuicVersionVector& supported_versions,
     QuartcPacketWriter* packet_writer) {
   // |dummy_id| and |dummy_address| are used because Quartc network layer will
   // not use these two.
@@ -185,7 +188,7 @@
     QuicSocketAddress dummy_address(QuicIpAddress::Any4(), /*port=*/0);
     return quic::CreateQuicConnection(
         dummy_id, dummy_address, connection_helper_.get(), alarm_factory_,
-        packet_writer, perspective, CurrentSupportedVersions());
+        packet_writer, perspective, supported_versions);
 }
 
 std::unique_ptr<QuicConnection> CreateQuicConnection(
diff --git a/net/third_party/quic/quartc/quartc_factory.h b/net/third_party/quic/quartc/quartc_factory.h
index d4b15614..87acf91 100644
--- a/net/third_party/quic/quartc/quartc_factory.h
+++ b/net/third_party/quic/quartc/quartc_factory.h
@@ -58,12 +58,14 @@
   // Creates a new QuartcSession using the given configuration.
   std::unique_ptr<QuartcSession> CreateQuartcClientSession(
       const QuartcSessionConfig& quartc_session_config,
+      const ParsedQuicVersionVector& supported_versions,
       QuicStringPiece server_crypto_config,
       QuartcPacketTransport* packet_transport);
 
  private:
   std::unique_ptr<QuicConnection> CreateQuicConnection(
       Perspective perspective,
+      const ParsedQuicVersionVector& supported_versions,
       QuartcPacketWriter* packet_writer);
 
   // Used to implement QuicAlarmFactory.  Owned by the user and must outlive
diff --git a/net/third_party/quic/quartc/quartc_fakes.h b/net/third_party/quic/quartc/quartc_fakes.h
new file mode 100644
index 0000000..801c6f9
--- /dev/null
+++ b/net/third_party/quic/quartc/quartc_fakes.h
@@ -0,0 +1,138 @@
+// Copyright (c) 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 NET_THIRD_PARTY_QUIC_QUARTC_QUARTC_FAKES_H_
+#define NET_THIRD_PARTY_QUIC_QUARTC_QUARTC_FAKES_H_
+
+#include "net/third_party/quic/core/quic_error_codes.h"
+#include "net/third_party/quic/core/quic_types.h"
+#include "net/third_party/quic/platform/api/quic_clock.h"
+#include "net/third_party/quic/platform/api/quic_string.h"
+#include "net/third_party/quic/quartc/quartc_endpoint.h"
+#include "net/third_party/quic/quartc/quartc_session.h"
+#include "net/third_party/quic/quartc/quartc_stream.h"
+
+namespace quic {
+
+class FakeQuartcEndpointDelegate : public QuartcEndpoint::Delegate {
+ public:
+  explicit FakeQuartcEndpointDelegate(QuartcSession::Delegate* session_delegate)
+      : session_delegate_(session_delegate) {}
+
+  void OnSessionCreated(QuartcSession* session) override {
+    CHECK_EQ(session_, nullptr);
+    CHECK_NE(session, nullptr);
+    session_ = session;
+    session_->SetDelegate(session_delegate_);
+    session_->StartCryptoHandshake();
+  }
+
+  void OnConnectError(QuicErrorCode error,
+                      const QuicString& error_details) override {
+    LOG(FATAL) << "Unexpected error during QuartcEndpoint::Connect(); error="
+               << error << ", error_details=" << error_details;
+  }
+
+  QuartcSession* session() { return session_; }
+
+ private:
+  QuartcSession::Delegate* session_delegate_;
+  QuartcSession* session_ = nullptr;
+};
+
+class FakeQuartcSessionDelegate : public QuartcSession::Delegate {
+ public:
+  explicit FakeQuartcSessionDelegate(QuartcStream::Delegate* stream_delegate,
+                                     const QuicClock* clock)
+      : stream_delegate_(stream_delegate), clock_(clock) {}
+
+  void OnConnectionWritable() override {
+    LOG(INFO) << "Connection writable!";
+    if (!writable_time_.IsInitialized()) {
+      writable_time_ = clock_->Now();
+    }
+  }
+
+  // Called when peers have established forward-secure encryption
+  void OnCryptoHandshakeComplete() override {
+    LOG(INFO) << "Crypto handshake complete!";
+    crypto_handshake_time_ = clock_->Now();
+  }
+
+  // Called when connection closes locally, or remotely by peer.
+  void OnConnectionClosed(QuicErrorCode error_code,
+                          const QuicString& error_details,
+                          ConnectionCloseSource source) override {
+    connected_ = false;
+  }
+
+  // Called when an incoming QUIC stream is created.
+  void OnIncomingStream(QuartcStream* quartc_stream) override {
+    last_incoming_stream_ = quartc_stream;
+    last_incoming_stream_->SetDelegate(stream_delegate_);
+  }
+
+  void OnMessageReceived(QuicStringPiece message) override {
+    incoming_messages_.emplace_back(message);
+  }
+
+  void OnCongestionControlChange(QuicBandwidth bandwidth_estimate,
+                                 QuicBandwidth pacing_rate,
+                                 QuicTime::Delta latest_rtt) override {}
+
+  QuartcStream* last_incoming_stream() { return last_incoming_stream_; }
+
+  // Returns all received messages.
+  const std::vector<QuicString>& incoming_messages() {
+    return incoming_messages_;
+  }
+
+  bool connected() { return connected_; }
+  QuicTime writable_time() const { return writable_time_; }
+  QuicTime crypto_handshake_time() const { return crypto_handshake_time_; }
+
+ private:
+  QuartcStream* last_incoming_stream_;
+  std::vector<QuicString> incoming_messages_;
+  bool connected_ = true;
+  QuartcStream::Delegate* stream_delegate_;
+  QuicTime writable_time_ = QuicTime::Zero();
+  QuicTime crypto_handshake_time_ = QuicTime::Zero();
+  const QuicClock* clock_;
+};
+
+class FakeQuartcStreamDelegate : public QuartcStream::Delegate {
+ public:
+  size_t OnReceived(QuartcStream* stream,
+                    iovec* iov,
+                    size_t iov_length,
+                    bool fin) override {
+    size_t bytes_consumed = 0;
+    for (size_t i = 0; i < iov_length; ++i) {
+      received_data_[stream->id()] +=
+          QuicString(static_cast<const char*>(iov[i].iov_base), iov[i].iov_len);
+      bytes_consumed += iov[i].iov_len;
+    }
+    return bytes_consumed;
+  }
+
+  void OnClose(QuartcStream* stream) override {
+    errors_[stream->id()] = stream->stream_error();
+  }
+
+  void OnBufferChanged(QuartcStream* stream) override {}
+
+  bool has_data() { return !received_data_.empty(); }
+  std::map<QuicStreamId, QuicString> data() { return received_data_; }
+
+  QuicRstStreamErrorCode stream_error(QuicStreamId id) { return errors_[id]; }
+
+ private:
+  std::map<QuicStreamId, QuicString> received_data_;
+  std::map<QuicStreamId, QuicRstStreamErrorCode> errors_;
+};
+
+}  // namespace quic
+
+#endif  // NET_THIRD_PARTY_QUIC_QUARTC_QUARTC_FAKES_H_
diff --git a/net/third_party/quic/quartc/quartc_session_test.cc b/net/third_party/quic/quartc/quartc_session_test.cc
index 5f94191..fb05282c 100644
--- a/net/third_party/quic/quartc/quartc_session_test.cc
+++ b/net/third_party/quic/quartc/quartc_session_test.cc
@@ -4,7 +4,6 @@
 
 #include "net/third_party/quic/quartc/quartc_session.h"
 
-#include "build/build_config.h"
 #include "net/third_party/quic/core/quic_simple_buffer_allocator.h"
 #include "net/third_party/quic/core/quic_types.h"
 #include "net/third_party/quic/core/tls_client_handshaker.h"
@@ -16,6 +15,7 @@
 #include "net/third_party/quic/platform/api/quic_test_mem_slice_vector.h"
 #include "net/third_party/quic/quartc/counting_packet_filter.h"
 #include "net/third_party/quic/quartc/quartc_endpoint.h"
+#include "net/third_party/quic/quartc/quartc_fakes.h"
 #include "net/third_party/quic/quartc/quartc_packet_writer.h"
 #include "net/third_party/quic/quartc/simulated_packet_transport.h"
 #include "net/third_party/quic/test_tools/mock_clock.h"
@@ -24,9 +24,6 @@
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
-// Tests flaky on iOS.
-// TODO(vasilvv): figure out what's wrong and re-enable if possible.
-#if !defined(OS_IOS)
 namespace quic {
 
 namespace {
@@ -39,129 +36,11 @@
 
 static QuicByteCount kDefaultMaxPacketSize = 1200;
 
-class FakeQuartcEndpointDelegate : public QuartcEndpoint::Delegate {
- public:
-  explicit FakeQuartcEndpointDelegate(QuartcSession::Delegate* session_delegate)
-      : session_delegate_(session_delegate) {}
-
-  void OnSessionCreated(QuartcSession* session) override {
-    CHECK_EQ(session_, nullptr);
-    CHECK_NE(session, nullptr);
-    session_ = session;
-    session_->SetDelegate(session_delegate_);
-    session_->StartCryptoHandshake();
-  }
-
-  void OnConnectError(QuicErrorCode error,
-                      const QuicString& error_details) override {
-    LOG(FATAL) << "Unexpected error during QuartcEndpoint::Connect(); error="
-               << error << ", error_details=" << error_details;
-  }
-
-  QuartcSession* session() { return session_; }
-
- private:
-  QuartcSession::Delegate* session_delegate_;
-  QuartcSession* session_ = nullptr;
-};
-
-class FakeQuartcSessionDelegate : public QuartcSession::Delegate {
- public:
-  explicit FakeQuartcSessionDelegate(QuartcStream::Delegate* stream_delegate,
-                                     const QuicClock* clock)
-      : stream_delegate_(stream_delegate), clock_(clock) {}
-
-  void OnConnectionWritable() override {
-    LOG(INFO) << "Connection writable!";
-    if (!writable_time_.IsInitialized()) {
-      writable_time_ = clock_->Now();
-    }
-  }
-
-  // Called when peers have established forward-secure encryption
-  void OnCryptoHandshakeComplete() override {
-    LOG(INFO) << "Crypto handshake complete!";
-    crypto_handshake_time_ = clock_->Now();
-  }
-
-  // Called when connection closes locally, or remotely by peer.
-  void OnConnectionClosed(QuicErrorCode error_code,
-                          const QuicString& error_details,
-                          ConnectionCloseSource source) override {
-    connected_ = false;
-  }
-
-  // Called when an incoming QUIC stream is created.
-  void OnIncomingStream(QuartcStream* quartc_stream) override {
-    last_incoming_stream_ = quartc_stream;
-    last_incoming_stream_->SetDelegate(stream_delegate_);
-  }
-
-  void OnMessageReceived(QuicStringPiece message) override {
-    incoming_messages_.emplace_back(message);
-  }
-
-  void OnCongestionControlChange(QuicBandwidth bandwidth_estimate,
-                                 QuicBandwidth pacing_rate,
-                                 QuicTime::Delta latest_rtt) override {}
-
-  QuartcStream* last_incoming_stream() { return last_incoming_stream_; }
-
-  // Returns all received messages.
-  const std::vector<QuicString>& incoming_messages() {
-    return incoming_messages_;
-  }
-
-  bool connected() { return connected_; }
-  QuicTime writable_time() const { return writable_time_; }
-  QuicTime crypto_handshake_time() const { return crypto_handshake_time_; }
-
- private:
-  QuartcStream* last_incoming_stream_;
-  std::vector<QuicString> incoming_messages_;
-  bool connected_ = true;
-  QuartcStream::Delegate* stream_delegate_;
-  QuicTime writable_time_ = QuicTime::Zero();
-  QuicTime crypto_handshake_time_ = QuicTime::Zero();
-  const QuicClock* clock_;
-};
-
-class FakeQuartcStreamDelegate : public QuartcStream::Delegate {
- public:
-  size_t OnReceived(QuartcStream* stream,
-                    iovec* iov,
-                    size_t iov_length,
-                    bool fin) override {
-    size_t bytes_consumed = 0;
-    for (size_t i = 0; i < iov_length; ++i) {
-      received_data_[stream->id()] +=
-          QuicString(static_cast<const char*>(iov[i].iov_base), iov[i].iov_len);
-      bytes_consumed += iov[i].iov_len;
-    }
-    return bytes_consumed;
-  }
-
-  void OnClose(QuartcStream* stream) override {
-    errors_[stream->id()] = stream->stream_error();
-  }
-
-  void OnBufferChanged(QuartcStream* stream) override {}
-
-  bool has_data() { return !received_data_.empty(); }
-  std::map<QuicStreamId, QuicString> data() { return received_data_; }
-
-  QuicRstStreamErrorCode stream_error(QuicStreamId id) { return errors_[id]; }
-
- private:
-  std::map<QuicStreamId, QuicString> received_data_;
-  std::map<QuicStreamId, QuicRstStreamErrorCode> errors_;
-};
-
 class QuartcSessionTest : public QuicTest {
  public:
   ~QuartcSessionTest() override {}
 
-  void Init() {
+  void Init(bool create_client_endpoint = true) {
     client_transport_ =
         QuicMakeUnique<simulator::SimulatedQuartcPacketTransport>(
             &simulator_, "client_transport", "server_transport",
@@ -192,11 +71,12 @@
 
     // No 0-rtt setup, because server config is empty.
     // CannotCreateDataStreamBeforeHandshake depends on 1-rtt setup.
-    client_endpoint_ = QuicMakeUnique<QuartcClientEndpoint>(
-        simulator_.GetAlarmFactory(), simulator_.GetClock(),
-        client_endpoint_delegate_.get(), quic::QuartcSessionConfig(),
-        /*serialized_server_config=*/"");
-
+    if (create_client_endpoint) {
+      client_endpoint_ = QuicMakeUnique<QuartcClientEndpoint>(
+          simulator_.GetAlarmFactory(), simulator_.GetClock(),
+          client_endpoint_delegate_.get(), quic::QuartcSessionConfig(),
+          /*serialized_server_config=*/"");
+    }
     server_endpoint_ = QuicMakeUnique<QuartcServerEndpoint>(
         simulator_.GetAlarmFactory(), simulator_.GetClock(),
         server_endpoint_delegate_.get(), quic::QuartcSessionConfig());
@@ -620,12 +500,13 @@
   TestSendReceiveStreams();
 }
 
-// TODO(psla): re-enable once Simulator::AddActor() DCHECK failure fixed.
-TEST_F(QuartcSessionTest, DISABLED_PreSharedKeyHandshakeIs0RTT) {
+TEST_F(QuartcSessionTest, PreSharedKeyHandshakeIs0RTT) {
   QuartcSessionConfig session_config;
   session_config.pre_shared_key = "foo";
 
-  Init();
+  // Client endpoint is created below. Destructing client endpoint
+  // causes issues with the simulator.
+  Init(/*create_client_endpoint=*/false);
 
   server_endpoint_->Connect(server_transport_.get());
 
@@ -676,4 +557,3 @@
 }  // namespace
 
 }  // namespace quic
-#endif  // !defined(OS_IOS)
diff --git a/net/third_party/quic/test_tools/crypto_test_utils_test.cc b/net/third_party/quic/test_tools/crypto_test_utils_test.cc
index ca12254..4ef7e67 100644
--- a/net/third_party/quic/test_tools/crypto_test_utils_test.cc
+++ b/net/third_party/quic/test_tools/crypto_test_utils_test.cc
@@ -4,7 +4,6 @@
 
 #include "net/third_party/quic/test_tools/crypto_test_utils.h"
 
-#include "net/test/gtest_util.h"
 #include "net/third_party/quic/core/proto/crypto_server_config.pb.h"
 #include "net/third_party/quic/core/quic_utils.h"
 #include "net/third_party/quic/core/tls_server_handshaker.h"
diff --git a/net/third_party/quic/test_tools/mock_quic_time_wait_list_manager.cc b/net/third_party/quic/test_tools/mock_quic_time_wait_list_manager.cc
index 56eafde..0ac68f6 100644
--- a/net/third_party/quic/test_tools/mock_quic_time_wait_list_manager.cc
+++ b/net/third_party/quic/test_tools/mock_quic_time_wait_list_manager.cc
@@ -18,9 +18,9 @@
     : QuicTimeWaitListManager(writer, visitor, clock, alarm_factory) {
   // Though AddConnectionIdToTimeWait is mocked, we want to retain its
   // functionality.
-  EXPECT_CALL(*this, AddConnectionIdToTimeWait(_, _, _, _))
+  EXPECT_CALL(*this, AddConnectionIdToTimeWait(_, _, _, _, _))
       .Times(testing::AnyNumber());
-  ON_CALL(*this, AddConnectionIdToTimeWait(_, _, _, _))
+  ON_CALL(*this, AddConnectionIdToTimeWait(_, _, _, _, _))
       .WillByDefault(
           Invoke(this, &MockTimeWaitListManager::
                            QuicTimeWaitListManager_AddConnectionIdToTimeWait));
diff --git a/net/third_party/quic/test_tools/mock_quic_time_wait_list_manager.h b/net/third_party/quic/test_tools/mock_quic_time_wait_list_manager.h
index b55e4f8..dec394ae 100644
--- a/net/third_party/quic/test_tools/mock_quic_time_wait_list_manager.h
+++ b/net/third_party/quic/test_tools/mock_quic_time_wait_list_manager.h
@@ -19,10 +19,11 @@
                           QuicAlarmFactory* alarm_factory);
   ~MockTimeWaitListManager() override;
 
-  MOCK_METHOD4(AddConnectionIdToTimeWait,
+  MOCK_METHOD5(AddConnectionIdToTimeWait,
                void(QuicConnectionId connection_id,
                     bool ietf_quic,
                     QuicTimeWaitListManager::TimeWaitAction action,
+                    EncryptionLevel encryption_level,
                     std::vector<std::unique_ptr<QuicEncryptedPacket>>*
                         termination_packets));
 
@@ -30,15 +31,18 @@
       QuicConnectionId connection_id,
       bool ietf_quic,
       QuicTimeWaitListManager::TimeWaitAction action,
+      EncryptionLevel encryption_level,
       std::vector<std::unique_ptr<QuicEncryptedPacket>>* termination_packets) {
-    QuicTimeWaitListManager::AddConnectionIdToTimeWait(
-        connection_id, ietf_quic, action, termination_packets);
+    QuicTimeWaitListManager::AddConnectionIdToTimeWait(connection_id, ietf_quic,
+                                                       action, encryption_level,
+                                                       termination_packets);
   }
 
-  MOCK_METHOD4(ProcessPacket,
+  MOCK_METHOD5(ProcessPacket,
                void(const QuicSocketAddress& server_address,
                     const QuicSocketAddress& client_address,
                     QuicConnectionId connection_id,
+                    PacketHeaderFormat header_format,
                     std::unique_ptr<QuicPerPacketContext> packet_context));
 
   MOCK_METHOD6(SendVersionNegotiationPacket,
diff --git a/net/third_party/quic/test_tools/quic_test_client.cc b/net/third_party/quic/test_tools/quic_test_client.cc
index 3f2fa6a9..514ef041 100644
--- a/net/third_party/quic/test_tools/quic_test_client.cc
+++ b/net/third_party/quic/test_tools/quic_test_client.cc
@@ -27,6 +27,7 @@
 #include "net/third_party/quic/test_tools/quic_spdy_stream_peer.h"
 #include "net/third_party/quic/test_tools/quic_test_utils.h"
 #include "net/third_party/quic/tools/quic_url.h"
+#include "third_party/boringssl/src/include/openssl/x509.h"
 
 namespace quic {
 namespace test {
@@ -60,20 +61,22 @@
       return QUIC_FAILURE;
     }
 
-    // Convert certs to X509Certificate.
-    std::vector<QuicStringPiece> cert_pieces(certs.size());
-    for (unsigned i = 0; i < certs.size(); i++) {
-      cert_pieces[i] = QuicStringPiece(certs[i]);
+    const uint8_t* data;
+    data = reinterpret_cast<const uint8_t*>(certs[0].data());
+    bssl::UniquePtr<X509> cert(d2i_X509(nullptr, &data, certs[0].size()));
+    if (!cert.get()) {
+      return QUIC_FAILURE;
     }
-    // TODO(rtenneti): Fix after adding support for real certs. Currently,
-    // cert_pieces are "leaf" and "intermediate" and CreateFromDERCertChain
-    // fails to return cert from these cert_pieces.
-    //    bssl::UniquePtr<X509> cert(d2i_X509(nullptr, &data, certs[0].size()));
-    //    if (!cert.get()) {
-    //      return QUIC_FAILURE;
-    //    }
-    //
-    //    common_name_ = cert->subject().GetDisplayName();
+
+    static const unsigned kMaxCommonNameLength = 256;
+    char buf[kMaxCommonNameLength];
+    X509_NAME* subject_name = X509_get_subject_name(cert.get());
+    if (X509_NAME_get_text_by_NID(subject_name, NID_commonName, buf,
+                                  sizeof(buf)) <= 0) {
+      return QUIC_FAILURE;
+    }
+
+    common_name_ = buf;
     cert_sct_ = cert_sct;
 
     if (!verifier_) {
@@ -608,7 +611,7 @@
 
 bool QuicTestClient::WaitUntil(int timeout_ms, std::function<bool()> trigger) {
   int64_t timeout_us = timeout_ms * kNumMicrosPerMilli;
-  int64_t old_timeout_us = epoll_server()->timeout_in_us();
+  int64_t old_timeout_us = epoll_server()->timeout_in_us_for_test();
   if (timeout_us > 0) {
     epoll_server()->set_timeout_in_us(timeout_us);
   }
diff --git a/net/third_party/quic/test_tools/quic_test_utils.cc b/net/third_party/quic/test_tools/quic_test_utils.cc
index 40ff8fa..0c7151e5 100644
--- a/net/third_party/quic/test_tools/quic_test_utils.cc
+++ b/net/third_party/quic/test_tools/quic_test_utils.cc
@@ -124,8 +124,8 @@
   // Re-construct the data packet with data ownership.
   return QuicMakeUnique<QuicPacket>(
       buffer, length, /* owns_buffer */ true,
-      header.destination_connection_id_length,
-      header.source_connection_id_length, header.version_flag,
+      GetIncludedDestinationConnectionIdLength(header),
+      GetIncludedSourceConnectionIdLength(header), header.version_flag,
       header.nonce != nullptr, header.packet_number_length,
       header.retry_token_length_length, header.retry_token.length(),
       header.length_length);
@@ -531,11 +531,6 @@
 
 MockQuicCryptoStream::~MockQuicCryptoStream() {}
 
-QuicLongHeaderType MockQuicCryptoStream::GetLongHeaderType(
-    QuicStreamOffset /*offset*/) const {
-  return HANDSHAKE;
-}
-
 bool MockQuicCryptoStream::encryption_established() const {
   return false;
 }
@@ -790,8 +785,8 @@
     const QuicString& data) {
   return ConstructEncryptedPacket(
       destination_connection_id, source_connection_id, version_flag, reset_flag,
-      packet_number, data, PACKET_8BYTE_CONNECTION_ID,
-      PACKET_0BYTE_CONNECTION_ID, PACKET_4BYTE_PACKET_NUMBER);
+      packet_number, data, CONNECTION_ID_PRESENT, CONNECTION_ID_ABSENT,
+      PACKET_4BYTE_PACKET_NUMBER);
 }
 
 QuicEncryptedPacket* ConstructEncryptedPacket(
@@ -801,13 +796,13 @@
     bool reset_flag,
     uint64_t packet_number,
     const QuicString& data,
-    QuicConnectionIdLength destination_connection_id_length,
-    QuicConnectionIdLength source_connection_id_length,
+    QuicConnectionIdIncluded destination_connection_id_included,
+    QuicConnectionIdIncluded source_connection_id_included,
     QuicPacketNumberLength packet_number_length) {
   return ConstructEncryptedPacket(
       destination_connection_id, source_connection_id, version_flag, reset_flag,
-      packet_number, data, destination_connection_id_length,
-      source_connection_id_length, packet_number_length, nullptr);
+      packet_number, data, destination_connection_id_included,
+      source_connection_id_included, packet_number_length, nullptr);
 }
 
 QuicEncryptedPacket* ConstructEncryptedPacket(
@@ -817,14 +812,14 @@
     bool reset_flag,
     uint64_t packet_number,
     const QuicString& data,
-    QuicConnectionIdLength destination_connection_id_length,
-    QuicConnectionIdLength source_connection_id_length,
+    QuicConnectionIdIncluded destination_connection_id_included,
+    QuicConnectionIdIncluded source_connection_id_included,
     QuicPacketNumberLength packet_number_length,
     ParsedQuicVersionVector* versions) {
   return ConstructEncryptedPacket(
       destination_connection_id, source_connection_id, version_flag, reset_flag,
-      packet_number, data, destination_connection_id_length,
-      source_connection_id_length, packet_number_length, versions,
+      packet_number, data, destination_connection_id_included,
+      source_connection_id_included, packet_number_length, versions,
       Perspective::IS_CLIENT);
 }
 QuicEncryptedPacket* ConstructEncryptedPacket(
@@ -834,16 +829,17 @@
     bool reset_flag,
     uint64_t packet_number,
     const QuicString& data,
-    QuicConnectionIdLength destination_connection_id_length,
-    QuicConnectionIdLength source_connection_id_length,
+    QuicConnectionIdIncluded destination_connection_id_included,
+    QuicConnectionIdIncluded source_connection_id_included,
     QuicPacketNumberLength packet_number_length,
     ParsedQuicVersionVector* versions,
     Perspective perspective) {
   QuicPacketHeader header;
   header.destination_connection_id = destination_connection_id;
-  header.destination_connection_id_length = destination_connection_id_length;
+  header.destination_connection_id_included =
+      destination_connection_id_included;
   header.source_connection_id = source_connection_id;
-  header.source_connection_id_length = source_connection_id_length;
+  header.source_connection_id_included = source_connection_id_included;
   header.version_flag = version_flag;
   header.reset_flag = reset_flag;
   header.packet_number_length = packet_number_length;
@@ -898,16 +894,17 @@
     bool reset_flag,
     uint64_t packet_number,
     const QuicString& data,
-    QuicConnectionIdLength destination_connection_id_length,
-    QuicConnectionIdLength source_connection_id_length,
+    QuicConnectionIdIncluded destination_connection_id_included,
+    QuicConnectionIdIncluded source_connection_id_included,
     QuicPacketNumberLength packet_number_length,
     ParsedQuicVersionVector* versions,
     Perspective perspective) {
   QuicPacketHeader header;
   header.destination_connection_id = destination_connection_id;
-  header.destination_connection_id_length = destination_connection_id_length;
+  header.destination_connection_id_included =
+      destination_connection_id_included;
   header.source_connection_id = source_connection_id;
-  header.source_connection_id_length = source_connection_id_length;
+  header.source_connection_id_included = source_connection_id_included;
   header.version_flag = version_flag;
   header.reset_flag = reset_flag;
   header.packet_number_length = packet_number_length;
@@ -925,8 +922,9 @@
   // Now set the frame type to 0x1F, which is an invalid frame type.
   reinterpret_cast<unsigned char*>(
       packet->mutable_data())[GetStartOfEncryptedData(
-      framer.transport_version(), destination_connection_id_length,
-      source_connection_id_length, version_flag,
+      framer.transport_version(),
+      GetIncludedDestinationConnectionIdLength(header),
+      GetIncludedSourceConnectionIdLength(header), version_flag,
       false /* no diversification nonce */, packet_number_length,
       VARIABLE_LENGTH_INTEGER_LENGTH_0, 0, VARIABLE_LENGTH_INTEGER_LENGTH_0)] =
       0x1F;
@@ -1125,16 +1123,13 @@
   (*server_connection)->AdvanceTime(connection_start_time);
 }
 
-QuicStreamId NextStreamId(QuicTransportVersion version) {
-  return QuicUtils::StreamIdDelta(version);
-}
-
 QuicStreamId GetNthClientInitiatedBidirectionalStreamId(
     QuicTransportVersion version,
     int n) {
   return QuicUtils::GetFirstBidirectionalStreamId(version,
                                                   Perspective::IS_CLIENT) +
-         NextStreamId(version) * (n + 1);
+         // + 1 because spdy_session contains headers stream.
+         QuicUtils::StreamIdDelta(version) * (n + 1);
 }
 
 QuicStreamId GetNthServerInitiatedBidirectionalStreamId(
diff --git a/net/third_party/quic/test_tools/quic_test_utils.h b/net/third_party/quic/test_tools/quic_test_utils.h
index f8b1f4c..7b8314e 100644
--- a/net/third_party/quic/test_tools/quic_test_utils.h
+++ b/net/third_party/quic/test_tools/quic_test_utils.h
@@ -80,8 +80,8 @@
     bool reset_flag,
     uint64_t packet_number,
     const QuicString& data,
-    QuicConnectionIdLength destination_connection_id_length,
-    QuicConnectionIdLength source_connection_id_length,
+    QuicConnectionIdIncluded destination_connection_id_included,
+    QuicConnectionIdIncluded source_connection_id_included,
     QuicPacketNumberLength packet_number_length,
     ParsedQuicVersionVector* versions,
     Perspective perspective);
@@ -97,8 +97,8 @@
     bool reset_flag,
     uint64_t packet_number,
     const QuicString& data,
-    QuicConnectionIdLength destination_connection_id_length,
-    QuicConnectionIdLength source_connection_id_length,
+    QuicConnectionIdIncluded destination_connection_id_included,
+    QuicConnectionIdIncluded source_connection_id_included,
     QuicPacketNumberLength packet_number_length,
     ParsedQuicVersionVector* versions);
 
@@ -110,8 +110,8 @@
     bool reset_flag,
     uint64_t packet_number,
     const QuicString& data,
-    QuicConnectionIdLength destination_connection_id_length,
-    QuicConnectionIdLength source_connection_id_length,
+    QuicConnectionIdIncluded destination_connection_id_included,
+    QuicConnectionIdIncluded source_connection_id_included,
     QuicPacketNumberLength packet_number_length);
 
 // This form assumes |connection_id_length| == PACKET_8BYTE_CONNECTION_ID,
@@ -143,8 +143,8 @@
     bool reset_flag,
     uint64_t packet_number,
     const QuicString& data,
-    QuicConnectionIdLength destination_connection_id_length,
-    QuicConnectionIdLength source_connection_id_length,
+    QuicConnectionIdIncluded destination_connection_id_included,
+    QuicConnectionIdIncluded source_connection_id_included,
     QuicPacketNumberLength packet_number_length,
     ParsedQuicVersionVector* versions,
     Perspective perspective);
@@ -657,7 +657,6 @@
 
   ~MockQuicCryptoStream() override;
 
-  QuicLongHeaderType GetLongHeaderType(QuicStreamOffset offset) const override;
   bool encryption_established() const override;
   bool handshake_confirmed() const override;
   const QuicCryptoNegotiatedParameters& crypto_negotiated_params()
@@ -1174,7 +1173,6 @@
 // Helper functions for stream ids, to allow test logic to abstract over the
 // HTTP stream numbering scheme (i.e. whether one or two QUIC streams are used
 // per HTTP transaction).
-QuicStreamId NextStreamId(QuicTransportVersion version);
 QuicStreamId GetNthClientInitiatedBidirectionalStreamId(
     QuicTransportVersion version,
     int n);
diff --git a/net/third_party/quic/tools/quic_client.h b/net/third_party/quic/tools/quic_client.h
index 15cc622..7928fe8 100644
--- a/net/third_party/quic/tools/quic_client.h
+++ b/net/third_party/quic/tools/quic_client.h
@@ -12,20 +12,16 @@
 #include <memory>
 #include <string>
 
-#include "base/command_line.h"
 #include "base/macros.h"
 #include "net/third_party/quic/core/http/quic_client_push_promise_index.h"
 #include "net/third_party/quic/core/http/quic_spdy_client_session.h"
-#include "net/third_party/quic/core/http/quic_spdy_stream.h"
 #include "net/third_party/quic/core/quic_config.h"
 #include "net/third_party/quic/core/quic_packet_reader.h"
 #include "net/third_party/quic/core/quic_process_packet_interface.h"
 #include "net/third_party/quic/platform/api/quic_containers.h"
 #include "net/third_party/quic/platform/api/quic_epoll.h"
-#include "net/third_party/quic/tools/quic_client_base.h"
 #include "net/third_party/quic/tools/quic_client_epoll_network_helper.h"
 #include "net/third_party/quic/tools/quic_spdy_client_base.h"
-#include "net/tools/epoll_server/epoll_server.h"
 
 namespace quic {
 
diff --git a/net/third_party/quic/tools/quic_client_base.h b/net/third_party/quic/tools/quic_client_base.h
index 7b89b7c..df9072b 100644
--- a/net/third_party/quic/tools/quic_client_base.h
+++ b/net/third_party/quic/tools/quic_client_base.h
@@ -102,7 +102,7 @@
 
   // Wait for events until the handshake is confirmed.
   // Returns true if the crypto handshake succeeds, false otherwise.
-  bool WaitForCryptoHandshakeConfirmed() QUIC_WARN_UNUSED_RESULT;
+  bool WaitForCryptoHandshakeConfirmed() QUIC_MUST_USE_RESULT;
 
   // Wait up to 50ms, and handle any events which occur.
   // Returns true if there are any outstanding requests.
diff --git a/net/third_party/quic/tools/quic_client_epoll_network_helper.cc b/net/third_party/quic/tools/quic_client_epoll_network_helper.cc
index 7922446..374aa41 100644
--- a/net/third_party/quic/tools/quic_client_epoll_network_helper.cc
+++ b/net/third_party/quic/tools/quic_client_epoll_network_helper.cc
@@ -11,7 +11,6 @@
 #include <sys/socket.h>
 #include <unistd.h>
 
-#include "base/run_loop.h"
 #include "net/third_party/quic/core/crypto/quic_random.h"
 #include "net/third_party/quic/core/http/spdy_utils.h"
 #include "net/third_party/quic/core/quic_connection.h"
@@ -23,6 +22,7 @@
 #include "net/third_party/quic/platform/api/quic_bug_tracker.h"
 #include "net/third_party/quic/platform/api/quic_logging.h"
 #include "net/third_party/quic/platform/api/quic_ptr_util.h"
+#include "net/third_party/quic/platform/api/quic_system_event_loop.h"
 #include "net/third_party/quic/platform/impl/quic_socket_utils.h"
 
 #ifndef SO_RXQ_OVFL
@@ -118,7 +118,7 @@
 }
 
 void QuicClientEpollNetworkHelper::RunEventLoop() {
-  base::RunLoop().RunUntilIdle();
+  QuicRunSystemEventLoopIteration();
   epoll_server_->WaitForEventsAndExecuteCallbacks();
 }
 
diff --git a/net/third_party/quic/tools/quic_memory_cache_backend_test.cc b/net/third_party/quic/tools/quic_memory_cache_backend_test.cc
index 26a60fd..cb0f1a5 100644
--- a/net/third_party/quic/tools/quic_memory_cache_backend_test.cc
+++ b/net/third_party/quic/tools/quic_memory_cache_backend_test.cc
@@ -3,8 +3,7 @@
 // found in the LICENSE file.
 
 #include "net/third_party/quic/tools/quic_memory_cache_backend.h"
-#include "base/files/file_path.h"
-#include "base/path_service.h"
+
 #include "net/third_party/quic/platform/api/quic_map_util.h"
 #include "net/third_party/quic/platform/api/quic_str_cat.h"
 #include "net/third_party/quic/platform/api/quic_test.h"
@@ -30,14 +29,7 @@
     (*headers)[":scheme"] = "https";
   }
 
-  QuicString CacheDirectory() {
-    base::FilePath path;
-    base::PathService::Get(base::DIR_SOURCE_ROOT, &path);
-    path = path.AppendASCII("net").AppendASCII("data").AppendASCII(
-        "quic_http_response_cache_data");
-    // The file path is known to be an ascii string.
-    return path.MaybeAsASCII();
-  }
+  QuicString CacheDirectory() { return QuicGetTestMemoryCachePath(); }
 
   QuicMemoryCacheBackend cache_;
 };
diff --git a/net/third_party/quic/tools/quic_server.cc b/net/third_party/quic/tools/quic_server.cc
index e6ae84c..141b1a4 100644
--- a/net/third_party/quic/tools/quic_server.cc
+++ b/net/third_party/quic/tools/quic_server.cc
@@ -169,6 +169,8 @@
     dispatcher_->Shutdown();
   }
 
+  epoll_server_.Shutdown();
+
   close(fd_);
   fd_ = -1;
 }
diff --git a/net/third_party/quic/tools/quic_server.h b/net/third_party/quic/tools/quic_server.h
index e5c9588..3f2be0c 100644
--- a/net/third_party/quic/tools/quic_server.h
+++ b/net/third_party/quic/tools/quic_server.h
@@ -16,6 +16,7 @@
 #include "base/macros.h"
 #include "net/third_party/quic/core/crypto/quic_crypto_server_config.h"
 #include "net/third_party/quic/core/quic_config.h"
+#include "net/third_party/quic/core/quic_epoll_connection_helper.h"
 #include "net/third_party/quic/core/quic_framer.h"
 #include "net/third_party/quic/core/quic_packet_writer.h"
 #include "net/third_party/quic/core/quic_version_manager.h"
diff --git a/net/third_party/quic/tools/quic_simple_server_session_test.cc b/net/third_party/quic/tools/quic_simple_server_session_test.cc
index 4ec68c54..e121e80 100644
--- a/net/third_party/quic/tools/quic_simple_server_session_test.cc
+++ b/net/third_party/quic/tools/quic_simple_server_session_test.cc
@@ -798,6 +798,7 @@
   // Resetting 1st open stream will close the stream and give space for extra
   // stream to be opened.
   QuicStreamId stream_got_reset = GetNthServerInitiatedUnidirectionalId(0);
+  EXPECT_CALL(owner_, OnRstStreamReceived(_)).Times(1);
   EXPECT_CALL(*connection_, SendControlFrame(_));
   if (!IsVersion99()) {
     // For version 99, this is covered in InjectStopSending()
@@ -814,7 +815,6 @@
           kStreamFlowControlWindowSize - header_length, false)));
 
   EXPECT_CALL(*session_, SendBlocked(stream_to_open));
-  EXPECT_CALL(owner_, OnRstStreamReceived(_)).Times(1);
   QuicRstStreamFrame rst(kInvalidControlFrameId, stream_got_reset,
                          QUIC_STREAM_CANCELLED, 0);
   if (IsVersion99()) {
diff --git a/net/tools/epoll_server/epoll_server.cc b/net/tools/epoll_server/epoll_server.cc
index 8740b2a5..a686dd7d 100644
--- a/net/tools/epoll_server/epoll_server.cc
+++ b/net/tools/epoll_server/epoll_server.cc
@@ -462,6 +462,14 @@
   cb->OnUnregistration();
 }
 
+EpollServer::AlarmRegToken EpollServer::ReregisterAlarm(
+    EpollServer::AlarmRegToken iterator_token,
+    int64_t timeout_time_in_us) {
+  AlarmCB* cb = iterator_token->second;
+  alarm_map_.erase(iterator_token);
+  return alarm_map_.emplace(timeout_time_in_us, cb);
+}
+
 int EpollServer::NumFDsRegistered() const {
   DCHECK_GE(cb_map_.size(), 1u);
   // Omit the internal FD (read_fd_)
@@ -793,4 +801,9 @@
   eps_->UnregisterAlarm(token_);
 }
 
+void EpollAlarm::ReregisterAlarm(int64_t timeout_time_in_us) {
+  DCHECK(registered_);
+  token_ = eps_->ReregisterAlarm(token_, timeout_time_in_us);
+}
+
 }  // namespace net
diff --git a/net/tools/epoll_server/epoll_server.h b/net/tools/epoll_server/epoll_server.h
index 9792955..507c82158 100644
--- a/net/tools/epoll_server/epoll_server.h
+++ b/net/tools/epoll_server/epoll_server.h
@@ -404,6 +404,10 @@
   virtual void UnregisterAlarm(
       const EpollServer::AlarmRegToken& iterator_token);
 
+  virtual EpollServer::AlarmRegToken ReregisterAlarm(
+      EpollServer::AlarmRegToken iterator_token,
+      int64_t timeout_time_in_us);
+
   ////////////////////////////////////////
 
   // Summary:
@@ -475,12 +479,15 @@
 
   // Summary:
   //   Accessor for the current value of timeout_in_us.
-  int timeout_in_us() const { return timeout_in_us_; }
+  int timeout_in_us_for_test() const { return timeout_in_us_; }
 
   // Summary:
   // Returns true when the EpollServer() is being destroyed.
   bool in_shutdown() const { return in_shutdown_; }
 
+  // Compatibility stub.
+  void Shutdown() {}
+
  protected:
   virtual void SetNonblocking(int fd);
 
@@ -1033,6 +1040,9 @@
   // If the alarm was registered, unregister it.
   void UnregisterIfRegistered();
 
+  // Reregisters the alarm at specified time.
+  void ReregisterAlarm(int64_t timeout_time_in_us);
+
   bool registered() const { return registered_; }
 
   const EpollServer* eps() const { return eps_; }
diff --git a/net/url_request/http_with_dns_over_https_unittest.cc b/net/url_request/http_with_dns_over_https_unittest.cc
index 7bb2e8c0..40971a7 100644
--- a/net/url_request/http_with_dns_over_https_unittest.cc
+++ b/net/url_request/http_with_dns_over_https_unittest.cc
@@ -3,6 +3,7 @@
 // found in the LICENSE file.
 
 #include "base/bind.h"
+#include "net/base/proxy_server.h"
 #include "net/dns/dns_client.h"
 #include "net/dns/dns_config.h"
 #include "net/dns/dns_transaction.h"
@@ -206,7 +207,8 @@
   std::string group_name(request_info.url.host() + ":" +
                          request_info.url.port());
   EXPECT_EQ(network_session
-                ->GetTransportSocketPool(HttpNetworkSession::NORMAL_SOCKET_POOL)
+                ->GetSocketPool(HttpNetworkSession::NORMAL_SOCKET_POOL,
+                                ProxyServer::Direct())
                 ->IdleSocketCountInGroup(group_name),
             1u);
 
diff --git a/net/url_request/url_request_context_builder.cc b/net/url_request/url_request_context_builder.cc
index 5c0a176..54792fb 100644
--- a/net/url_request/url_request_context_builder.cc
+++ b/net/url_request/url_request_context_builder.cc
@@ -459,8 +459,7 @@
     context->set_http_auth_handler_factory(shared_http_auth_handler_factory_);
   } else {
     storage->set_http_auth_handler_factory(
-        HttpAuthHandlerRegistryFactory::CreateDefault(
-            context->host_resolver()));
+        HttpAuthHandlerRegistryFactory::CreateDefault());
   }
 
   if (cookie_store_set_by_client_) {
diff --git a/net/url_request/url_request_context_builder_unittest.cc b/net/url_request/url_request_context_builder_unittest.cc
index af94d76..d34cfec6 100644
--- a/net/url_request/url_request_context_builder_unittest.cc
+++ b/net/url_request/url_request_context_builder_unittest.cc
@@ -7,6 +7,7 @@
 #include "base/run_loop.h"
 #include "build/build_config.h"
 #include "net/base/request_priority.h"
+#include "net/dns/mock_host_resolver.h"
 #include "net/http/http_auth_challenge_tokenizer.h"
 #include "net/http/http_auth_handler.h"
 #include "net/http/http_auth_handler_factory.h"
@@ -50,6 +51,7 @@
                         CreateReason reason,
                         int nonce_count,
                         const NetLogWithSource& net_log,
+                        HostResolver* host_resolver,
                         std::unique_ptr<HttpAuthHandler>* handler) override {
     handler->reset();
 
@@ -75,6 +77,8 @@
 #endif  // defined(OS_LINUX) || defined(OS_ANDROID)
   }
 
+  std::unique_ptr<HostResolver> host_resolver_ =
+      std::make_unique<MockHostResolver>();
   EmbeddedTestServer test_server_;
   URLRequestContextBuilder builder_;
 };
@@ -119,7 +123,7 @@
   EXPECT_EQ(OK,
             context->http_auth_handler_factory()->CreateAuthHandlerFromString(
                 "basic", HttpAuth::AUTH_SERVER, null_ssl_info, gurl,
-                NetLogWithSource(), &handler));
+                NetLogWithSource(), host_resolver_.get(), &handler));
 }
 
 TEST_F(URLRequestContextBuilderTest, CustomHttpAuthHandlerFactory) {
@@ -135,19 +139,19 @@
   EXPECT_EQ(kBasicReturnCode,
             context->http_auth_handler_factory()->CreateAuthHandlerFromString(
                 "ExtraScheme", HttpAuth::AUTH_SERVER, null_ssl_info, gurl,
-                NetLogWithSource(), &handler));
+                NetLogWithSource(), host_resolver_.get(), &handler));
 
   // Verify that the default basic handler isn't present
   EXPECT_EQ(ERR_UNSUPPORTED_AUTH_SCHEME,
             context->http_auth_handler_factory()->CreateAuthHandlerFromString(
                 "basic", HttpAuth::AUTH_SERVER, null_ssl_info, gurl,
-                NetLogWithSource(), &handler));
+                NetLogWithSource(), host_resolver_.get(), &handler));
 
   // Verify that a handler isn't returned for a bogus scheme.
   EXPECT_EQ(ERR_UNSUPPORTED_AUTH_SCHEME,
             context->http_auth_handler_factory()->CreateAuthHandlerFromString(
                 "Bogus", HttpAuth::AUTH_SERVER, null_ssl_info, gurl,
-                NetLogWithSource(), &handler));
+                NetLogWithSource(), host_resolver_.get(), &handler));
 }
 
 #if BUILDFLAG(ENABLE_REPORTING)
diff --git a/net/url_request/url_request_http_job.cc b/net/url_request/url_request_http_job.cc
index ad454053..9a51c8e 100644
--- a/net/url_request/url_request_http_job.cc
+++ b/net/url_request/url_request_http_job.cc
@@ -37,6 +37,7 @@
 #include "net/cert/known_roots.h"
 #include "net/cookies/canonical_cookie.h"
 #include "net/cookies/cookie_store.h"
+#include "net/cookies/cookie_util.h"
 #include "net/filter/brotli_source_stream.h"
 #include "net/filter/filter_source_stream.h"
 #include "net/filter/gzip_source_stream.h"
@@ -691,46 +692,10 @@
   if (cookie_store && !(request_info_.load_flags & LOAD_DO_NOT_SEND_COOKIES)) {
     CookieOptions options;
     options.set_include_httponly();
-
-    // Set SameSiteCookieMode according to the rules laid out in
-    // https://tools.ietf.org/html/draft-ietf-httpbis-cookie-same-site:
-    //
-    // * Include both "strict" and "lax" same-site cookies if the request's
-    //   |url|, |initiator|, and |site_for_cookies| all have the same
-    //   registrable domain. Note: this also covers the case of a request
-    //   without an initiator (only happens for browser-initiated main frame
-    //   navigations).
-    //
-    // * Include only "lax" same-site cookies if the request's |URL| and
-    //   |site_for_cookies| have the same registrable domain, _and_ the
-    //   request's |method| is "safe" ("GET" or "HEAD").
-    //
-    //   Note that this will generally be the case only for cross-site requests
-    //   which target a top-level browsing context.
-    //
-    // * Include both "strict" and "lax" same-site cookies if the request is
-    //   tagged with a flag allowing it.
-    //   Note that this can be the case for requests initiated by extensions,
-    //   which need to behave as though they are made by the document itself,
-    //   but appear like cross-site ones.
-    //
-    // * Otherwise, do not include same-site cookies.
-    if (registry_controlled_domains::SameDomainOrHost(
-            request_->url(), request_->site_for_cookies(),
-            registry_controlled_domains::INCLUDE_PRIVATE_REGISTRIES)) {
-      if (!request_->initiator() ||
-          registry_controlled_domains::SameDomainOrHost(
-              request_->url(), request_->initiator().value().GetURL(),
-              registry_controlled_domains::INCLUDE_PRIVATE_REGISTRIES) ||
-          request_->attach_same_site_cookies()) {
-        options.set_same_site_cookie_mode(
-            CookieOptions::SameSiteCookieMode::INCLUDE_STRICT_AND_LAX);
-      } else if (HttpUtil::IsMethodSafe(request_->method())) {
-        options.set_same_site_cookie_mode(
-            CookieOptions::SameSiteCookieMode::INCLUDE_LAX);
-      }
-    }
-
+    options.set_same_site_cookie_context(
+        net::cookie_util::ComputeSameSiteContextForRequest(
+            request_->method(), request_->url(), request_->site_for_cookies(),
+            request_->initiator(), request_->attach_same_site_cookies()));
     cookie_store->GetCookieListWithOptionsAsync(
         request_->url(), options,
         base::Bind(&URLRequestHttpJob::SetCookieHeaderAndStart,
diff --git a/net/url_request/url_request_test_util.cc b/net/url_request/url_request_test_util.cc
index 211a41b..2c5d63e 100644
--- a/net/url_request/url_request_test_util.cc
+++ b/net/url_request/url_request_test_util.cc
@@ -94,7 +94,7 @@
   }
   if (!http_auth_handler_factory()) {
     context_storage_.set_http_auth_handler_factory(
-        HttpAuthHandlerFactory::CreateDefault(host_resolver()));
+        HttpAuthHandlerFactory::CreateDefault());
   }
   if (!http_server_properties()) {
     context_storage_.set_http_server_properties(
diff --git a/net/websockets/websocket_basic_stream_adapters_test.cc b/net/websockets/websocket_basic_stream_adapters_test.cc
index f399a61e..5a2d5c2 100644
--- a/net/websockets/websocket_basic_stream_adapters_test.cc
+++ b/net/websockets/websocket_basic_stream_adapters_test.cc
@@ -98,7 +98,7 @@
             ssl_params_),
         MEDIUM, SocketTag(), ClientSocketPool::RespectLimits::ENABLED,
         callback.callback(), ClientSocketPool::ProxyAuthCallback(),
-        socket_pool_manager_->GetTransportSocketPool(), net_log_);
+        socket_pool_manager_->GetSocketPool(ProxyServer::Direct()), net_log_);
     rv = callback.GetResult(rv);
     return rv == OK;
   }
diff --git a/pdf/out_of_process_instance.cc b/pdf/out_of_process_instance.cc
index 25bb9fdd..4db5cd4 100644
--- a/pdf/out_of_process_instance.cc
+++ b/pdf/out_of_process_instance.cc
@@ -106,6 +106,7 @@
 constexpr char kJSSaveDataType[] = "saveData";
 constexpr char kJSFileName[] = "fileName";
 constexpr char kJSDataToSave[] = "dataToSave";
+constexpr char kJSHasUnsavedChanges[] = "hasUnsavedChanges";
 // Consume save token (Plugin -> Page)
 constexpr char kJSConsumeSaveTokenType[] = "consumeSaveToken";
 // Go to page (Plugin -> Page)
@@ -1453,6 +1454,9 @@
   message.Set(kJSFileName, pp::Var(file_name));
   // This will be overwritten if the save is successful.
   message.Set(kJSDataToSave, pp::Var(pp::Var::Null()));
+  const bool hasUnsavedChanges =
+      edit_mode_ && !base::FeatureList::IsEnabled(features::kSaveEditedPDFForm);
+  message.Set(kJSHasUnsavedChanges, pp::Var(hasUnsavedChanges));
 
   if (ShouldSaveEdits()) {
     std::vector<uint8_t> data = engine_->GetSaveData();
diff --git a/pdf/pdf_features.cc b/pdf/pdf_features.cc
index 89c4cb9..aed5cca 100644
--- a/pdf/pdf_features.cc
+++ b/pdf/pdf_features.cc
@@ -7,8 +7,14 @@
 namespace chrome_pdf {
 namespace features {
 
-const base::Feature kSaveEditedPDFForm{"SaveEditedPDFForm",
-                                       base::FEATURE_DISABLED_BY_DEFAULT};
+const base::Feature kSaveEditedPDFForm {
+  "SaveEditedPDFForm",
+#if defined(OS_CHROMEOS)
+      base::FEATURE_ENABLED_BY_DEFAULT
+#else
+      base::FEATURE_DISABLED_BY_DEFAULT
+#endif  // defined(OS_CHROMEOS)
+};
 
 const base::Feature kPDFAnnotations {
   "PDFAnnotations",
diff --git a/remoting/host/desktop_display_info.cc b/remoting/host/desktop_display_info.cc
index cb72773c..7f571d93 100644
--- a/remoting/host/desktop_display_info.cc
+++ b/remoting/host/desktop_display_info.cc
@@ -46,6 +46,13 @@
 webrtc::DesktopSize DesktopDisplayInfo::CalcSizeDips(webrtc::DesktopSize size,
                                                      int dpi_x,
                                                      int dpi_y) {
+  // Guard against invalid input.
+  // TODO: Replace with a DCHECK, once crbug.com/938648 is fixed.
+  if (dpi_x == 0)
+    dpi_x = kDefaultDpi;
+  if (dpi_y == 0)
+    dpi_y = kDefaultDpi;
+
   webrtc::DesktopSize size_dips(size.width() * kDefaultDpi / dpi_x,
                                 size.height() * kDefaultDpi / dpi_y);
   return size_dips;
diff --git a/remoting/host/file_transfer/fake_file_operations.cc b/remoting/host/file_transfer/fake_file_operations.cc
index 041700cd6..d8f4884 100644
--- a/remoting/host/file_transfer/fake_file_operations.cc
+++ b/remoting/host/file_transfer/fake_file_operations.cc
@@ -4,7 +4,9 @@
 
 #include "remoting/host/file_transfer/fake_file_operations.h"
 
+#include <algorithm>
 #include <memory>
+#include <utility>
 
 #include "base/bind.h"
 #include "base/memory/weak_ptr.h"
@@ -12,6 +14,31 @@
 
 namespace remoting {
 
+class FakeFileOperations::FakeFileReader : public FileOperations::Reader {
+ public:
+  FakeFileReader(TestIo* test_io);
+  ~FakeFileReader() override;
+
+  void Open(OpenCallback callback) override;
+  void ReadChunk(std::size_t size, ReadCallback callback) override;
+
+  const base::FilePath& filename() const override;
+  std::uint64_t size() const override;
+  State state() const override;
+
+ private:
+  void DoOpen(OpenCallback callback);
+  void DoReadChunk(std::size_t size, ReadCallback callback);
+
+  FileOperations::State state_ = FileOperations::kCreated;
+  TestIo* test_io_;
+  protocol::FileTransferResult<InputFile> input_file_;
+  base::FilePath filename_;
+  std::size_t filesize_ = 0;
+  std::size_t read_offset_ = 0;
+  base::WeakPtrFactory<FakeFileReader> weak_ptr_factory_;
+};
+
 class FakeFileOperations::FakeFileWriter : public FileOperations::Writer {
  public:
   FakeFileWriter(TestIo* test_io);
@@ -34,8 +61,7 @@
 };
 
 std::unique_ptr<FileOperations::Reader> FakeFileOperations::CreateReader() {
-  NOTIMPLEMENTED();
-  return nullptr;
+  return std::make_unique<FakeFileReader>(test_io_);
 }
 
 std::unique_ptr<FileOperations::Writer> FakeFileOperations::CreateWriter() {
@@ -55,11 +81,115 @@
       chunks(std::move(chunks)) {}
 
 FakeFileOperations::OutputFile::OutputFile(const OutputFile& other) = default;
+FakeFileOperations::OutputFile::OutputFile(OutputFile&& other) = default;
+FakeFileOperations::OutputFile& FakeFileOperations::OutputFile::operator=(
+    const OutputFile&) = default;
+FakeFileOperations::OutputFile& FakeFileOperations::OutputFile::operator=(
+    OutputFile&&) = default;
 FakeFileOperations::OutputFile::~OutputFile() = default;
+
+FakeFileOperations::InputFile::InputFile(
+    base::FilePath filename,
+    std::string data,
+    base::Optional<protocol::FileTransfer_Error> io_error)
+    : filename(std::move(filename)),
+      data(std::move(data)),
+      io_error(std::move(io_error)) {}
+
+FakeFileOperations::InputFile::InputFile() = default;
+FakeFileOperations::InputFile::InputFile(const InputFile&) = default;
+FakeFileOperations::InputFile::InputFile(InputFile&&) = default;
+FakeFileOperations::InputFile& FakeFileOperations::InputFile::operator=(
+    const InputFile&) = default;
+FakeFileOperations::InputFile& FakeFileOperations::InputFile::operator=(
+    InputFile&&) = default;
+FakeFileOperations::InputFile::~InputFile() = default;
+
 FakeFileOperations::TestIo::TestIo() = default;
 FakeFileOperations::TestIo::TestIo(const TestIo& other) = default;
 FakeFileOperations::TestIo::~TestIo() = default;
 
+FakeFileOperations::FakeFileReader::FakeFileReader(TestIo* test_io)
+    : test_io_(test_io), weak_ptr_factory_(this) {}
+
+FakeFileOperations::FakeFileReader::~FakeFileReader() = default;
+
+void FakeFileOperations::FakeFileReader::Open(
+    FileOperations::Reader::OpenCallback callback) {
+  CHECK_EQ(kCreated, state_) << "Open called twice";
+  state_ = kBusy;
+  input_file_ = test_io_->input_file;
+  base::SequencedTaskRunnerHandle::Get()->PostTask(
+      FROM_HERE,
+      base::BindOnce(&FakeFileReader::DoOpen, weak_ptr_factory_.GetWeakPtr(),
+                     std::move(callback)));
+}
+
+void FakeFileOperations::FakeFileReader::ReadChunk(
+    std::size_t size,
+    FileOperations::Reader::ReadCallback callback) {
+  CHECK_EQ(kReady, state_) << "ReadChunk called when writer not ready";
+  state_ = kBusy;
+  base::SequencedTaskRunnerHandle::Get()->PostTask(
+      FROM_HERE, base::BindOnce(&FakeFileReader::DoReadChunk,
+                                weak_ptr_factory_.GetWeakPtr(), size,
+                                std::move(callback)));
+}
+
+const base::FilePath& FakeFileOperations::FakeFileReader::filename() const {
+  return filename_;
+}
+
+std::uint64_t FakeFileOperations::FakeFileReader::size() const {
+  return filesize_;
+}
+
+FileOperations::State FakeFileOperations::FakeFileReader::state() const {
+  return state_;
+}
+
+void FakeFileOperations::FakeFileReader::DoOpen(
+    FileOperations::Reader::OpenCallback callback) {
+  if (input_file_) {
+    filename_ = input_file_->filename;
+    filesize_ = input_file_->data.size();
+    state_ = kReady;
+    std::move(callback).Run(kSuccessTag);
+  } else {
+    state_ = kFailed;
+    std::move(callback).Run(input_file_.error());
+  }
+}
+
+void FakeFileOperations::FakeFileReader::DoReadChunk(
+    std::size_t size,
+    FileOperations::Reader::ReadCallback callback) {
+  if (size == 0) {
+    state_ = kReady;
+    std::move(callback).Run(std::string());
+    return;
+  }
+
+  std::size_t remaining_data = input_file_->data.size() - read_offset_;
+
+  if (remaining_data == 0) {
+    if (input_file_->io_error) {
+      state_ = kFailed;
+      std::move(callback).Run(*input_file_->io_error);
+    } else {
+      state_ = kComplete;
+      std::move(callback).Run(std::string());
+    }
+    return;
+  }
+
+  std::size_t read_size = std::min(size, remaining_data);
+  state_ = kReady;
+  std::move(callback).Run(
+      std::string(input_file_->data, read_offset_, read_size));
+  read_offset_ += read_size;
+}
+
 FakeFileOperations::FakeFileWriter::FakeFileWriter(TestIo* test_io)
     : test_io_(test_io), weak_ptr_factory_(this) {}
 
@@ -109,9 +239,6 @@
 }
 
 void FakeFileOperations::FakeFileWriter::DoOpen(Callback callback) {
-  if (state_ == kFailed) {
-    return;
-  }
   if (!test_io_->io_error) {
     state_ = kReady;
     std::move(callback).Run(kSuccessTag);
@@ -123,9 +250,6 @@
 
 void FakeFileOperations::FakeFileWriter::DoWrite(std::string data,
                                                  Callback callback) {
-  if (state_ == kFailed) {
-    return;
-  }
   if (!test_io_->io_error) {
     chunks_.push_back(std::move(data));
     state_ = kReady;
@@ -139,9 +263,6 @@
 }
 
 void FakeFileOperations::FakeFileWriter::DoClose(Callback callback) {
-  if (state_ == kFailed) {
-    return;
-  }
   if (!test_io_->io_error) {
     test_io_->files_written.push_back(
         OutputFile(filename_, false /* failed */, std::move(chunks_)));
diff --git a/remoting/host/file_transfer/fake_file_operations.h b/remoting/host/file_transfer/fake_file_operations.h
index cfacb19..9c879f08 100644
--- a/remoting/host/file_transfer/fake_file_operations.h
+++ b/remoting/host/file_transfer/fake_file_operations.h
@@ -25,6 +25,9 @@
                bool failed,
                std::vector<std::string> chunks);
     OutputFile(const OutputFile& other);
+    OutputFile(OutputFile&& other);
+    OutputFile& operator=(const OutputFile&);
+    OutputFile& operator=(OutputFile&&);
     ~OutputFile();
 
     // The filename provided to Open.
@@ -38,6 +41,28 @@
     std::vector<std::string> chunks;
   };
 
+  struct InputFile {
+    InputFile();
+    InputFile(base::FilePath filename,
+              std::string data,
+              base::Optional<protocol::FileTransfer_Error> io_error);
+    InputFile(const InputFile& other);
+    InputFile(InputFile&& other);
+    InputFile& operator=(const InputFile&);
+    InputFile& operator=(InputFile&&);
+    ~InputFile();
+
+    // The filename reported by the reader.
+    base::FilePath filename;
+
+    // The file data to provide in response to read requests.
+    std::string data;
+
+    // If set, this error will be returned instead of EOF once the provided data
+    // has been read.
+    base::Optional<protocol::FileTransfer_Error> io_error;
+  };
+
   // Used to interact with FakeFileOperations after ownership is passed
   // elsewhere.
   struct TestIo {
@@ -45,6 +70,10 @@
     TestIo(const TestIo& other);
     ~TestIo();
 
+    // The file information used for the next call to Reader::Open. If an error,
+    // it will be returned from the Open call.
+    protocol::FileTransferResult<InputFile> input_file;
+
     // An element will be added for each file written in full or in part.
     std::vector<OutputFile> files_written;
 
@@ -60,6 +89,7 @@
   std::unique_ptr<Writer> CreateWriter() override;
 
  private:
+  class FakeFileReader;
   class FakeFileWriter;
 
   TestIo* test_io_;
diff --git a/remoting/host/file_transfer/file_transfer_message_handler.cc b/remoting/host/file_transfer/file_transfer_message_handler.cc
index a40e991..4831e6e9 100644
--- a/remoting/host/file_transfer/file_transfer_message_handler.cc
+++ b/remoting/host/file_transfer/file_transfer_message_handler.cc
@@ -4,6 +4,9 @@
 
 #include "remoting/host/file_transfer/file_transfer_message_handler.h"
 
+#include <cstddef>
+#include <utility>
+
 #include "base/bind.h"
 #include "base/bind_helpers.h"
 #include "base/path_service.h"
@@ -22,6 +25,14 @@
 // it consists entirely of disallowed characters.)
 constexpr char kDefaultFileName[] = "crd_transfer";
 
+// The max SCTP message size that can be safely sent in a cross-browser fashion
+// is 16 KiB. Thus, 8 KiB should be a safe value even with messaging overhead.
+constexpr std::size_t kChunkSize = 8192;  // 8 KiB
+
+// The max number of chunks that should be queued for sending at one time. This
+// helps smooth out spiky IO latency.
+constexpr std::size_t kMaxQueuedChunks = 128;  // 128 * 8 KiB = 1 MiB
+
 }  // namespace
 
 FileTransferMessageHandler::FileTransferMessageHandler(
@@ -29,7 +40,8 @@
     std::unique_ptr<protocol::MessagePipe> pipe,
     std::unique_ptr<FileOperations> file_operations)
     : protocol::NamedMessagePipeHandler(name, std::move(pipe)),
-      file_operations_(std::move(file_operations)) {
+      file_operations_(std::move(file_operations)),
+      weak_ptr_factory_(this) {
   DCHECK(file_operations_);
 }
 
@@ -39,84 +51,88 @@
 
 void FileTransferMessageHandler::OnIncomingMessage(
     std::unique_ptr<CompoundBuffer> buffer) {
+  if (state_ == kFailed) {
+    // Ignore any messages that come in after cancel or error.
+    return;
+  }
+
   protocol::FileTransfer message;
   CompoundBufferInputStream buffer_stream(buffer.get());
   if (!message.ParseFromZeroCopyStream(&buffer_stream)) {
     LOG(ERROR) << "Failed to parse message.";
-    CancelAndSendError(protocol::MakeFileTransferError(
+    Cancel();
+    SendError(protocol::MakeFileTransferError(
         FROM_HERE, protocol::FileTransfer_Error_Type_PROTOCOL_ERROR));
     return;
   }
 
-  if (message.has_metadata()) {
-    StartFile(std::move(*message.mutable_metadata()));
-    return;
-  }
-
-  switch (state_) {
-    case kWriting:
-      // This is the expected state.
-      break;
-    case kFailed:
-      // Ignore any messages that come in after cancel or error.
-      return;
-    case kConnected:
-      // Don't send an error in response to an error.
-      if (!message.has_error()) {
-        LOG(ERROR) << "First message must contain file metadata";
-        CancelAndSendError(protocol::MakeFileTransferError(
-            FROM_HERE, protocol::FileTransfer_Error_Type_PROTOCOL_ERROR));
-      }
-      return;
-    case kClosed:
-      LOG(ERROR) << "Message received after End";
-      CancelAndSendError(protocol::MakeFileTransferError(
-          FROM_HERE, protocol::FileTransfer_Error_Type_PROTOCOL_ERROR));
-      return;
-  }
-
   switch (message.message_case()) {
-    case protocol::FileTransfer::kData:
-      Write(std::move(*message.mutable_data()->mutable_data()));
-      break;
-    case protocol::FileTransfer::kEnd:
-      Close();
-      break;
-    case protocol::FileTransfer::kError:
-      if (message.error().type() !=
-          protocol::FileTransfer_Error_Type_CANCELED) {
-        LOG(ERROR) << "File transfer error from client: " << message.error();
+    // Writing messages.
+    case protocol::FileTransfer::kMetadata:
+      if (state_ != kConnected) {
+        UnexpectedMessage(FROM_HERE, "metadata");
+        return;
       }
-      Cancel();
-      break;
-    default:
+      OnMetadata(std::move(*message.mutable_metadata()));
+      return;
+    case protocol::FileTransfer::kData:
+      if (state_ != kWriting) {
+        UnexpectedMessage(FROM_HERE, "data");
+        return;
+      }
+      OnData(std::move(*message.mutable_data()->mutable_data()));
+      return;
+    case protocol::FileTransfer::kEnd:
+      if (state_ != kWriting) {
+        UnexpectedMessage(FROM_HERE, "end");
+        return;
+      }
+      OnEnd();
+      return;
+
+    // Reading messages.
+    case protocol::FileTransfer::kRequestTransfer:
+      if (state_ != kConnected) {
+        UnexpectedMessage(FROM_HERE, "request_transfer");
+        return;
+      }
+      OnRequestTransfer();
+      return;
+    case protocol::FileTransfer::kSuccess:
+      if (state_ != kEof) {
+        UnexpectedMessage(FROM_HERE, "success");
+        return;
+      }
+      OnSuccess();
+      return;
+
+    // Common messages.
+    case protocol::FileTransfer::kError:
+      OnError(std::move(*message.mutable_error()));
+      return;
+
+    case protocol::FileTransfer::MESSAGE_NOT_SET:
       LOG(ERROR) << "Received invalid file-transfer message.";
-      CancelAndSendError(protocol::MakeFileTransferError(
+      Cancel();
+      SendError(protocol::MakeFileTransferError(
           FROM_HERE, protocol::FileTransfer_Error_Type_PROTOCOL_ERROR));
-      break;
+      return;
   }
 }
 
 void FileTransferMessageHandler::OnDisconnecting() {}
 
-void FileTransferMessageHandler::StartFile(
+void FileTransferMessageHandler::OnMetadata(
     protocol::FileTransfer::Metadata metadata) {
-  if (state_ != kConnected) {
-    LOG(ERROR) << "Only one file per connection is supported.";
-    CancelAndSendError(protocol::MakeFileTransferError(
-        FROM_HERE, protocol::FileTransfer_Error_Type_PROTOCOL_ERROR));
-    return;
-  }
-
   SetState(kWriting);
   // Unretained is sound because the callbacks won't be called after
   // BufferedFileWriter is destroyed, which is in turn owned by this
   // FileTransferMessageHandler.
   buffered_file_writer_.emplace(
       file_operations_->CreateWriter(),
-      base::BindOnce(&FileTransferMessageHandler::OnComplete,
+      base::BindOnce(&FileTransferMessageHandler::OnWritingComplete,
                      base::Unretained(this)),
-      base::BindOnce(&FileTransferMessageHandler::OnError,
+      base::BindOnce(&FileTransferMessageHandler::OnWriteError,
                      base::Unretained(this)));
   buffered_file_writer_->Start(
       // Ensure filename is safe, and convert from UTF-8 to a FilePath.
@@ -125,46 +141,132 @@
                             kDefaultFileName));
 }
 
-void FileTransferMessageHandler::Write(std::string data) {
-  DCHECK(state_ == kWriting);
+void FileTransferMessageHandler::OnData(std::string data) {
+  DCHECK_EQ(kWriting, state_);
   buffered_file_writer_->Write(std::move(data));
 }
 
-void FileTransferMessageHandler::Close() {
-  DCHECK(state_ == kWriting);
+void FileTransferMessageHandler::OnEnd() {
+  DCHECK_EQ(kWriting, state_);
   SetState(kClosed);
   buffered_file_writer_->Close();
 }
 
+void FileTransferMessageHandler::OnRequestTransfer() {
+  SetState(kReading);
+  file_reader_ = file_operations_->CreateReader();
+  // Unretained is sound because FileReader will not call us after it is
+  // destroyed, and we own it.
+  file_reader_->Open(base::BindOnce(&FileTransferMessageHandler::OnOpenResult,
+                                    base::Unretained(this)));
+}
+
+void FileTransferMessageHandler::OnSuccess() {
+  DCHECK_EQ(kEof, state_);
+  SetState(kClosed);
+}
+
+void FileTransferMessageHandler::OnError(protocol::FileTransfer_Error error) {
+  if (error.type() != protocol::FileTransfer_Error_Type_CANCELED) {
+    LOG(ERROR) << "File transfer error from client: " << error;
+  }
+  Cancel();
+}
+
+void FileTransferMessageHandler::OnOpenResult(
+    FileOperations::Reader::OpenResult result) {
+  if (!result) {
+    Cancel();
+    SendError(result.error());
+    return;
+  }
+
+  protocol::FileTransfer metadata_message;
+  metadata_message.mutable_metadata()->set_filename(
+      file_reader_->filename().AsUTF8Unsafe());
+  metadata_message.mutable_metadata()->set_size(file_reader_->size());
+  protocol::NamedMessagePipeHandler::Send(metadata_message, base::DoNothing());
+  ReadNextChunk();
+}
+
+void FileTransferMessageHandler::OnReadResult(
+    FileOperations::Reader::ReadResult result) {
+  if (!result) {
+    Cancel();
+    SendError(result.error());
+    return;
+  }
+
+  if (result->empty()) {
+    SetState(kEof);
+    protocol::FileTransfer end_message;
+    end_message.mutable_end();
+    protocol::NamedMessagePipeHandler::Send(end_message, base::DoNothing());
+  } else {
+    ++queued_chunks_;
+    if (queued_chunks_ < kMaxQueuedChunks) {
+      ReadNextChunk();
+    }
+    protocol::FileTransfer data_message;
+    data_message.mutable_data()->set_data(std::move(*result));
+    // Call Send last in case it invokes ReadNextChunk synchronously.
+    protocol::NamedMessagePipeHandler::Send(
+        data_message, base::BindOnce(&FileTransferMessageHandler::OnChunkSent,
+                                     weak_ptr_factory_.GetWeakPtr()));
+  }
+}
+
+void FileTransferMessageHandler::OnChunkSent() {
+  --queued_chunks_;
+  ReadNextChunk();
+}
+
+void FileTransferMessageHandler::OnWritingComplete() {
+  protocol::FileTransfer success_message;
+  success_message.mutable_success();
+  protocol::NamedMessagePipeHandler::Send(success_message, base::DoNothing());
+}
+
+void FileTransferMessageHandler::OnWriteError(
+    protocol::FileTransfer_Error error) {
+  Cancel();
+  SendError(std::move(error));
+}
+
+void FileTransferMessageHandler::ReadNextChunk() {
+  // Make sure we haven't received an error from the client and that we're not
+  // currently reading a chunk.
+  if (state_ != kReading || file_reader_->state() != FileOperations::kReady) {
+    return;
+  }
+
+  // Unretained is sound because file_reader_ is guaranteed not to execute any
+  // callbacks after it is destroyed.
+  file_reader_->ReadChunk(
+      kChunkSize, base::BindOnce(&FileTransferMessageHandler::OnReadResult,
+                                 base::Unretained(this)));
+}
+
 void FileTransferMessageHandler::Cancel() {
   SetState(kFailed);
+  file_reader_.reset();
   // Will implicitly cancel if still in progress.
   buffered_file_writer_.reset();
 }
 
-void FileTransferMessageHandler::OnComplete() {
-  SendResult(kSuccessTag);  // Success
+void FileTransferMessageHandler::SendError(protocol::FileTransfer_Error error) {
+  protocol::FileTransfer error_message;
+  *error_message.mutable_error() = std::move(error);
+  protocol::NamedMessagePipeHandler::Send(error_message, base::DoNothing());
 }
 
-void FileTransferMessageHandler::OnError(protocol::FileTransfer_Error error) {
-  CancelAndSendError(std::move(error));
-}
-
-void FileTransferMessageHandler::SendResult(
-    protocol::FileTransferResult<Monostate> result) {
-  protocol::FileTransfer result_message;
-  if (result) {
-    result_message.mutable_success();
-  } else {
-    *result_message.mutable_error() = std::move(result.error());
-  }
-  Send(result_message, base::DoNothing());
-}
-
-void FileTransferMessageHandler::CancelAndSendError(
-    protocol::FileTransfer_Error error) {
+void FileTransferMessageHandler::UnexpectedMessage(base::Location from_here,
+                                                   const char* message) {
+  LOG(ERROR) << "Unexpected file-transfer message received: " << message
+             << ". Current state: " << state_;
   Cancel();
-  SendResult(error);
+  SendError(protocol::MakeFileTransferError(
+      from_here, protocol::FileTransfer_Error_Type_PROTOCOL_ERROR));
 }
 
 void FileTransferMessageHandler::SetState(State state) {
@@ -173,11 +275,17 @@
       // This is the initial state, but should never be reached again.
       NOTREACHED();
       break;
+    case kReading:
+      DCHECK_EQ(kConnected, state_);
+      break;
     case kWriting:
       DCHECK_EQ(kConnected, state_);
       break;
+    case kEof:
+      DCHECK_EQ(kReading, state_);
+      break;
     case kClosed:
-      DCHECK_EQ(kWriting, state_);
+      DCHECK(state_ == kWriting || state_ == kEof);
       break;
     case kFailed:
       // Any state can change to kFailed.
diff --git a/remoting/host/file_transfer/file_transfer_message_handler.h b/remoting/host/file_transfer/file_transfer_message_handler.h
index 0b735ca..4db624e 100644
--- a/remoting/host/file_transfer/file_transfer_message_handler.h
+++ b/remoting/host/file_transfer/file_transfer_message_handler.h
@@ -37,27 +37,56 @@
   enum State {
     // Initial state.
     kConnected,
-    // We are writing a file.
+    // Writing a file from the client.
     kWriting,
+    // Reading a file and sending to the client.
+    kReading,
+    // Reading complete and waiting for confirmation from client.
+    kEof,
     // End states
     // File successfully written.
     kClosed,
     // An error occured or the transfer was canceled.
     kFailed,
   };
-  void StartFile(protocol::FileTransfer_Metadata metadata);
-  void Write(std::string data);
-  void Close();
-  void Cancel();
-  void OnComplete();
+
+  // Handlers for specific messages from the client.
+  void OnMetadata(protocol::FileTransfer_Metadata metadata);
+  void OnData(std::string data);
+  void OnEnd();
+  void OnRequestTransfer();
+  void OnSuccess();
   void OnError(protocol::FileTransfer_Error error);
-  void SendResult(protocol::FileTransferResult<Monostate> result);
-  void CancelAndSendError(protocol::FileTransfer_Error error);
+
+  // File reading callbacks.
+  void OnOpenResult(FileOperations::Reader::OpenResult result);
+  void OnReadResult(FileOperations::Reader::ReadResult result);
+  void OnChunkSent();
+
+  // File writing callbacks.
+  void OnWritingComplete();
+  void OnWriteError(protocol::FileTransfer_Error error);
+
+  // Reads the next chunk in reading mode.
+  void ReadNextChunk();
+
+  // Cancels any current operation and transitions to failed state.
+  void Cancel();
+
+  // Sends an error message to the client.
+  void SendError(protocol::FileTransfer_Error error);
+
+  // Handles an unexpected message being received.
+  void UnexpectedMessage(base::Location from_here, const char* message);
+
   void SetState(State state);
 
   State state_ = kConnected;
   std::unique_ptr<FileOperations> file_operations_;
   base::Optional<BufferedFileWriter> buffered_file_writer_;
+  std::unique_ptr<FileOperations::Reader> file_reader_;
+  std::size_t queued_chunks_ = 0;
+  base::WeakPtrFactory<FileTransferMessageHandler> weak_ptr_factory_;
 };
 
 }  // namespace remoting
diff --git a/remoting/host/file_transfer/file_transfer_message_handler_unittest.cc b/remoting/host/file_transfer/file_transfer_message_handler_unittest.cc
index f32204e..e4cf0da 100644
--- a/remoting/host/file_transfer/file_transfer_message_handler_unittest.cc
+++ b/remoting/host/file_transfer/file_transfer_message_handler_unittest.cc
@@ -87,6 +87,8 @@
   std::unique_ptr<protocol::FakeMessagePipe> fake_pipe_;
   protocol::FileTransfer fake_metadata_;
   protocol::FileTransfer fake_end_;
+  protocol::FileTransfer fake_request_transfer_;
+  protocol::FileTransfer fake_success_;
 };
 
 FileTransferMessageHandlerTest::FileTransferMessageHandlerTest()
@@ -99,18 +101,20 @@
   fake_pipe_ =
       base::WrapUnique(new protocol::FakeMessagePipe(false /* asynchronous */));
 
-  fake_metadata_.Clear();
   fake_metadata_.mutable_metadata()->set_filename(kTestFilename);
   fake_metadata_.mutable_metadata()->set_size(
       kTestDataOne.size() + kTestDataTwo.size() + kTestDataThree.size());
-  fake_end_.Clear();
   fake_end_.mutable_end();
+  fake_request_transfer_.mutable_request_transfer();
+  fake_success_.mutable_success();
 }
 
 void FileTransferMessageHandlerTest::TearDown() {}
 
-// Verify that the message handler creates, writes to, and closes a
-// FileProxyWrapper without errors when given valid input.
+// Upload tests.
+
+// Verifies that the message handler creates, writes to, and closes a
+// FileOperations::Writer without errors when given valid input.
 TEST_F(FileTransferMessageHandlerTest, WritesThreeChunks) {
   FakeFileOperations::TestIo test_io;
   auto file_operations = std::make_unique<FakeFileOperations>(&test_io);
@@ -137,15 +141,13 @@
 
   const base::queue<std::string>& actual_sent_messages =
       fake_pipe_->sent_messages();
-  protocol::FileTransfer expected_response;
-  expected_response.mutable_success();
   base::queue<std::string> expected_sent_messages;
-  expected_sent_messages.push(expected_response.SerializeAsString());
+  expected_sent_messages.push(fake_success_.SerializeAsString());
   ASSERT_TRUE(QueuesEqual(expected_sent_messages, actual_sent_messages));
 }
 
 // Verifies that the message handler sends an error protobuf when
-// FileProxyWrapper returns an error.
+// FileOperations::Writer returns an error.
 TEST_F(FileTransferMessageHandlerTest, HandlesWriteError) {
   FakeFileOperations::TestIo test_io;
   auto file_operations = std::make_unique<FakeFileOperations>(&test_io);
@@ -331,4 +333,101 @@
             response.error().type());
 }
 
+// Download tests.
+
+// Verifies that the message handler will read and respond with a file when a
+// RequestTransfer message is received.
+TEST_F(FileTransferMessageHandlerTest, ReadsFile) {
+  FakeFileOperations::TestIo test_io;
+  auto file_operations = std::make_unique<FakeFileOperations>(&test_io);
+
+  test_io.input_file = FakeFileOperations::InputFile(
+      base::FilePath::FromUTF8Unsafe(kTestFilename),
+      kTestDataOne + kTestDataTwo + kTestDataThree, base::nullopt);
+
+  // This will delete itself when fake_pipe_->ClosePipe() is called.
+  new FileTransferMessageHandler(kTestDatachannelName, fake_pipe_->Wrap(),
+                                 std::move(file_operations));
+
+  fake_pipe_->OpenPipe();
+  fake_pipe_->Receive(MessageToBuffer(fake_request_transfer_));
+  scoped_task_environment_.RunUntilIdle();
+  fake_pipe_->Receive(MessageToBuffer(fake_success_));
+  scoped_task_environment_.RunUntilIdle();
+
+  fake_pipe_->ClosePipe();
+
+  const base::queue<std::string>& actual_sent_messages =
+      fake_pipe_->sent_messages();
+  base::queue<std::string> expected_sent_messages;
+  expected_sent_messages.push(fake_metadata_.SerializeAsString());
+  protocol::FileTransfer data;
+  data.mutable_data()->set_data(test_io.input_file->data);
+  expected_sent_messages.push(data.SerializeAsString());
+  expected_sent_messages.push(fake_end_.SerializeAsString());
+  ASSERT_TRUE(QueuesEqual(expected_sent_messages, actual_sent_messages));
+}
+
+// Verifies that the message handler forwards errors from opening a file with
+// the reader.
+TEST_F(FileTransferMessageHandlerTest, ForwardsReaderOpenError) {
+  FakeFileOperations::TestIo test_io;
+  auto file_operations = std::make_unique<FakeFileOperations>(&test_io);
+
+  test_io.input_file = protocol::MakeFileTransferError(
+      FROM_HERE, protocol::FileTransfer_Error_Type_CANCELED);
+
+  // This will delete itself when fake_pipe_->ClosePipe() is called.
+  new FileTransferMessageHandler(kTestDatachannelName, fake_pipe_->Wrap(),
+                                 std::move(file_operations));
+
+  fake_pipe_->OpenPipe();
+  fake_pipe_->Receive(MessageToBuffer(fake_request_transfer_));
+  scoped_task_environment_.RunUntilIdle();
+
+  fake_pipe_->ClosePipe();
+
+  const base::queue<std::string>& actual_sent_messages =
+      fake_pipe_->sent_messages();
+  base::queue<std::string> expected_sent_messages;
+  protocol::FileTransfer error;
+  *error.mutable_error() = test_io.input_file.error();
+  expected_sent_messages.push(error.SerializeAsString());
+  ASSERT_TRUE(QueuesEqual(expected_sent_messages, actual_sent_messages));
+}
+
+// Verifies that the message handler forwards errors from reading a file.
+TEST_F(FileTransferMessageHandlerTest, ForwardsReadError) {
+  FakeFileOperations::TestIo test_io;
+  auto file_operations = std::make_unique<FakeFileOperations>(&test_io);
+
+  test_io.input_file = FakeFileOperations::InputFile(
+      base::FilePath::FromUTF8Unsafe(kTestFilename),
+      kTestDataOne + kTestDataTwo + kTestDataThree,
+      protocol::MakeFileTransferError(
+          FROM_HERE, protocol::FileTransfer_Error_Type_IO_ERROR));
+
+  // This will delete itself when fake_pipe_->ClosePipe() is called.
+  new FileTransferMessageHandler(kTestDatachannelName, fake_pipe_->Wrap(),
+                                 std::move(file_operations));
+
+  fake_pipe_->OpenPipe();
+  fake_pipe_->Receive(MessageToBuffer(fake_request_transfer_));
+  scoped_task_environment_.RunUntilIdle();
+
+  fake_pipe_->ClosePipe();
+
+  const base::queue<std::string>& actual_sent_messages =
+      fake_pipe_->sent_messages();
+  base::queue<std::string> expected_sent_messages;
+  expected_sent_messages.push(fake_metadata_.SerializeAsString());
+  protocol::FileTransfer data;
+  data.mutable_data()->set_data(test_io.input_file->data);
+  expected_sent_messages.push(data.SerializeAsString());
+  protocol::FileTransfer error;
+  *error.mutable_error() = *test_io.input_file->io_error;
+  expected_sent_messages.push(error.SerializeAsString());
+  ASSERT_TRUE(QueuesEqual(expected_sent_messages, actual_sent_messages));
+}
+
 }  // namespace remoting
diff --git a/remoting/host/process_stats_sender_unittest.cc b/remoting/host/process_stats_sender_unittest.cc
index a4b28096..04d9593 100644
--- a/remoting/host/process_stats_sender_unittest.cc
+++ b/remoting/host/process_stats_sender_unittest.cc
@@ -6,6 +6,7 @@
 
 #include <stdint.h>
 
+#include <functional>
 #include <utility>
 #include <vector>
 
@@ -112,7 +113,7 @@
         stats->reset();
         run_loop->Quit();
       },
-      base::Unretained(&stats), base::ConstRef(stub), base::ConstRef(agent),
+      base::Unretained(&stats), std::cref(stub), std::cref(agent),
       base::Unretained(&run_loop)));
   message_loop.task_runner()->PostTask(
       FROM_HERE,
@@ -151,8 +152,8 @@
         stats->reset();
         run_loop->Quit();
       },
-      base::Unretained(&stats), base::ConstRef(stub), base::ConstRef(agent1),
-      base::ConstRef(agent2), base::Unretained(&run_loop)));
+      base::Unretained(&stats), std::cref(stub), std::cref(agent1),
+      std::cref(agent2), base::Unretained(&run_loop)));
   message_loop.task_runner()->PostTask(
       FROM_HERE,
       base::BindOnce(
diff --git a/remoting/proto/file_transfer.proto b/remoting/proto/file_transfer.proto
index 1cbeecd..33ae17c 100644
--- a/remoting/proto/file_transfer.proto
+++ b/remoting/proto/file_transfer.proto
@@ -33,6 +33,11 @@
   // Next Id: 1
   message Success {}
 
+  // Receiver->sender message sent requesting the sender to select and send a
+  // file. This must be the first message on the connection.
+  // Next Id: 1
+  message RequestTransfer {}
+
   // Bidirectional message aborting the transfer due to an error or user
   // cancellation. This may be sent by the sender to signal a read error, by the
   // receiver to signal a write error, or by either side to signal any of the
@@ -57,9 +62,9 @@
     optional Type type = 1;
     // The error code returned by the failed API call (if applicable).
     optional int32 api_error_code = 2;
-    // The function in which the error occured.
+    // The function in which the error occurred.
     optional string function = 3;
-    // The source file in which the error occured.
+    // The source file in which the error occurred.
     optional string source_file = 4;
     // The line number on which the error occurred.
     optional uint32 line_number = 5;
@@ -70,6 +75,7 @@
     Data data = 2;
     End end = 3;
     Success success = 4;
-    Error error = 5;
+    RequestTransfer request_transfer = 5;
+    Error error = 6;
   }
 }
diff --git a/remoting/protocol/ssl_hmac_channel_authenticator.cc b/remoting/protocol/ssl_hmac_channel_authenticator.cc
index 44a4290..e59b2583 100644
--- a/remoting/protocol/ssl_hmac_channel_authenticator.cc
+++ b/remoting/protocol/ssl_hmac_channel_authenticator.cc
@@ -28,9 +28,9 @@
 #include "net/cert/x509_certificate.h"
 #include "net/http/transport_security_state.h"
 #include "net/log/net_log_with_source.h"
-#include "net/socket/client_socket_handle.h"
 #include "net/socket/ssl_client_socket.h"
 #include "net/socket/ssl_server_socket.h"
+#include "net/socket/stream_socket.h"
 #include "net/ssl/ssl_config_service.h"
 #include "net/ssl/ssl_server_config.h"
 #include "net/traffic_annotation/network_traffic_annotation.h"
@@ -306,19 +306,16 @@
     context.cert_verifier = cert_verifier_.get();
     context.cert_transparency_verifier = ct_verifier_.get();
     context.ct_policy_enforcer = ct_policy_enforcer_.get();
-    std::unique_ptr<net::ClientSocketHandle> socket_handle(
-        new net::ClientSocketHandle);
-    socket_handle->SetSocket(
-        std::make_unique<NetStreamSocketAdapter>(std::move(socket)));
-
+    std::unique_ptr<net::StreamSocket> stream_socket =
+        std::make_unique<NetStreamSocketAdapter>(std::move(socket));
 #if defined(OS_NACL)
     // net_nacl doesn't include ClientSocketFactory.
     socket_.reset(new net::SSLClientSocketImpl(
-        std::move(socket_handle), host_and_port, ssl_config, context));
+        std::move(stream_socket), host_and_port, ssl_config, context));
 #else
     socket_ =
         net::ClientSocketFactory::GetDefaultFactory()->CreateSSLClientSocket(
-            std::move(socket_handle), host_and_port, ssl_config, context);
+            std::move(stream_socket), host_and_port, ssl_config, context);
 #endif
 
     result = socket_->Connect(
diff --git a/remoting/protocol/webrtc_dummy_video_encoder.cc b/remoting/protocol/webrtc_dummy_video_encoder.cc
index 6261e73..e72cb02c 100644
--- a/remoting/protocol/webrtc_dummy_video_encoder.cc
+++ b/remoting/protocol/webrtc_dummy_video_encoder.cc
@@ -162,7 +162,6 @@
   encoded_image.content_type_ = webrtc::VideoContentType::SCREENSHARE;
 
   webrtc::CodecSpecificInfo codec_specific_info;
-  memset(&codec_specific_info, 0, sizeof(codec_specific_info));
   codec_specific_info.codecType = frame.codec;
 
   if (frame.codec == webrtc::kVideoCodecVP8) {
@@ -182,6 +181,12 @@
     vp9_info->num_spatial_layers = 1;
     vp9_info->gof_idx = webrtc::kNoGofIdx;
     vp9_info->temporal_idx = webrtc::kNoTemporalIdx;
+    vp9_info->flexible_mode = false;
+    vp9_info->temporal_up_switch = true;
+    vp9_info->inter_layer_predicted = false;
+    vp9_info->first_frame_in_picture = true;
+    vp9_info->end_of_picture = true;
+    vp9_info->spatial_layer_resolution_present = false;
   } else if (frame.codec == webrtc::kVideoCodecH264) {
 #if defined(USE_H264_ENCODER)
     webrtc::CodecSpecificInfoH264* h264_info =
diff --git a/remoting/signaling/xmpp_signal_strategy.cc b/remoting/signaling/xmpp_signal_strategy.cc
index ab0a948..4cf93c0 100644
--- a/remoting/signaling/xmpp_signal_strategy.cc
+++ b/remoting/signaling/xmpp_signal_strategy.cc
@@ -24,8 +24,8 @@
 #include "net/cert/multi_log_ct_verifier.h"
 #include "net/http/transport_security_state.h"
 #include "net/socket/client_socket_factory.h"
-#include "net/socket/client_socket_handle.h"
 #include "net/socket/ssl_client_socket.h"
+#include "net/socket/stream_socket.h"
 #include "net/traffic_annotation/network_traffic_annotation.h"
 #include "net/url_request/url_request_context_getter.h"
 #include "remoting/base/buffered_socket_writer.h"
@@ -336,10 +336,6 @@
 
   DCHECK(!read_pending_);
 
-  std::unique_ptr<net::ClientSocketHandle> socket_handle(
-      new net::ClientSocketHandle());
-  socket_handle->SetSocket(std::move(socket_));
-
   cert_verifier_ = net::CertVerifier::CreateDefault();
   transport_security_state_.reset(new net::TransportSecurityState());
   cert_transparency_verifier_.reset(new net::MultiLogCTVerifier());
@@ -351,7 +347,7 @@
   context.ct_policy_enforcer = ct_policy_enforcer_.get();
 
   socket_ = socket_factory_->CreateSSLClientSocket(
-      std::move(socket_handle),
+      std::move(socket_),
       net::HostPortPair(xmpp_server_config_.host, kDefaultHttpsPort),
       net::SSLConfig(), context);
 
diff --git a/remoting/signaling/xmpp_signal_strategy_unittest.cc b/remoting/signaling/xmpp_signal_strategy_unittest.cc
index 5884ef4..923caaf 100644
--- a/remoting/signaling/xmpp_signal_strategy_unittest.cc
+++ b/remoting/signaling/xmpp_signal_strategy_unittest.cc
@@ -12,6 +12,7 @@
 #include "base/message_loop/message_loop.h"
 #include "base/run_loop.h"
 #include "net/socket/socket_test_util.h"
+#include "net/socket/stream_socket.h"
 #include "net/url_request/url_request_test_util.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/libjingle_xmpp/xmllite/xmlelement.h"
@@ -92,13 +93,13 @@
 class MockClientSocketFactory : public net::MockClientSocketFactory {
  public:
   std::unique_ptr<net::SSLClientSocket> CreateSSLClientSocket(
-      std::unique_ptr<net::ClientSocketHandle> transport_socket,
+      std::unique_ptr<net::StreamSocket> stream_socket,
       const net::HostPortPair& host_and_port,
       const net::SSLConfig& ssl_config,
       const net::SSLClientSocketContext& context) override {
     ssl_socket_created_ = true;
     return net::MockClientSocketFactory::CreateSSLClientSocket(
-        std::move(transport_socket), host_and_port, ssl_config, context);
+        std::move(stream_socket), host_and_port, ssl_config, context);
   }
 
   bool ssl_socket_created() const { return ssl_socket_created_; }
diff --git a/remoting/test/it2me_standalone_host.cc b/remoting/test/it2me_standalone_host.cc
index 944172a1..4c8bb7a 100644
--- a/remoting/test/it2me_standalone_host.cc
+++ b/remoting/test/it2me_standalone_host.cc
@@ -4,6 +4,7 @@
 
 #include "remoting/test/it2me_standalone_host.h"
 
+#include <functional>
 #include <iostream>
 #include <vector>
 
@@ -79,9 +80,9 @@
 }
 
 void It2MeStandaloneHost::StartOutputTimer() {
-  timer_.Start(FROM_HERE, base::TimeDelta::FromSeconds(1),
-               base::Bind(&OutputFakeConnectionEventLogger,
-                          base::ConstRef(event_logger_)));
+  timer_.Start(
+      FROM_HERE, base::TimeDelta::FromSeconds(1),
+      base::Bind(&OutputFakeConnectionEventLogger, std::cref(event_logger_)));
 }
 
 void It2MeStandaloneHost::Connect() {
diff --git a/sandbox/linux/seccomp-bpf-helpers/syscall_parameters_restrictions.cc b/sandbox/linux/seccomp-bpf-helpers/syscall_parameters_restrictions.cc
index 55394a7..68ce32a 100644
--- a/sandbox/linux/seccomp-bpf-helpers/syscall_parameters_restrictions.cc
+++ b/sandbox/linux/seccomp-bpf-helpers/syscall_parameters_restrictions.cc
@@ -34,12 +34,14 @@
 #if !defined(OS_NACL_NONSFI)
 #include <sys/ioctl.h>
 #include <sys/ptrace.h>
-#if !defined(PTRACE_GET_THREAD_AREA) && defined(OS_LINUX) && \
-    !defined(OS_CHROMEOS)
+#if defined(OS_LINUX) && !defined(OS_CHROMEOS) && !defined(__arm__) && \
+    !defined(__aarch64__) && !defined(PTRACE_GET_THREAD_AREA)
 // Also include asm/ptrace-abi.h since ptrace.h in older libc (for instance
 // the one in Ubuntu 16.04 LTS) is missing PTRACE_GET_THREAD_AREA.
+// asm/ptrace-abi.h doesn't exist on arm32 and PTRACE_GET_THREAD_AREA isn't
+// defined on aarch64, so don't try to include this on those platforms.
 #include <asm/ptrace-abi.h>
-#endif  // !PTRACE_GET_THREAD_AREA && OS_LINUX && !OS_CHROMEOS
+#endif
 #endif  // !OS_NACL_NONSFI
 
 #if defined(OS_ANDROID)
diff --git a/services/image_annotation/image_annotation_service.cc b/services/image_annotation/image_annotation_service.cc
index eb51a80..2ca3dbc 100644
--- a/services/image_annotation/image_annotation_service.cc
+++ b/services/image_annotation/image_annotation_service.cc
@@ -12,7 +12,9 @@
 
 namespace image_annotation {
 
-constexpr base::Feature ImageAnnotationService::kExperiment;
+// static
+const base::Feature ImageAnnotationService::kExperiment{
+    "ImageAnnotationServiceExperimental", base::FEATURE_DISABLED_BY_DEFAULT};
 constexpr base::FeatureParam<std::string> ImageAnnotationService::kServerUrl;
 constexpr base::FeatureParam<std::string> ImageAnnotationService::kApiKey;
 constexpr base::FeatureParam<int> ImageAnnotationService::kThrottleMs;
diff --git a/services/image_annotation/image_annotation_service.h b/services/image_annotation/image_annotation_service.h
index 9cfc8d2..61348ee 100644
--- a/services/image_annotation/image_annotation_service.h
+++ b/services/image_annotation/image_annotation_service.h
@@ -23,9 +23,8 @@
 
 class ImageAnnotationService : public service_manager::Service {
  public:
-  // Whether or not to enable service logic for experimentation.
-  static constexpr base::Feature kExperiment{
-      "ImageAnnotationServiceExperimental", base::FEATURE_DISABLED_BY_DEFAULT};
+  // Whether or not to override service parameters for experimentation.
+  static const base::Feature kExperiment;
 
   ImageAnnotationService(
       service_manager::mojom::ServiceRequest request,
@@ -36,10 +35,9 @@
  private:
   // Service params:
 
-  // The service will fail gracefully (i.e. return error codes) when this param
-  // is empty. This ensures graceful behavior when |kExperiment| is disabled.
-  static constexpr base::FeatureParam<std::string> kServerUrl{&kExperiment,
-                                                              "server_url", ""};
+  static constexpr base::FeatureParam<std::string> kServerUrl{
+      &kExperiment, "server_url",
+      "https://ckintersect-pa.googleapis.com/v1/intersect/pixels"};
   // An override Google API key. If empty, the API key with which the browser
   // was built (if any) will be used instead.
   static constexpr base::FeatureParam<std::string> kApiKey{&kExperiment,
diff --git a/services/metrics/public/cpp/ukm_source.cc b/services/metrics/public/cpp/ukm_source.cc
index 20f6d5b8..64d47c8 100644
--- a/services/metrics/public/cpp/ukm_source.cc
+++ b/services/metrics/public/cpp/ukm_source.cc
@@ -58,8 +58,12 @@
   NavigationData sanitized_navigation_data;
   sanitized_navigation_data.urls = std::move(sanitized_urls);
   sanitized_navigation_data.previous_source_id = previous_source_id;
+  sanitized_navigation_data.previous_same_document_source_id =
+      previous_same_document_source_id;
   sanitized_navigation_data.opener_source_id = opener_source_id;
   sanitized_navigation_data.tab_id = tab_id;
+  sanitized_navigation_data.is_same_document_navigation =
+      is_same_document_navigation;
   return sanitized_navigation_data;
 }
 
@@ -110,6 +114,11 @@
   if (navigation_data_.previous_source_id != kInvalidSourceId)
     proto_source->set_previous_source_id(navigation_data_.previous_source_id);
 
+  if (navigation_data_.previous_same_document_source_id != kInvalidSourceId) {
+    proto_source->set_previous_same_document_source_id(
+        navigation_data_.previous_same_document_source_id);
+  }
+
   if (navigation_data_.opener_source_id != kInvalidSourceId)
     proto_source->set_opener_source_id(navigation_data_.opener_source_id);
 
@@ -117,6 +126,9 @@
   // source_url_recorder.cc
   if (navigation_data_.tab_id != 0)
     proto_source->set_tab_id(navigation_data_.tab_id);
+
+  if (navigation_data_.is_same_document_navigation)
+    proto_source->set_is_same_document_navigation(true);
 }
 
 }  // namespace ukm
diff --git a/services/metrics/public/cpp/ukm_source.h b/services/metrics/public/cpp/ukm_source.h
index 95943bf..0a7a1d76 100644
--- a/services/metrics/public/cpp/ukm_source.h
+++ b/services/metrics/public/cpp/ukm_source.h
@@ -61,6 +61,12 @@
     // The previous source id for this tab.
     SourceId previous_source_id = kInvalidSourceId;
 
+    // The source id for the previous same document navigation, if the
+    // previously committed source was a same document navigation. If
+    // the previously committed source was not a same document
+    // navigation, this field will be set to kInvalidSourceId.
+    SourceId previous_same_document_source_id = kInvalidSourceId;
+
     // The source id for the source which opened this tab. This should be set to
     // kInvalidSourceId for all but the first navigation in the tab.
     SourceId opener_source_id = kInvalidSourceId;
@@ -68,6 +74,11 @@
     // A unique identifier for the tab the source navigated in. Tab ids should
     // be increasing over time within a session.
     int64_t tab_id = 0;
+
+    // Whether this source is for a same document navigation. Examples of same
+    // document navigations are fragment navigations, pushState/replaceState,
+    // and same page history navigation.
+    bool is_same_document_navigation = false;
   };
 
   UkmSource(SourceId id, const GURL& url);
diff --git a/services/network/cert_verify_proc_chromeos_unittest.cc b/services/network/cert_verify_proc_chromeos_unittest.cc
index a2fcd02..6c5934c 100644
--- a/services/network/cert_verify_proc_chromeos_unittest.cc
+++ b/services/network/cert_verify_proc_chromeos_unittest.cc
@@ -11,6 +11,7 @@
 #include "net/base/net_errors.h"
 #include "net/cert/cert_verify_proc.h"
 #include "net/cert/cert_verify_result.h"
+#include "net/cert/crl_set.h"
 #include "net/cert/nss_cert_database_chromeos.h"
 #include "net/cert/x509_util.h"
 #include "net/cert/x509_util_nss.h"
@@ -106,9 +107,9 @@
       std::string* root_subject_name) {
     int flags = 0;
     net::CertVerifyResult verify_result;
-    int error =
-        verify_proc->Verify(cert, "127.0.0.1", std::string(), flags, NULL,
-                            additional_trust_anchors, &verify_result);
+    int error = verify_proc->Verify(cert, "127.0.0.1", std::string(), flags,
+                                    net::CRLSet::BuiltinCRLSet().get(),
+                                    additional_trust_anchors, &verify_result);
     if (!verify_result.verified_cert->intermediate_buffers().empty()) {
       root_subject_name->assign(GetSubjectCN(
           verify_result.verified_cert->intermediate_buffers().back().get()));
diff --git a/services/network/cookie_manager_unittest.cc b/services/network/cookie_manager_unittest.cc
index 0647cf1..5b3afe6 100644
--- a/services/network/cookie_manager_unittest.cc
+++ b/services/network/cookie_manager_unittest.cc
@@ -486,16 +486,16 @@
 
   // Retrieve only unrestricted cookies.
   net::CookieOptions options;
-  EXPECT_EQ(net::CookieOptions::SameSiteCookieMode::DO_NOT_INCLUDE,
-            options.same_site_cookie_mode());
+  EXPECT_EQ(net::CookieOptions::SameSiteCookieContext::CROSS_SITE,
+            options.same_site_cookie_context());
   std::vector<net::CanonicalCookie> cookies = service_wrapper()->GetCookieList(
       GURL("https://foo_host.com/with/path"), options);
   ASSERT_EQ(1u, cookies.size());
   EXPECT_EQ("A", cookies[0].Name());
 
   // Retrieve unrestricted and lax cookies.
-  options.set_same_site_cookie_mode(
-      net::CookieOptions::SameSiteCookieMode::INCLUDE_LAX);
+  options.set_same_site_cookie_context(
+      net::CookieOptions::SameSiteCookieContext::SAME_SITE_LAX);
   cookies = service_wrapper()->GetCookieList(
       GURL("https://foo_host.com/with/path"), options);
   ASSERT_EQ(2u, cookies.size());
@@ -504,8 +504,8 @@
   EXPECT_EQ("C", cookies[1].Name());
 
   // Retrieve everything.
-  options.set_same_site_cookie_mode(
-      net::CookieOptions::SameSiteCookieMode::INCLUDE_STRICT_AND_LAX);
+  options.set_same_site_cookie_context(
+      net::CookieOptions::SameSiteCookieContext::SAME_SITE_STRICT);
   cookies = service_wrapper()->GetCookieList(
       GURL("https://foo_host.com/with/path"), options);
   ASSERT_EQ(3u, cookies.size());
diff --git a/services/network/cookie_settings.cc b/services/network/cookie_settings.cc
index 82fc269..b0f9ac8 100644
--- a/services/network/cookie_settings.cc
+++ b/services/network/cookie_settings.cc
@@ -3,6 +3,9 @@
 // found in the LICENSE file.
 
 #include "services/network/cookie_settings.h"
+
+#include <functional>
+
 #include "base/bind.h"
 #include "net/base/net_errors.h"
 #include "net/base/static_cookie_policy.h"
@@ -24,7 +27,7 @@
     return SessionCleanupCookieStore::DeleteCookiePredicate();
   return base::BindRepeating(&CookieSettings::ShouldDeleteCookieOnExit,
                              base::Unretained(this),
-                             base::ConstRef(content_settings_));
+                             std::cref(content_settings_));
 }
 
 void CookieSettings::GetCookieSetting(const GURL& url,
diff --git a/services/network/expect_ct_reporter.cc b/services/network/expect_ct_reporter.cc
index 2692cdc..d98a015 100644
--- a/services/network/expect_ct_reporter.cc
+++ b/services/network/expect_ct_reporter.cc
@@ -79,7 +79,7 @@
   return "";
 }
 
-void AddSCT(const net::SignedCertificateTimestampAndStatus& sct,
+bool AddSCT(const net::SignedCertificateTimestampAndStatus& sct,
             base::ListValue* list) {
   std::unique_ptr<base::DictionaryValue> list_item(new base::DictionaryValue());
   // Chrome implements RFC6962, not 6962-bis, so the reports contain v1 SCTs.
@@ -102,11 +102,13 @@
   list_item->SetString("status", status);
   list_item->SetString("source", SCTOriginToString(sct.sct->origin));
   std::string serialized_sct;
-  net::ct::EncodeSignedCertificateTimestamp(sct.sct, &serialized_sct);
+  if (!net::ct::EncodeSignedCertificateTimestamp(sct.sct, &serialized_sct))
+    return false;
   std::string encoded_serialized_sct;
   base::Base64Encode(serialized_sct, &encoded_serialized_sct);
   list_item->SetString("serialized_sct", encoded_serialized_sct);
   list->Append(std::move(list_item));
+  return true;
 }
 
 constexpr net::NetworkTrafficAnnotationTag kExpectCTReporterTrafficAnnotation =
@@ -176,7 +178,8 @@
 
   std::unique_ptr<base::ListValue> scts(new base::ListValue());
   for (const auto& sct_and_status : signed_certificate_timestamps) {
-    AddSCT(sct_and_status, scts.get());
+    if (!AddSCT(sct_and_status, scts.get()))
+      LOG(ERROR) << "Failed to add signed certificate timestamp to list";
   }
   report->Set("scts", std::move(scts));
 
diff --git a/services/network/expect_ct_reporter_unittest.cc b/services/network/expect_ct_reporter_unittest.cc
index bf8f9a6..ac1f340 100644
--- a/services/network/expect_ct_reporter_unittest.cc
+++ b/services/network/expect_ct_reporter_unittest.cc
@@ -150,8 +150,10 @@
     net::ct::SCTVerifyStatus expected_status,
     const base::ListValue& report_list) {
   std::string expected_serialized_sct;
-  net::ct::EncodeSignedCertificateTimestamp(expected_sct,
-                                            &expected_serialized_sct);
+  if (!net::ct::EncodeSignedCertificateTimestamp(expected_sct,
+                                                 &expected_serialized_sct)) {
+    return ::testing::AssertionFailure() << "Failed to serialize SCT";
+  }
 
   for (size_t i = 0; i < report_list.GetSize(); i++) {
     const base::DictionaryValue* report_sct;
diff --git a/services/network/mdns_responder.cc b/services/network/mdns_responder.cc
index f1f8c02..a3aadf77 100644
--- a/services/network/mdns_responder.cc
+++ b/services/network/mdns_responder.cc
@@ -29,9 +29,7 @@
 #include "net/dns/public/util.h"
 #include "net/dns/record_parsed.h"
 #include "net/dns/record_rdata.h"
-#include "net/socket/datagram_client_socket.h"
 #include "net/socket/datagram_server_socket.h"
-#include "net/socket/udp_client_socket.h"
 #include "net/socket/udp_server_socket.h"
 
 // TODO(qingsi): Several features to implement:
@@ -76,34 +74,6 @@
 // Maximum delay allowed for per-response rate-limited responses.
 const base::TimeDelta kMaxScheduledDelay = base::TimeDelta::FromSeconds(10);
 
-constexpr net::NetworkTrafficAnnotationTag kTrafficAnnotation =
-    net::DefineNetworkTrafficAnnotation("mdns_responder", R"(
-        semantics {
-          sender: "mDNS Responder"
-          description:
-            "mDNS responder implements a multicast DNS responder as defined in "
-            "RFC 6762."
-          trigger:
-            "Any network request that may require name registration or "
-            "deregistration, and also mDNS queries for name resolution from "
-            "the local network."
-          data:
-            "DNS records of type A, AAAA or NSEC for name registration or "
-            "resolution."
-          destination: OTHER
-          destination_other:
-            "mDNS responses are sent to the mDNS multicast groups within the "
-            "subnets where the user resides."
-          }
-        policy {
-          cookies_allowed: NO
-          setting:
-            "No setting for this feature. Individual usages may have their own "
-            "disabling flags."
-          policy_exception_justification:
-            "This is core networking functionality on local networks."
-        })");
-
 class RandomUuidNameGenerator
     : public network::MdnsResponderManager::NameGenerator {
  public:
@@ -282,12 +252,11 @@
 class MdnsResponderManager::SocketHandler {
  public:
   SocketHandler(uint16_t id,
-                net::MDnsSendRecvSocketPair socket_pair,
+                std::unique_ptr<net::DatagramServerSocket> socket,
                 MdnsResponderManager* responder_manager)
       : id_(id),
         scheduler_(std::make_unique<ResponseScheduler>(this)),
-        send_socket_(std::move(socket_pair.first)),
-        recv_socket_(std::move(socket_pair.second)),
+        socket_(std::move(socket)),
         responder_manager_(responder_manager),
         io_buffer_(base::MakeRefCounted<net::IOBufferWithSize>(
             net::dns_protocol::kMaxUDPSize + 1)),
@@ -296,17 +265,14 @@
 
   int Start() {
     net::IPEndPoint end_point;
-    int rv = recv_socket_->GetLocalAddress(&end_point);
-    if (rv != net::OK)
+    int rv = socket_->GetLocalAddress(&end_point);
+    if (rv != net::OK) {
       return rv;
-    const net::AddressFamily af = end_point.GetFamily();
-#ifdef DEBUG
-    DCHECK(af == net::ADDRESS_FAMILY_IPV4 || af == net::ADDRESS_FAMILY_IPV6);
-    net::IPEndPoint send_socket_end_point;
-    DCHECK(send_socket_->GetLocalAddress(&send_socket_end_point));
-    DCHECK_EQ(af, send_socket_end_point.GetFamily());
-#endif
-    multicast_addr_ = net::dns_util::GetMdnsGroupEndPoint(af);
+    }
+    DCHECK(end_point.GetFamily() == net::ADDRESS_FAMILY_IPV4 ||
+           end_point.GetFamily() == net::ADDRESS_FAMILY_IPV6);
+    multicast_addr_ =
+        net::dns_util::GetMdnsGroupEndPoint(end_point.GetFamily());
     int result = DoReadLoop();
     if (result == net::ERR_IO_PENDING) {
       // An in-progress read loop is considered a completed start.
@@ -338,9 +304,9 @@
     int result;
     do {
       // Using base::Unretained(this) is safe because the CompletionOnceCallback
-      // is automatically cancelled when |recv_socket_| is destroyed, and the
-      // latter is owned by |this|.
-      result = recv_socket_->RecvFrom(
+      // is automatically cancelled when |socket_| is destroyed, and the latter
+      // is owned by |this|.
+      result = socket_->RecvFrom(
           io_buffer_.get(), io_buffer_->size(), &recv_addr_,
           base::BindOnce(&MdnsResponderManager::SocketHandler::OnRead,
                          base::Unretained(this)));
@@ -366,8 +332,7 @@
 
   uint16_t id_;
   std::unique_ptr<ResponseScheduler> scheduler_;
-  std::unique_ptr<net::DatagramClientSocket> send_socket_;
-  std::unique_ptr<net::DatagramServerSocket> recv_socket_;
+  std::unique_ptr<net::DatagramServerSocket> socket_;
   // A back pointer to the responder manager that owns this socket handler. The
   // handler should be destroyed before |responder_manager_| becomes invalid or
   // a weak reference should be used to access the manager when there is no such
@@ -521,11 +486,10 @@
     scoped_refptr<MdnsResponseSendOption> option) {
   auto* buf_data = buf.get();
   size_t buf_size = buf->size();
-  send_socket_->Write(buf_data, buf_size,
-                      base::BindOnce(&ResponseScheduler::OnResponseSent,
-                                     scheduler_->GetWeakPtr(), std::move(buf),
-                                     std::move(option)),
-                      kTrafficAnnotation);
+  socket_->SendTo(buf_data, buf_size, multicast_addr_,
+                  base::BindOnce(&ResponseScheduler::OnResponseSent,
+                                 scheduler_->GetWeakPtr(), std::move(buf),
+                                 std::move(option)));
 }
 
 void MdnsResponderManager::SocketHandler::SetTickClockForTesting(
@@ -654,16 +618,16 @@
   VLOG(1) << "Starting mDNS responder manager.";
   DCHECK(start_result_ == SocketHandlerStartResult::UNSPECIFIED);
   DCHECK(socket_handler_by_id_.empty());
-  std::vector<net::MDnsSendRecvSocketPair> socket_pairs;
+  std::vector<std::unique_ptr<net::DatagramServerSocket>> sockets;
   // Create and return only bound sockets.
-  socket_factory_->CreateSocketPairs(&socket_pairs);
+  socket_factory_->CreateSockets(&sockets);
 
   uint16_t next_available_id = 1;
-  for (auto& send_recv_sockets : socket_pairs) {
+  for (std::unique_ptr<net::DatagramServerSocket>& socket : sockets) {
     socket_handler_by_id_.emplace(
         next_available_id,
         std::make_unique<MdnsResponderManager::SocketHandler>(
-            next_available_id, std::move(send_recv_sockets), this));
+            next_available_id, std::move(socket), this));
     ++next_available_id;
   }
 
@@ -682,7 +646,7 @@
   size_t num_started_socket_handlers = socket_handler_by_id_.size();
   if (socket_handler_by_id_.empty()) {
     start_result_ = SocketHandlerStartResult::ALL_FAILURE;
-    LOG(ERROR) << "mDNS responder manager failed to start.";
+    LOG(ERROR) << "mDNS responder manager failed to started.";
     return;
   }
 
diff --git a/services/network/mdns_responder_unittest.cc b/services/network/mdns_responder_unittest.cc
index e6edb79..286b767 100644
--- a/services/network/mdns_responder_unittest.cc
+++ b/services/network/mdns_responder_unittest.cc
@@ -80,23 +80,22 @@
 
   ~MockFailingMdnsSocketFactory() override = default;
 
-  MOCK_METHOD1(CreateSocketPairs,
-               void(std::vector<net::MDnsSendRecvSocketPair>*));
+  MOCK_METHOD1(CreateSockets,
+               void(std::vector<std::unique_ptr<net::DatagramServerSocket>>*));
 
   MOCK_METHOD1(OnSendTo, void(const std::string&));
 
   // Emulates the asynchronous contract of invoking |callback| in the SendTo
   // primitive but failed sending;
   int FailToSend(const std::string& packet,
-                 net::CompletionOnceCallback* callback,
-                 const net::NetworkTrafficAnnotationTag& traffic_annotation) {
+                 const std::string& address,
+                 net::CompletionRepeatingCallback callback) {
     OnSendTo(packet);
-    task_runner_->PostTask(FROM_HERE,
-                           base::BindOnce(
-                               [](net::CompletionOnceCallback callback) {
-                                 std::move(callback).Run(-1);
-                               },
-                               std::move(*callback)));
+    task_runner_->PostTask(
+        FROM_HERE,
+        base::BindOnce(
+            [](net::CompletionRepeatingCallback callback) { callback.Run(-1); },
+            callback));
     return -1;
   }
 
@@ -105,13 +104,12 @@
   int FailToRecv(net::IOBuffer* buffer,
                  int size,
                  net::IPEndPoint* address,
-                 net::CompletionOnceCallback* callback) {
-    task_runner_->PostTask(FROM_HERE,
-                           base::BindOnce(
-                               [](net::CompletionOnceCallback callback) {
-                                 std::move(callback).Run(-1);
-                               },
-                               std::move(*callback)));
+                 net::CompletionRepeatingCallback callback) {
+    task_runner_->PostTask(
+        FROM_HERE,
+        base::BindOnce(
+            [](net::CompletionRepeatingCallback callback) { callback.Run(-1); },
+            callback));
     return -1;
   }
 
@@ -614,7 +612,7 @@
 // Test that the host generates a Mojo connection error when no socket handler
 // is successfully started.
 TEST_F(MdnsResponderTest, ClosesBindingWhenNoSocketHanlderStarted) {
-  EXPECT_CALL(failing_socket_factory_, CreateSocketPairs(_)).WillOnce(Return());
+  EXPECT_CALL(failing_socket_factory_, CreateSockets(_)).WillOnce(Return());
   Reset(true /* use_failing_socket_factory */);
   RunUntilNoTasksRemain();
   // MdnsResponderTest::OnMojoConnectionError.
@@ -625,30 +623,26 @@
 // Test that an announcement is retried after send failure.
 TEST_F(MdnsResponderTest, AnnouncementRetriedAfterSendFailure) {
   auto create_send_failing_socket =
-      [this](std::vector<net::MDnsSendRecvSocketPair>* socket_pairs) {
-        auto send_socket =
-            std::make_unique<NiceMock<net::MockMDnsDatagramClientSocket>>();
-
-        auto recv_socket =
+      [this](std::vector<std::unique_ptr<net::DatagramServerSocket>>* sockets) {
+        auto socket =
             std::make_unique<NiceMock<net::MockMDnsDatagramServerSocket>>(
                 net::ADDRESS_FAMILY_IPV4);
 
-        ON_CALL(*send_socket, WriteInternal(_, _, _))
+        ON_CALL(*socket, SendToInternal(_, _, _))
             .WillByDefault(Invoke(&failing_socket_factory_,
                                   &MockFailingMdnsSocketFactory::FailToSend));
-        ON_CALL(*recv_socket, RecvFromInternal(_, _, _, _))
+        ON_CALL(*socket, RecvFromInternal(_, _, _, _))
             .WillByDefault(Return(-1));
 
-        socket_pairs->push_back(
-            std::make_pair(std::move(send_socket), std::move(recv_socket)));
+        sockets->push_back(std::move(socket));
       };
-  EXPECT_CALL(failing_socket_factory_, CreateSocketPairs(_))
+  EXPECT_CALL(failing_socket_factory_, CreateSockets(_))
       .WillOnce(Invoke(create_send_failing_socket));
   Reset(true /* use_failing_socket_factory */);
   const auto& addr = kPublicAddrs[0];
   std::string expected_announcement =
       CreateResolutionResponse(kDefaultTtl, {{"0.local", addr}});
-  // Mocked CreateSocketPairs above only creates one pair of sockets.
+  // Mocked CreateSockets above only creates one socket.
   EXPECT_CALL(failing_socket_factory_, OnSendTo(expected_announcement))
       .Times(kNumAnnouncementsPerInterface + kNumMaxRetriesPerResponse);
   const auto name = CreateNameForAddress(0, addr);
@@ -974,25 +968,22 @@
 // Test that if all socket handlers fail to read, the manager restarts itself.
 TEST_F(MdnsResponderTest, ManagerCanRestartAfterAllSocketHandlersFailToRead) {
   auto create_read_failing_socket =
-      [this](std::vector<net::MDnsSendRecvSocketPair>* socket_pairs) {
-        auto send_socket =
-            std::make_unique<NiceMock<net::MockMDnsDatagramClientSocket>>();
-        auto recv_socket =
+      [this](std::vector<std::unique_ptr<net::DatagramServerSocket>>* sockets) {
+        auto socket =
             std::make_unique<NiceMock<net::MockMDnsDatagramServerSocket>>(
                 net::ADDRESS_FAMILY_IPV4);
 
-        ON_CALL(*send_socket, WriteInternal(_, _, _)).WillByDefault(Return(0));
-        ON_CALL(*recv_socket, RecvFromInternal(_, _, _, _))
+        ON_CALL(*socket, SendToInternal(_, _, _)).WillByDefault(Return(0));
+        ON_CALL(*socket, RecvFromInternal(_, _, _, _))
             .WillByDefault(Invoke(&failing_socket_factory_,
                                   &MockFailingMdnsSocketFactory::FailToRecv));
 
-        socket_pairs->push_back(
-            std::make_pair(std::move(send_socket), std::move(recv_socket)));
+        sockets->push_back(std::move(socket));
       };
-  EXPECT_CALL(failing_socket_factory_, CreateSocketPairs(_))
+  EXPECT_CALL(failing_socket_factory_, CreateSockets(_))
       .WillOnce(Invoke(create_read_failing_socket));
   Reset(true /* use_failing_socket_factory */);
-  EXPECT_CALL(failing_socket_factory_, CreateSocketPairs(_)).Times(1);
+  EXPECT_CALL(failing_socket_factory_, CreateSockets(_)).Times(1);
   RunUntilNoTasksRemain();
 }
 
diff --git a/services/network/network_context_unittest.cc b/services/network/network_context_unittest.cc
index 7d637b4..ede6e0cc 100644
--- a/services/network/network_context_unittest.cc
+++ b/services/network/network_context_unittest.cc
@@ -50,6 +50,7 @@
 #include "net/base/ip_endpoint.h"
 #include "net/base/net_errors.h"
 #include "net/base/network_change_notifier.h"
+#include "net/base/proxy_server.h"
 #include "net/base/test_completion_callback.h"
 #include "net/cert/cert_verify_result.h"
 #include "net/cert/mock_cert_verifier.h"
@@ -294,9 +295,10 @@
     context->url_request_context()
         ->http_transaction_factory()
         ->GetSession()
-        ->GetTransportSocketPool(
-            net::HttpNetworkSession::SocketPoolType::NORMAL_SOCKET_POOL)
-        ->GetInfoAsValue("", "", false)
+        ->GetSocketPool(
+            net::HttpNetworkSession::SocketPoolType::NORMAL_SOCKET_POOL,
+            net::ProxyServer::Direct())
+        ->GetInfoAsValue("", "")
         ->GetInteger(name, &value);
     return value;
   }
@@ -307,9 +309,10 @@
         context->url_request_context()
             ->http_transaction_factory()
             ->GetSession()
-            ->GetTransportSocketPool(
-                net::HttpNetworkSession::SocketPoolType::NORMAL_SOCKET_POOL)
-            ->GetInfoAsValue("", "", false);
+            ->GetSocketPool(
+                net::HttpNetworkSession::SocketPoolType::NORMAL_SOCKET_POOL,
+                net::ProxyServer::Direct())
+            ->GetInfoAsValue("", "");
 
     int count = 0;
     base::Value* active_socket_count = pool_info->FindPathOfType(
diff --git a/services/network/network_service.cc b/services/network/network_service.cc
index e06cd1a..a5c461a7 100644
--- a/services/network/network_service.cc
+++ b/services/network/network_service.cc
@@ -499,8 +499,7 @@
   DCHECK(!http_auth_handler_factory_);
 
   http_auth_handler_factory_ = net::HttpAuthHandlerRegistryFactory::Create(
-      host_resolver_.get(), &http_auth_preferences_,
-      http_auth_static_params->supported_schemes
+      &http_auth_preferences_, http_auth_static_params->supported_schemes
 #if defined(OS_CHROMEOS)
       ,
       http_auth_static_params->allow_gssapi_library_load
@@ -659,7 +658,7 @@
 net::HttpAuthHandlerFactory* NetworkService::GetHttpAuthHandlerFactory() {
   if (!http_auth_handler_factory_) {
     http_auth_handler_factory_ = net::HttpAuthHandlerFactory::CreateDefault(
-        host_resolver_.get(), &http_auth_preferences_
+        &http_auth_preferences_
 #if defined(OS_ANDROID) && BUILDFLAG(USE_KERBEROS)
         ,
         base::BindRepeating(&CreateAuthSystem, this)
diff --git a/services/network/public/cpp/cookie_manager.typemap b/services/network/public/cpp/cookie_manager.typemap
index 079c275b..c176706 100644
--- a/services/network/public/cpp/cookie_manager.typemap
+++ b/services/network/public/cpp/cookie_manager.typemap
@@ -16,7 +16,7 @@
 type_mappings = [
   "network.mojom.CookiePriority=net::CookiePriority",
   "network.mojom.CookieSameSite=net::CookieSameSite",
-  "network.mojom.CookieSameSiteFilter=net::CookieOptions::SameSiteCookieMode",
+  "network.mojom.CookieSameSiteFilter=net::CookieOptions::SameSiteCookieContext",
   "network.mojom.CookieOptions=net::CookieOptions",
   "network.mojom.CanonicalCookie=net::CanonicalCookie",
 ]
diff --git a/services/network/public/cpp/cookie_manager_mojom_traits.cc b/services/network/public/cpp/cookie_manager_mojom_traits.cc
index b538a609..7ecc3dac 100644
--- a/services/network/public/cpp/cookie_manager_mojom_traits.cc
+++ b/services/network/public/cpp/cookie_manager_mojom_traits.cc
@@ -74,14 +74,14 @@
 
 network::mojom::CookieSameSiteFilter
 EnumTraits<network::mojom::CookieSameSiteFilter,
-           net::CookieOptions::SameSiteCookieMode>::
-    ToMojom(net::CookieOptions::SameSiteCookieMode input) {
+           net::CookieOptions::SameSiteCookieContext>::
+    ToMojom(net::CookieOptions::SameSiteCookieContext input) {
   switch (input) {
-    case net::CookieOptions::SameSiteCookieMode::INCLUDE_STRICT_AND_LAX:
+    case net::CookieOptions::SameSiteCookieContext::SAME_SITE_STRICT:
       return network::mojom::CookieSameSiteFilter::INCLUDE_STRICT_AND_LAX;
-    case net::CookieOptions::SameSiteCookieMode::INCLUDE_LAX:
+    case net::CookieOptions::SameSiteCookieContext::SAME_SITE_LAX:
       return network::mojom::CookieSameSiteFilter::INCLUDE_LAX;
-    case net::CookieOptions::SameSiteCookieMode::DO_NOT_INCLUDE:
+    case net::CookieOptions::SameSiteCookieContext::CROSS_SITE:
       return network::mojom::CookieSameSiteFilter::DO_NOT_INCLUDE;
   }
   NOTREACHED();
@@ -89,18 +89,18 @@
 }
 
 bool EnumTraits<network::mojom::CookieSameSiteFilter,
-                net::CookieOptions::SameSiteCookieMode>::
+                net::CookieOptions::SameSiteCookieContext>::
     FromMojom(network::mojom::CookieSameSiteFilter input,
-              net::CookieOptions::SameSiteCookieMode* output) {
+              net::CookieOptions::SameSiteCookieContext* output) {
   switch (input) {
     case network::mojom::CookieSameSiteFilter::INCLUDE_STRICT_AND_LAX:
-      *output = net::CookieOptions::SameSiteCookieMode::INCLUDE_STRICT_AND_LAX;
+      *output = net::CookieOptions::SameSiteCookieContext::SAME_SITE_STRICT;
       return true;
     case network::mojom::CookieSameSiteFilter::INCLUDE_LAX:
-      *output = net::CookieOptions::SameSiteCookieMode::INCLUDE_LAX;
+      *output = net::CookieOptions::SameSiteCookieContext::SAME_SITE_LAX;
       return true;
     case network::mojom::CookieSameSiteFilter::DO_NOT_INCLUDE:
-      *output = net::CookieOptions::SameSiteCookieMode::DO_NOT_INCLUDE;
+      *output = net::CookieOptions::SameSiteCookieContext::CROSS_SITE;
       return true;
   }
   return false;
@@ -114,10 +114,10 @@
   else
     cookie_options->set_include_httponly();
 
-  net::CookieOptions::SameSiteCookieMode same_site_cookie_mode;
-  if (!mojo_options.ReadCookieSameSiteFilter(&same_site_cookie_mode))
+  net::CookieOptions::SameSiteCookieContext same_site_cookie_context;
+  if (!mojo_options.ReadCookieSameSiteFilter(&same_site_cookie_context))
     return false;
-  cookie_options->set_same_site_cookie_mode(same_site_cookie_mode);
+  cookie_options->set_same_site_cookie_context(same_site_cookie_context);
 
   if (mojo_options.update_access_time())
     cookie_options->set_update_access_time();
diff --git a/services/network/public/cpp/cookie_manager_mojom_traits.h b/services/network/public/cpp/cookie_manager_mojom_traits.h
index fc413fde..dac85818 100644
--- a/services/network/public/cpp/cookie_manager_mojom_traits.h
+++ b/services/network/public/cpp/cookie_manager_mojom_traits.h
@@ -30,12 +30,12 @@
 
 template <>
 struct EnumTraits<network::mojom::CookieSameSiteFilter,
-                  net::CookieOptions::SameSiteCookieMode> {
+                  net::CookieOptions::SameSiteCookieContext> {
   static network::mojom::CookieSameSiteFilter ToMojom(
-      net::CookieOptions::SameSiteCookieMode input);
+      net::CookieOptions::SameSiteCookieContext input);
 
   static bool FromMojom(network::mojom::CookieSameSiteFilter input,
-                        net::CookieOptions::SameSiteCookieMode* output);
+                        net::CookieOptions::SameSiteCookieContext* output);
 };
 
 template <>
@@ -43,9 +43,9 @@
   static bool exclude_httponly(const net::CookieOptions& o) {
     return o.exclude_httponly();
   }
-  static net::CookieOptions::SameSiteCookieMode cookie_same_site_filter(
+  static net::CookieOptions::SameSiteCookieContext cookie_same_site_filter(
       const net::CookieOptions& o) {
-    return o.same_site_cookie_mode();
+    return o.same_site_cookie_context();
   }
   static bool update_access_time(const net::CookieOptions& o) {
     return o.update_access_time();
diff --git a/services/network/public/cpp/features.cc b/services/network/public/cpp/features.cc
index 6f81e483..72e96fb 100644
--- a/services/network/public/cpp/features.cc
+++ b/services/network/public/cpp/features.cc
@@ -20,7 +20,7 @@
 const base::Feature kNetworkService {
   "NetworkService",
 #if defined(OS_WIN) || defined(OS_MACOSX) || \
-    (defined(OS_LINUX) && !defined(OS_CHROMEOS) && !defined(IS_CHROMECAST))
+    (defined(OS_LINUX) && !defined(IS_CHROMECAST))
       base::FEATURE_ENABLED_BY_DEFAULT
 };
 #else
diff --git a/services/network/restricted_cookie_manager.cc b/services/network/restricted_cookie_manager.cc
index ffcef5a..f4d77b65 100644
--- a/services/network/restricted_cookie_manager.cc
+++ b/services/network/restricted_cookie_manager.cc
@@ -14,10 +14,10 @@
 #include "base/strings/string_util.h"
 #include "base/threading/sequenced_task_runner_handle.h"
 #include "mojo/public/cpp/bindings/message.h"
-#include "net/base/registry_controlled_domains/registry_controlled_domain.h"
 #include "net/cookies/cookie_constants.h"
 #include "net/cookies/cookie_options.h"
 #include "net/cookies/cookie_store.h"
+#include "net/cookies/cookie_util.h"
 #include "services/network/cookie_managers_shared.h"
 #include "services/network/cookie_settings.h"
 
@@ -118,18 +118,11 @@
     return;
   }
 
+  // TODO(https://crbug.com/925311): Wire initiator here.
   net::CookieOptions net_options;
-  if (net::registry_controlled_domains::SameDomainOrHost(
-          url, site_for_cookies,
-          net::registry_controlled_domains::INCLUDE_PRIVATE_REGISTRIES)) {
-    // TODO(mkwst): This check ought to further distinguish between frames
-    // initiated in a strict or lax same-site context.
-    net_options.set_same_site_cookie_mode(
-        net::CookieOptions::SameSiteCookieMode::INCLUDE_STRICT_AND_LAX);
-  } else {
-    net_options.set_same_site_cookie_mode(
-        net::CookieOptions::SameSiteCookieMode::DO_NOT_INCLUDE);
-  }
+  net_options.set_same_site_cookie_context(
+      net::cookie_util::ComputeSameSiteContext(url, site_for_cookies,
+                                               base::nullopt /*initiator*/));
 
   cookie_store_->GetCookieListWithOptionsAsync(
       url, net_options,
@@ -214,18 +207,11 @@
     return;
   }
 
+  // TODO(https://crbug.com/925311): Wire initiator here.
   net::CookieOptions net_options;
-  if (net::registry_controlled_domains::SameDomainOrHost(
-          url, site_for_cookies,
-          net::registry_controlled_domains::INCLUDE_PRIVATE_REGISTRIES)) {
-    // TODO(mkwst): This check ought to further distinguish between frames
-    // initiated in a strict or lax same-site context.
-    net_options.set_same_site_cookie_mode(
-        net::CookieOptions::SameSiteCookieMode::INCLUDE_STRICT_AND_LAX);
-  } else {
-    net_options.set_same_site_cookie_mode(
-        net::CookieOptions::SameSiteCookieMode::DO_NOT_INCLUDE);
-  }
+  net_options.set_same_site_cookie_context(
+      net::cookie_util::ComputeSameSiteContext(url, site_for_cookies,
+                                               base::nullopt /*initiator*/));
 
   auto listener = std::make_unique<Listener>(cookie_store_, url, net_options,
                                              std::move(mojo_listener));
diff --git a/services/network/tls_client_socket.cc b/services/network/tls_client_socket.cc
index 44c8e17..8fa33735 100644
--- a/services/network/tls_client_socket.cc
+++ b/services/network/tls_client_socket.cc
@@ -11,8 +11,8 @@
 #include "base/memory/ptr_util.h"
 #include "net/base/net_errors.h"
 #include "net/socket/client_socket_factory.h"
-#include "net/socket/client_socket_handle.h"
 #include "net/socket/ssl_client_socket.h"
+#include "net/socket/stream_socket.h"
 #include "net/ssl/ssl_config.h"
 #include "net/ssl/ssl_config_service.h"
 
@@ -29,7 +29,7 @@
 void TLSClientSocket::Connect(
     const net::HostPortPair& host_port_pair,
     const net::SSLConfig& ssl_config,
-    std::unique_ptr<net::ClientSocketHandle> tcp_socket,
+    std::unique_ptr<net::StreamSocket> tcp_socket,
     const net::SSLClientSocketContext& ssl_client_socket_context,
     net::ClientSocketFactory* socket_factory,
     mojom::TCPConnectedSocket::UpgradeToTLSCallback callback,
diff --git a/services/network/tls_client_socket.h b/services/network/tls_client_socket.h
index 898c1e5..16f5f0f 100644
--- a/services/network/tls_client_socket.h
+++ b/services/network/tls_client_socket.h
@@ -19,9 +19,9 @@
 #include "services/network/socket_data_pump.h"
 
 namespace net {
-class SSLClientSocket;
-class ClientSocketHandle;
 class ClientSocketFactory;
+class SSLClientSocket;
+class StreamSocket;
 }  // namespace net
 
 namespace network {
@@ -37,7 +37,7 @@
 
   void Connect(const net::HostPortPair& host_port_pair,
                const net::SSLConfig& ssl_config,
-               std::unique_ptr<net::ClientSocketHandle> tcp_socket,
+               std::unique_ptr<net::StreamSocket> tcp_socket,
                const net::SSLClientSocketContext& ssl_client_socket_context,
                net::ClientSocketFactory* socket_factory,
                mojom::TCPConnectedSocket::UpgradeToTLSCallback callback,
diff --git a/services/network/tls_socket_factory.cc b/services/network/tls_socket_factory.cc
index 2f9ffc19..c3cb491 100644
--- a/services/network/tls_socket_factory.cc
+++ b/services/network/tls_socket_factory.cc
@@ -16,7 +16,7 @@
 #include "net/cert/ct_policy_enforcer.h"
 #include "net/cert/multi_log_ct_verifier.h"
 #include "net/socket/client_socket_factory.h"
-#include "net/socket/client_socket_handle.h"
+#include "net/socket/stream_socket.h"
 #include "net/ssl/ssl_config.h"
 #include "net/ssl/ssl_config_service.h"
 #include "net/url_request/url_request_context.h"
@@ -86,11 +86,9 @@
         mojo::ScopedDataPipeProducerHandle(), base::nullopt);
     return;
   }
-  auto socket_handle = std::make_unique<net::ClientSocketHandle>();
-  socket_handle->SetSocket(socket_delegate->TakeSocket());
   CreateTLSClientSocket(
       host_port_pair, std::move(socket_options), std::move(request),
-      std::move(socket_handle), std::move(observer),
+      socket_delegate->TakeSocket(), std::move(observer),
       static_cast<net::NetworkTrafficAnnotationTag>(traffic_annotation),
       std::move(callback));
 }
@@ -99,7 +97,7 @@
     const net::HostPortPair& host_port_pair,
     mojom::TLSClientSocketOptionsPtr socket_options,
     mojom::TLSClientSocketRequest request,
-    std::unique_ptr<net::ClientSocketHandle> underlying_socket,
+    std::unique_ptr<net::StreamSocket> underlying_socket,
     mojom::SocketObserverPtr observer,
     const net::NetworkTrafficAnnotationTag& traffic_annotation,
     mojom::TCPConnectedSocket::UpgradeToTLSCallback callback) {
diff --git a/services/network/tls_socket_factory.h b/services/network/tls_socket_factory.h
index 76509df..ae997a9 100644
--- a/services/network/tls_socket_factory.h
+++ b/services/network/tls_socket_factory.h
@@ -18,9 +18,9 @@
 #include "services/network/public/mojom/tls_socket.mojom.h"
 
 namespace net {
-class ClientSocketHandle;
 class ClientSocketFactory;
 class SSLConfigService;
+class StreamSocket;
 }  // namespace net
 
 namespace network {
@@ -69,7 +69,7 @@
       const net::HostPortPair& host_port_pair,
       mojom::TLSClientSocketOptionsPtr socket_options,
       mojom::TLSClientSocketRequest request,
-      std::unique_ptr<net::ClientSocketHandle> socket,
+      std::unique_ptr<net::StreamSocket> underlying_socket,
       mojom::SocketObserverPtr observer,
       const net::NetworkTrafficAnnotationTag& traffic_annotation,
       mojom::TCPConnectedSocket::UpgradeToTLSCallback callback);
diff --git a/services/resource_coordinator/memory_instrumentation/queued_request_dispatcher.cc b/services/resource_coordinator/memory_instrumentation/queued_request_dispatcher.cc
index c66e9fa9..991bef52 100644
--- a/services/resource_coordinator/memory_instrumentation/queued_request_dispatcher.cc
+++ b/services/resource_coordinator/memory_instrumentation/queued_request_dispatcher.cc
@@ -6,10 +6,12 @@
 
 #include <inttypes.h>
 
+#include "base/android/library_loader/anchor_functions_buildflags.h"
 #include "base/bind.h"
 #include "base/command_line.h"
 #include "base/logging.h"
 #include "base/metrics/histogram_macros.h"
+#include "base/process/process_metrics.h"
 #include "base/strings/pattern.h"
 #include "base/strings/stringprintf.h"
 #include "base/trace_event/trace_event.h"
@@ -68,6 +70,40 @@
 #endif
 }
 
+#if BUILDFLAG(SUPPORTS_CODE_ORDERING)
+size_t ReportGlobalNativeCodeResidentMemoryKb(
+    const std::map<base::ProcessId, mojom::RawOSMemDump*>& pid_to_pmd) {
+  std::vector<uint8_t> common_map;
+
+  for (const auto& pmd : pid_to_pmd) {
+    if (!pmd.second || pmd.second->native_library_pages_bitmap.empty()) {
+      DLOG(WARNING) << "No process pagemap entry for " << pmd.first;
+      return 0;
+    }
+
+    if (common_map.size() < pmd.second->native_library_pages_bitmap.size()) {
+      common_map.resize(pmd.second->native_library_pages_bitmap.size());
+    }
+    for (size_t i = 0; i < pmd.second->native_library_pages_bitmap.size();
+         ++i) {
+      common_map[i] |= pmd.second->native_library_pages_bitmap[i];
+    }
+  }
+
+  // |accessed_pages_set| will be ~40kB on 32 bit mode and ~80kB on 64 bit mode.
+  std::set<size_t> accessed_pages_set;
+  for (size_t i = 0; i < common_map.size(); i++) {
+    for (int j = 0; j < 8; j++) {
+      if (common_map[i] & (1 << j))
+        accessed_pages_set.insert(i * 8 + j);
+    }
+  }
+
+  const size_t kPageSize = base::GetPageSize();
+  return accessed_pages_set.size() * kPageSize / 1024;
+}
+#endif  // #if BUILDFLAG(SUPPORTS_CODE_ORDERING)
+
 memory_instrumentation::mojom::OSMemDumpPtr CreatePublicOSDump(
     const mojom::RawOSMemDump& internal_os_dump,
     uint32_t shared_resident_kb) {
@@ -443,6 +479,7 @@
   mojom::GlobalMemoryDumpPtr global_dump(mojom::GlobalMemoryDump::New());
   global_dump->start_time = request->start_time;
   global_dump->process_dumps.reserve(request->responses.size());
+  global_dump->aggregated_metrics = mojom::AggregatedMetrics::New();
   for (const auto& response : request->responses) {
     base::ProcessId pid = response.second.process_id;
 
@@ -546,6 +583,13 @@
     global_dump->process_dumps.push_back(std::move(pmd));
   }
 
+#if BUILDFLAG(SUPPORTS_CODE_ORDERING)
+  size_t native_resident_kb =
+      ReportGlobalNativeCodeResidentMemoryKb(pid_to_os_dump);
+  global_dump->aggregated_metrics->native_library_resident_kb =
+      native_resident_kb;
+#endif  // BUILDFLAG(SUPPORTS_CODE_ORDERING)
+
   const bool global_success = request->failed_memory_dump_count == 0;
 
   // In the single process-case, we want to ensure that global_success
diff --git a/services/resource_coordinator/public/cpp/memory_instrumentation/global_memory_dump.cc b/services/resource_coordinator/public/cpp/memory_instrumentation/global_memory_dump.cc
index c56e2fe..96e025b3 100644
--- a/services/resource_coordinator/public/cpp/memory_instrumentation/global_memory_dump.cc
+++ b/services/resource_coordinator/public/cpp/memory_instrumentation/global_memory_dump.cc
@@ -4,10 +4,14 @@
 
 #include "services/resource_coordinator/public/cpp/memory_instrumentation/global_memory_dump.h"
 
+#include <vector>
+
 namespace memory_instrumentation {
 
 GlobalMemoryDump::GlobalMemoryDump(
-    std::vector<mojom::ProcessMemoryDumpPtr> process_dumps) {
+    std::vector<mojom::ProcessMemoryDumpPtr> process_dumps,
+    mojom::AggregatedMetricsPtr aggregated_metrics)
+    : aggregated_metrics_(std::move(aggregated_metrics)) {
   auto it = process_dumps_.before_begin();
   for (mojom::ProcessMemoryDumpPtr& process_dump : process_dumps) {
     it = process_dumps_.emplace_after(it, std::move(process_dump));
@@ -18,7 +22,8 @@
 std::unique_ptr<GlobalMemoryDump> GlobalMemoryDump::MoveFrom(
     mojom::GlobalMemoryDumpPtr ptr) {
   return ptr ? std::unique_ptr<GlobalMemoryDump>(
-                   new GlobalMemoryDump(std::move(ptr->process_dumps)))
+                   new GlobalMemoryDump(std::move(ptr->process_dumps),
+                                        std::move(ptr->aggregated_metrics)))
              : nullptr;
 }
 
@@ -41,4 +46,10 @@
   return base::Optional<uint64_t>(metric_it->second);
 }
 
+GlobalMemoryDump::AggregatedMetrics::AggregatedMetrics(
+    mojom::AggregatedMetricsPtr aggregated_metrics)
+    : aggregated_metrics_(std::move(aggregated_metrics)) {}
+
+GlobalMemoryDump::AggregatedMetrics::~AggregatedMetrics() = default;
+
 }  // namespace memory_instrumentation
diff --git a/services/resource_coordinator/public/cpp/memory_instrumentation/global_memory_dump.h b/services/resource_coordinator/public/cpp/memory_instrumentation/global_memory_dump.h
index 6f5cfdc..a9c4754 100644
--- a/services/resource_coordinator/public/cpp/memory_instrumentation/global_memory_dump.h
+++ b/services/resource_coordinator/public/cpp/memory_instrumentation/global_memory_dump.h
@@ -5,6 +5,9 @@
 #ifndef SERVICES_RESOURCE_COORDINATOR_PUBLIC_CPP_MEMORY_INSTRUMENTATION_GLOBAL_MEMORY_DUMP_H_
 #define SERVICES_RESOURCE_COORDINATOR_PUBLIC_CPP_MEMORY_INSTRUMENTATION_GLOBAL_MEMORY_DUMP_H_
 
+#include <utility>
+#include <vector>
+
 #include "base/component_export.h"
 #include "base/optional.h"
 #include "services/resource_coordinator/public/mojom/memory_instrumentation/memory_instrumentation.mojom.h"
@@ -39,6 +42,25 @@
     DISALLOW_COPY_AND_ASSIGN(ProcessDump);
   };
 
+ public:
+  class COMPONENT_EXPORT(RESOURCE_COORDINATOR_PUBLIC_MEMORY_INSTRUMENTATION)
+      AggregatedMetrics {
+   public:
+    AggregatedMetrics(mojom::AggregatedMetricsPtr aggregated_metrics);
+    ~AggregatedMetrics();
+
+    size_t native_library_resident_kb() const {
+      if (!aggregated_metrics_)
+        return 0;
+      return aggregated_metrics_->native_library_resident_kb;
+    }
+
+   private:
+    mojom::AggregatedMetricsPtr aggregated_metrics_;
+
+    DISALLOW_COPY_AND_ASSIGN(AggregatedMetrics);
+  };
+
   ~GlobalMemoryDump();
 
   // Creates an owned instance of this class wrapping the given mojo struct.
@@ -49,10 +71,14 @@
     return process_dumps_;
   }
 
+  const AggregatedMetrics& aggregated_metrics() { return aggregated_metrics_; }
+
  private:
-  GlobalMemoryDump(std::vector<mojom::ProcessMemoryDumpPtr> process_dumps);
+  GlobalMemoryDump(std::vector<mojom::ProcessMemoryDumpPtr> process_dumps,
+                   mojom::AggregatedMetricsPtr aggregated_metrics);
 
   std::forward_list<ProcessDump> process_dumps_;
+  AggregatedMetrics aggregated_metrics_;
 
   DISALLOW_COPY_AND_ASSIGN(GlobalMemoryDump);
 };
diff --git a/services/resource_coordinator/public/cpp/memory_instrumentation/os_metrics.h b/services/resource_coordinator/public/cpp/memory_instrumentation/os_metrics.h
index 01e0f18..c4c2b00 100644
--- a/services/resource_coordinator/public/cpp/memory_instrumentation/os_metrics.h
+++ b/services/resource_coordinator/public/cpp/memory_instrumentation/os_metrics.h
@@ -4,6 +4,8 @@
 #ifndef SERVICES_RESOURCE_COORDINATOR_PUBLIC_CPP_MEMORY_INSTRUMENTATION_OS_METRICS_H_
 #define SERVICES_RESOURCE_COORDINATOR_PUBLIC_CPP_MEMORY_INSTRUMENTATION_OS_METRICS_H_
 
+#include <vector>
+
 #include "base/component_export.h"
 #include "base/gtest_prod_util.h"
 #include "base/process/process_handle.h"
@@ -34,6 +36,7 @@
   FRIEND_TEST_ALL_PREFIXES(OSMetricsTest, ParseProcSmaps);
   FRIEND_TEST_ALL_PREFIXES(OSMetricsTest, TestWinModuleReading);
   FRIEND_TEST_ALL_PREFIXES(OSMetricsTest, TestMachOReading);
+  FRIEND_TEST_ALL_PREFIXES(OSMetricsTest, GetMappedAndResidentPages);
   FRIEND_TEST_ALL_PREFIXES(heap_profiling::ProfilingJsonExporterTest,
                            MemoryMaps);
 
@@ -41,6 +44,28 @@
   static std::vector<mojom::VmRegionPtr> GetProcessModules(base::ProcessId);
 #endif
 
+#if defined(OS_LINUX) || defined(OS_ANDROID)
+  // Provides information on the dump state of resident pages.
+  enum class MappedAndResidentPagesDumpState {
+    // Access to /proc/<pid>/pagemap can be denied for android devices running
+    // a kernel version < 4.4.
+    kAccessPagemapDenied,
+    kFailure,
+    kSuccess
+  };
+
+  // Depends on /proc/self/pagemap to determine mapped and resident pages
+  // within bounds (start_address inclusive and end_address exclusive).
+  // It does not use mincore() because it only checks to see
+  // if the page is in the cache and up to date.
+  // mincore() has no guarantee a page has been mapped by the current process.
+  // Guaranteed to work on Android.
+  static MappedAndResidentPagesDumpState GetMappedAndResidentPages(
+      const size_t start_address,
+      const size_t end_address,
+      std::vector<uint8_t>* accessed_pages_bitmap);
+
+#endif  // defined(OS_LINUX) || defined(OS_ANDROID)
 };
 
 }  // namespace memory_instrumentation
diff --git a/services/resource_coordinator/public/cpp/memory_instrumentation/os_metrics_linux.cc b/services/resource_coordinator/public/cpp/memory_instrumentation/os_metrics_linux.cc
index 4b104adf..a5b0773 100644
--- a/services/resource_coordinator/public/cpp/memory_instrumentation/os_metrics_linux.cc
+++ b/services/resource_coordinator/public/cpp/memory_instrumentation/os_metrics_linux.cc
@@ -7,6 +7,8 @@
 #include <stdint.h>
 #include <memory>
 
+#include "base/android/library_loader/anchor_functions.h"
+#include "base/android/library_loader/anchor_functions_buildflags.h"
 #include "base/debug/elf_reader.h"
 #include "base/files/file_util.h"
 #include "base/files/scoped_file.h"
@@ -242,6 +244,28 @@
   dump->platform_private_footprint->vm_swap_bytes = vm_swap_bytes;
   dump->resident_set_kb = process_metrics->GetResidentSetSize() / 1024;
 
+#if defined(OS_ANDROID)
+#if BUILDFLAG(SUPPORTS_CODE_ORDERING)
+  if (!base::android::AreAnchorsSane()) {
+    DLOG(WARNING) << "Incorrect code ordering";
+    return false;
+  }
+
+  std::vector<uint8_t> accessed_pages_bitmap;
+  OSMetrics::MappedAndResidentPagesDumpState state =
+      OSMetrics::GetMappedAndResidentPages(base::android::kStartOfText,
+                                           base::android::kEndOfText,
+                                           &accessed_pages_bitmap);
+
+  // MappedAndResidentPagesDumpState |state| can be |kAccessPagemapDenied|
+  // for Android devices running a kernel version < 4.4.
+  if (state != OSMetrics::MappedAndResidentPagesDumpState::kSuccess)
+    return state != OSMetrics::MappedAndResidentPagesDumpState::kFailure;
+
+  dump->native_library_pages_bitmap = std::move(accessed_pages_bitmap);
+#endif  // BUILDFLAG(SUPPORTS_CODE_ORDERING)
+#endif  //  defined(OS_ANDROID)
+
   return true;
 }
 
@@ -266,4 +290,52 @@
   return maps;
 }
 
+// static
+OSMetrics::MappedAndResidentPagesDumpState OSMetrics::GetMappedAndResidentPages(
+    const size_t start_address,
+    const size_t end_address,
+    std::vector<uint8_t>* accessed_pages_bitmap) {
+  const char* kPagemap = "/proc/self/pagemap";
+
+  base::ScopedFILE pagemap_file(fopen(kPagemap, "r"));
+  if (!pagemap_file.get()) {
+    DLOG(WARNING) << "Could not open " << kPagemap;
+    return OSMetrics::MappedAndResidentPagesDumpState::kAccessPagemapDenied;
+  }
+
+  const size_t kPageSize = base::GetPageSize();
+  const size_t start_page = start_address / kPageSize;
+  // |end_address| is exclusive.
+  const size_t end_page = (end_address - 1) / kPageSize;
+  const size_t total_pages = end_page - start_page + 1;
+
+  // The pagemap has one 64 bit entry per page or 8 bytes.
+  auto offset = static_cast<long>(start_page * 8);
+  if (fseek(pagemap_file.get(), offset, SEEK_SET) != 0) {
+    DLOG(ERROR) << "Error in fseek " << kPagemap;
+    return OSMetrics::MappedAndResidentPagesDumpState::kFailure;
+  }
+
+  // |entries| will be 2kB/MB (if |kPageSize| = 4096),
+  // that would only be ~80kB on Android, and up to 200kB on Linux (for 100MB)
+  std::vector<uint64_t> entries(total_pages);
+  if (fread(&entries[0], sizeof(uint64_t), total_pages, pagemap_file.get()) !=
+      total_pages) {
+    return OSMetrics::MappedAndResidentPagesDumpState::kFailure;
+  }
+
+  accessed_pages_bitmap->resize(1 + (total_pages - 1) / 8);
+  for (size_t page = 0; page < total_pages; page++) {
+    // Bit 63 is "page present" according to
+    // https://www.kernel.org/doc/Documentation/vm/pagemap.txt.
+    if (entries[page] & (1LL << 63)) {
+      auto byte = page / 8;
+      auto bit = page & 0x7;
+      CHECK_LT(byte, accessed_pages_bitmap->size());
+      (*accessed_pages_bitmap)[byte] |= 1 << bit;
+    }
+  }
+  return OSMetrics::MappedAndResidentPagesDumpState::kSuccess;
+}
+
 }  // namespace memory_instrumentation
diff --git a/services/resource_coordinator/public/cpp/memory_instrumentation/os_metrics_unittest.cc b/services/resource_coordinator/public/cpp/memory_instrumentation/os_metrics_unittest.cc
index 2eb789b..84865e8 100644
--- a/services/resource_coordinator/public/cpp/memory_instrumentation/os_metrics_unittest.cc
+++ b/services/resource_coordinator/public/cpp/memory_instrumentation/os_metrics_unittest.cc
@@ -3,8 +3,12 @@
 // found in the LICENSE file.
 #include "services/resource_coordinator/public/cpp/memory_instrumentation/os_metrics.h"
 
+#include <set>
+#include <vector>
+
 #include "base/files/file_util.h"
 #include "base/process/process_handle.h"
+#include "base/process/process_metrics.h"
 #include "build/build_config.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
@@ -18,6 +22,10 @@
 #include <windows.h>
 #endif
 
+#if defined(OS_LINUX) || defined(OS_ANDROID)
+#include <sys/mman.h>
+#endif
+
 namespace memory_instrumentation {
 
 #if defined(OS_LINUX) || defined(OS_ANDROID)
@@ -197,6 +205,51 @@
   EXPECT_EQ(4 * 1024UL, maps_2[0]->byte_stats_private_dirty_resident);
   EXPECT_EQ(0 * 1024UL, maps_2[0]->byte_stats_swapped);
 }
+
+TEST(OSMetricsTest, GetMappedAndResidentPages) {
+  const size_t kPages = 16;
+  const size_t kPageSize = base::GetPageSize();
+  const size_t kLength = kPages * kPageSize;
+
+  // mmap guarantees addr is aligned with kPagesize.
+  void* addr = mmap(NULL, kLength, PROT_READ | PROT_WRITE,
+                    MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
+
+  ASSERT_NE(MAP_FAILED, addr) << "mmap() failed";
+
+  std::set<size_t> pages;
+  uint8_t* array = static_cast<uint8_t*>(addr);
+  for (unsigned int i = 0; i < kPages / 2; ++i) {
+    int page = rand() % kPages;
+    int offset = rand() % kPageSize;
+    *static_cast<volatile uint8_t*>(array + page * kPageSize + offset) =
+        rand() % 256;
+    pages.insert(page);
+  }
+
+  size_t start_address = reinterpret_cast<size_t>(addr);
+
+  std::vector<uint8_t> accessed_pages_bitmap;
+  OSMetrics::MappedAndResidentPagesDumpState state =
+      OSMetrics::GetMappedAndResidentPages(
+          start_address, start_address + kLength, &accessed_pages_bitmap);
+
+  ASSERT_EQ(munmap(addr, kLength), 0);
+  if (state == OSMetrics::MappedAndResidentPagesDumpState::kAccessPagemapDenied)
+    return;
+
+  EXPECT_EQ(state == OSMetrics::MappedAndResidentPagesDumpState::kSuccess,
+            true);
+  std::set<size_t> accessed_pages_set;
+  for (size_t i = 0; i < accessed_pages_bitmap.size(); i++) {
+    for (int j = 0; j < 8; j++)
+      if (accessed_pages_bitmap[i] & (1 << j))
+        accessed_pages_set.insert(i * 8 + j);
+  }
+
+  EXPECT_EQ(pages == accessed_pages_set, true);
+}
+
 #endif  // defined(OS_LINUX) || defined(OS_ANDROID)
 
 #if defined(OS_WIN)
diff --git a/services/resource_coordinator/public/mojom/memory_instrumentation/memory_instrumentation.mojom b/services/resource_coordinator/public/mojom/memory_instrumentation/memory_instrumentation.mojom
index 9826b15..81f39ad 100644
--- a/services/resource_coordinator/public/mojom/memory_instrumentation/memory_instrumentation.mojom
+++ b/services/resource_coordinator/public/mojom/memory_instrumentation/memory_instrumentation.mojom
@@ -140,6 +140,10 @@
   uint32 resident_set_kb = 0;
   PlatformPrivateFootprint platform_private_footprint;
   array<VmRegion> memory_maps;
+
+  // Each bit corresponds to a page. The bit is set if the page
+  // is mapped and resident in memory.
+  array<uint8> native_library_pages_bitmap;
 };
 
 // These structs are public:
@@ -187,6 +191,10 @@
   mojo_base.mojom.ProcessId pid;
 };
 
+// Metrics aggregated across all processes.
+struct AggregatedMetrics {
+  uint32 native_library_resident_kb = 0;
+};
 
 // This struct is returned by the public-facing API
 // Coordinator::RequestGlobalMemoryDump().
@@ -196,6 +204,7 @@
   // start time of a global dump may lag the request time substantially.
   mojo_base.mojom.TimeTicks start_time;
   array<ProcessMemoryDump> process_dumps;
+  AggregatedMetrics aggregated_metrics;
 };
 
 // This is the interface implemented by the per-process client library. This
diff --git a/services/resource_coordinator/public/mojom/signals.mojom b/services/resource_coordinator/public/mojom/signals.mojom
index 8bf62dd3..9d3fdfa 100644
--- a/services/resource_coordinator/public/mojom/signals.mojom
+++ b/services/resource_coordinator/public/mojom/signals.mojom
@@ -33,7 +33,5 @@
   kPID,
   kVisible,
   kUKMSourceId,
-  // Used by Page CUs to store current loading state.
-  kIsLoading,
   kLifecycleState,
 };
diff --git a/services/service_manager/sandbox/mac/common.sb b/services/service_manager/sandbox/mac/common.sb
index 27ebafc..579d9de 100644
--- a/services/service_manager/sandbox/mac/common.sb
+++ b/services/service_manager/sandbox/mac/common.sb
@@ -94,7 +94,7 @@
                               (param browser-pid))))
 
 (allow mach-lookup
-  (browser-service-name "FieldTrialMemoryServer")
+  (browser-service-name "MachPortRendezvousServer")
   (browser-service-name "rohitfork")
 )
 
diff --git a/services/service_manager/sandbox/mac/sandbox_mac.mm b/services/service_manager/sandbox/mac/sandbox_mac.mm
index d3b9305..fe999ab 100644
--- a/services/service_manager/sandbox/mac/sandbox_mac.mm
+++ b/services/service_manager/sandbox/mac/sandbox_mac.mm
@@ -24,10 +24,10 @@
 #include "base/mac/bundle_locations.h"
 #include "base/mac/foundation_util.h"
 #include "base/mac/mac_util.h"
+#include "base/mac/mach_port_rendezvous.h"
 #include "base/mac/scoped_cftyperef.h"
 #include "base/mac/scoped_nsautorelease_pool.h"
 #include "base/mac/scoped_nsobject.h"
-#include "base/metrics/field_trial_memory_mac.h"
 #include "base/rand_util.h"
 #include "base/stl_util.h"
 #include "base/strings/string16.h"
@@ -173,7 +173,7 @@
 
   if (!compiler.InsertStringParam(
           kSandboxFieldTrialSeverName,
-          base::FieldTrialMemoryClient::GetBootstrapName())) {
+          base::MachPortRendezvousClient::GetBootstrapName())) {
     return false;
   }
 
diff --git a/services/tracing/BUILD.gn b/services/tracing/BUILD.gn
index 0143fc8..07bcf20 100644
--- a/services/tracing/BUILD.gn
+++ b/services/tracing/BUILD.gn
@@ -13,6 +13,8 @@
     "agent_registry.h",
     "coordinator.cc",
     "coordinator.h",
+    "perfetto/chrome_event_bundle_json_exporter.cc",
+    "perfetto/chrome_event_bundle_json_exporter.h",
     "perfetto/consumer_host.cc",
     "perfetto/consumer_host.h",
     "perfetto/json_trace_exporter.cc",
@@ -102,6 +104,7 @@
 
   if (is_mac || is_linux || is_android || is_fuchsia) {
     sources += [
+      "perfetto/chrome_event_bundle_json_exporter_unittest.cc",
       "perfetto/consumer_host_unittest.cc",
       "perfetto/json_trace_exporter_unittest.cc",
       "perfetto/perfetto_integration_unittest.cc",
diff --git a/services/tracing/perfetto/chrome_event_bundle_json_exporter.cc b/services/tracing/perfetto/chrome_event_bundle_json_exporter.cc
new file mode 100644
index 0000000..a4b30e4
--- /dev/null
+++ b/services/tracing/perfetto/chrome_event_bundle_json_exporter.cc
@@ -0,0 +1,257 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "services/tracing/perfetto/chrome_event_bundle_json_exporter.h"
+
+#include <utility>
+
+#include "base/json/string_escape.h"
+#include "base/trace_event/trace_event.h"
+#include "third_party/perfetto/protos/perfetto/trace/chrome/chrome_trace_event.pbzero.h"
+#include "third_party/perfetto/protos/perfetto/trace/chrome/chrome_trace_packet.pb.h"
+
+namespace tracing {
+namespace {
+using TraceEvent = ::base::trace_event::TraceEvent;
+const char* GetStringFromStringTable(
+    const std::unordered_map<int, std::string>& string_table,
+    int index) {
+  auto it = string_table.find(index);
+  DCHECK(it != string_table.end());
+
+  return it->second.c_str();
+}
+void AppendProtoArrayAsJSON(std::string* out,
+                            const perfetto::protos::ChromeTracedValue& array);
+
+void AppendProtoDictAsJSON(std::string* out,
+                           const perfetto::protos::ChromeTracedValue& dict);
+
+void AppendProtoValueAsJSON(std::string* out,
+                            const perfetto::protos::ChromeTracedValue& value) {
+  base::trace_event::TraceEvent::TraceValue json_value;
+  if (value.has_int_value()) {
+    json_value.as_int = value.int_value();
+    TraceEvent::AppendValueAsJSON(TRACE_VALUE_TYPE_INT, json_value, out);
+  } else if (value.has_double_value()) {
+    json_value.as_double = value.double_value();
+    TraceEvent::AppendValueAsJSON(TRACE_VALUE_TYPE_DOUBLE, json_value, out);
+  } else if (value.has_bool_value()) {
+    json_value.as_bool = value.bool_value();
+    TraceEvent::AppendValueAsJSON(TRACE_VALUE_TYPE_BOOL, json_value, out);
+  } else if (value.has_string_value()) {
+    json_value.as_string = value.string_value().c_str();
+    TraceEvent::AppendValueAsJSON(TRACE_VALUE_TYPE_STRING, json_value, out);
+  } else if (value.has_nested_type()) {
+    if (value.nested_type() == perfetto::protos::ChromeTracedValue::ARRAY) {
+      AppendProtoArrayAsJSON(out, value);
+      return;
+    } else if (value.nested_type() ==
+               perfetto::protos::ChromeTracedValue::DICT) {
+      AppendProtoDictAsJSON(out, value);
+    } else {
+      NOTREACHED();
+    }
+  } else {
+    NOTREACHED();
+  }
+}
+
+void AppendProtoArrayAsJSON(std::string* out,
+                            const perfetto::protos::ChromeTracedValue& array) {
+  out->append("[");
+
+  bool is_first_entry = true;
+  for (auto& value : array.array_values()) {
+    if (!is_first_entry) {
+      out->append(",");
+    } else {
+      is_first_entry = false;
+    }
+
+    AppendProtoValueAsJSON(out, value);
+  }
+
+  out->append("]");
+}
+
+void AppendProtoDictAsJSON(std::string* out,
+                           const perfetto::protos::ChromeTracedValue& dict) {
+  out->append("{");
+
+  DCHECK_EQ(dict.dict_keys_size(), dict.dict_values_size());
+  for (int i = 0; i < dict.dict_keys_size(); ++i) {
+    if (i != 0) {
+      out->append(",");
+    }
+
+    base::EscapeJSONString(dict.dict_keys(i), true, out);
+    out->append(":");
+
+    AppendProtoValueAsJSON(out, dict.dict_values(i));
+  }
+
+  out->append("}");
+}
+
+void OutputJSONFromArgumentValue(
+    const perfetto::protos::ChromeTraceEvent::Arg& arg,
+    std::string* out) {
+  TraceEvent::TraceValue value;
+  if (arg.has_bool_value()) {
+    value.as_bool = arg.bool_value();
+    TraceEvent::AppendValueAsJSON(TRACE_VALUE_TYPE_BOOL, value, out);
+    return;
+  }
+
+  if (arg.has_uint_value()) {
+    value.as_uint = arg.uint_value();
+    TraceEvent::AppendValueAsJSON(TRACE_VALUE_TYPE_UINT, value, out);
+    return;
+  }
+
+  if (arg.has_int_value()) {
+    value.as_int = arg.int_value();
+    TraceEvent::AppendValueAsJSON(TRACE_VALUE_TYPE_INT, value, out);
+    return;
+  }
+
+  if (arg.has_double_value()) {
+    value.as_double = arg.double_value();
+    TraceEvent::AppendValueAsJSON(TRACE_VALUE_TYPE_DOUBLE, value, out);
+    return;
+  }
+
+  if (arg.has_pointer_value()) {
+    value.as_pointer = reinterpret_cast<void*>(arg.pointer_value());
+    TraceEvent::AppendValueAsJSON(TRACE_VALUE_TYPE_POINTER, value, out);
+    return;
+  }
+
+  if (arg.has_string_value()) {
+    std::string str = arg.string_value();
+    value.as_string = &str[0];
+    TraceEvent::AppendValueAsJSON(TRACE_VALUE_TYPE_STRING, value, out);
+    return;
+  }
+
+  if (arg.has_json_value()) {
+    *out += arg.json_value();
+    return;
+  }
+
+  if (arg.has_traced_value()) {
+    AppendProtoDictAsJSON(out, arg.traced_value());
+    return;
+  }
+
+  NOTREACHED();
+}
+}  // namespace
+
+ChromeEventBundleJsonExporter::ChromeEventBundleJsonExporter(
+    JSONTraceExporter::ArgumentFilterPredicate argument_filter_predicate,
+    JSONTraceExporter::OnTraceEventJSONCallback callback)
+    : JSONTraceExporter(std::move(argument_filter_predicate),
+                        std::move(callback)) {}
+
+void ChromeEventBundleJsonExporter::ProcessPackets(
+    const std::vector<perfetto::TracePacket>& packets) {
+  for (auto& encoded_packet : packets) {
+    perfetto::protos::ChromeTracePacket packet;
+    bool decoded = encoded_packet.Decode(&packet);
+    DCHECK(decoded);
+
+    if (packet.has_trace_stats()) {
+      SetTraceStatsMetadata(packet.trace_stats());
+      continue;
+    }
+
+    if (!packet.has_chrome_events()) {
+      continue;
+    }
+
+    auto& bundle = packet.chrome_events();
+
+    if (ShouldOutputTraceEvents()) {
+      std::unordered_map<int, std::string> string_table;
+      for (auto& string_table_entry : bundle.string_table()) {
+        string_table[string_table_entry.index()] = string_table_entry.value();
+      }
+
+      for (auto& event : bundle.trace_events()) {
+        const char* name =
+            event.has_name_index()
+                ? GetStringFromStringTable(string_table, event.name_index())
+                : event.name().c_str();
+        const char* category_group_name =
+            event.has_category_group_name_index()
+                ? GetStringFromStringTable(string_table,
+                                           event.category_group_name_index())
+                : event.category_group_name().c_str();
+
+        ConstructTraceEventJSONWithBuilder(
+            event, string_table,
+            AddTraceEvent(name, category_group_name, event.phase(),
+                          event.timestamp(), event.process_id(),
+                          event.thread_id()));
+      }
+    }
+
+    for (auto& metadata : bundle.metadata()) {
+      AddChromeMetadata(metadata);
+    }
+
+    for (auto& legacy_ftrace_output : bundle.legacy_ftrace_output()) {
+      AddLegacyFtrace(legacy_ftrace_output);
+    }
+
+    for (auto& legacy_json_trace : bundle.legacy_json_trace()) {
+      AddChromeLegacyJSONTrace(legacy_json_trace);
+    }
+  }
+}
+
+void ChromeEventBundleJsonExporter::ConstructTraceEventJSONWithBuilder(
+    const perfetto::protos::ChromeTraceEvent& event,
+    const std::unordered_map<int, std::string>& string_table,
+    JSONTraceExporter::ScopedJSONTraceEventAppender&& trace_event_builder) {
+  if (event.has_duration()) {
+    trace_event_builder.AddDuration(event.duration());
+  }
+
+  if (event.has_thread_duration()) {
+    trace_event_builder.AddThreadDuration(event.thread_duration());
+  }
+
+  if (event.has_thread_timestamp()) {
+    trace_event_builder.AddThreadTimestamp(event.thread_timestamp());
+  }
+
+  DCHECK((event.has_scope() && event.scope() != "") || !event.has_scope());
+  trace_event_builder.AddFlags(event.flags(),
+                               event.has_id()
+                                   ? base::Optional<uint64_t>(event.id())
+                                   : base::Optional<uint64_t>(),
+                               event.scope());
+
+  if (event.has_bind_id()) {
+    trace_event_builder.AddBindId(event.bind_id());
+  }
+  auto args_builder = trace_event_builder.BuildArgs();
+  for (const auto& arg : event.args()) {
+    const std::string& arg_name =
+        arg.has_name_index()
+            ? GetStringFromStringTable(string_table, arg.name_index())
+            : arg.name();
+
+    auto* maybe_arg = args_builder->MaybeAddArg(arg_name);
+    if (maybe_arg) {
+      OutputJSONFromArgumentValue(arg, maybe_arg->mutable_out());
+    }
+  }
+  // Do not add anything to |trace_event_builder| unless you destroy
+  // |args_builder|.
+}
+}  // namespace tracing
diff --git a/services/tracing/perfetto/chrome_event_bundle_json_exporter.h b/services/tracing/perfetto/chrome_event_bundle_json_exporter.h
new file mode 100644
index 0000000..db1cc3c
--- /dev/null
+++ b/services/tracing/perfetto/chrome_event_bundle_json_exporter.h
@@ -0,0 +1,52 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SERVICES_TRACING_PERFETTO_CHROME_EVENT_BUNDLE_JSON_EXPORTER_H_
+#define SERVICES_TRACING_PERFETTO_CHROME_EVENT_BUNDLE_JSON_EXPORTER_H_
+
+#include <string>
+#include <unordered_map>
+#include <vector>
+
+#include "base/macros.h"
+#include "services/tracing/perfetto/json_trace_exporter.h"
+#include "third_party/perfetto/include/perfetto/tracing/core/trace_packet.h"
+
+namespace perfetto {
+namespace protos {
+class ChromeTraceEvent;
+}
+}  // namespace perfetto
+
+namespace tracing {
+
+// Converts proto-encoded ChromeEventBundles into the legacy JSON trace format.
+// Conversion happens on-the-fly as new trace packets are received.
+class ChromeEventBundleJsonExporter : public JSONTraceExporter {
+ public:
+  ChromeEventBundleJsonExporter(
+      ArgumentFilterPredicate argument_filter_predicate,
+      OnTraceEventJSONCallback callback);
+  ~ChromeEventBundleJsonExporter() override = default;
+
+ protected:
+  // Assumes each packet can be decoded as a ChromeTracePacket. Only uses
+  // packets that have either |trace_stats| or |chrome_events| fields.
+  void ProcessPackets(
+      const std::vector<perfetto::TracePacket>& packets) override;
+
+ private:
+  // Takes |event| and constructs the output json through use of the
+  // |trace_event_builder|.
+  void ConstructTraceEventJSONWithBuilder(
+      const perfetto::protos::ChromeTraceEvent& event,
+      const std::unordered_map<int, std::string>& string_table,
+      ScopedJSONTraceEventAppender&& trace_event_builder);
+
+  DISALLOW_COPY_AND_ASSIGN(ChromeEventBundleJsonExporter);
+};
+
+}  // namespace tracing
+
+#endif  // SERVICES_TRACING_PERFETTO_CHROME_EVENT_BUNDLE_JSON_EXPORTER_H_
diff --git a/services/tracing/perfetto/chrome_event_bundle_json_exporter_unittest.cc b/services/tracing/perfetto/chrome_event_bundle_json_exporter_unittest.cc
new file mode 100644
index 0000000..395ee5f
--- /dev/null
+++ b/services/tracing/perfetto/chrome_event_bundle_json_exporter_unittest.cc
@@ -0,0 +1,739 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "services/tracing/perfetto/chrome_event_bundle_json_exporter.h"
+
+#include <memory>
+#include <tuple>
+#include <utility>
+
+#include "base/bind.h"
+#include "base/callback.h"
+#include "base/json/json_reader.h"
+#include "base/json/json_writer.h"
+#include "base/logging.h"
+#include "base/message_loop/message_loop.h"
+#include "base/run_loop.h"
+#include "base/strings/pattern.h"
+#include "base/test/trace_event_analyzer.h"
+#include "base/trace_event/trace_event.h"
+#include "base/values.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+#include "services/tracing/public/mojom/perfetto_service.mojom.h"
+#include "third_party/perfetto/include/perfetto/tracing/core/trace_config.h"
+#include "third_party/perfetto/include/perfetto/tracing/core/trace_packet.h"
+#include "third_party/perfetto/protos/perfetto/trace/chrome/chrome_trace_event.pbzero.h"
+#include "third_party/perfetto/protos/perfetto/trace/trace_packet.pb.h"
+
+using base::trace_event::TraceLog;
+
+namespace tracing {
+
+namespace {
+
+bool IsArgNameWhitelisted(const char* arg_name) {
+  return base::MatchPattern(arg_name, "granular_arg_whitelisted");
+}
+
+bool IsTraceEventArgsWhitelisted(
+    const char* category_group_name,
+    const char* event_name,
+    base::trace_event::ArgumentNameFilterPredicate* arg_filter) {
+  if (base::MatchPattern(category_group_name, "toplevel") &&
+      base::MatchPattern(event_name, "*")) {
+    return true;
+  }
+  if (base::MatchPattern(category_group_name, "benchmark") &&
+      base::MatchPattern(event_name, "granularly_whitelisted")) {
+    *arg_filter = base::BindRepeating(&IsArgNameWhitelisted);
+    return true;
+  }
+
+  return false;
+}
+
+}  // namespace
+
+class ChromeEventBundleJsonExporterTest : public testing::Test {
+ public:
+  void SetUp() override {
+    json_trace_exporter_.reset(new ChromeEventBundleJsonExporter(
+        JSONTraceExporter::ArgumentFilterPredicate(),
+        base::BindRepeating(
+            &ChromeEventBundleJsonExporterTest::OnTraceEventJSON,
+            base::Unretained(this))));
+  }
+
+  void TearDown() override { json_trace_exporter_.reset(); }
+
+  void EnableArgumentFilter() {
+    json_trace_exporter_->SetArgumentFilterForTesting(
+        base::BindRepeating(&IsTraceEventArgsWhitelisted));
+  }
+
+  void OnTraceEventJSON(const std::string& json,
+                        base::DictionaryValue* metadata,
+                        bool has_more) {
+    CHECK(!has_more);
+
+    unparsed_trace_data_ = json;
+    parsed_trace_data_ =
+        base::DictionaryValue::From(base::JSONReader::ReadDeprecated(json));
+    ASSERT_TRUE(parsed_trace_data_) << "Couldn't parse json: \n" << json;
+
+    // The TraceAnalyzer expects the raw trace output, without the
+    // wrapping root-node.
+    std::string raw_events;
+    auto* events_value = parsed_trace_data_->FindKey("traceEvents");
+    ASSERT_TRUE(events_value);
+    base::JSONWriter::Write(*events_value, &raw_events);
+
+    trace_analyzer_.reset(trace_analyzer::TraceAnalyzer::Create(raw_events));
+    EXPECT_TRUE(trace_analyzer_);
+  }
+
+  void SetTestPacketBasicData(
+      perfetto::protos::ChromeTraceEvent* new_trace_event) {
+    new_trace_event->set_name("foo_name");
+    new_trace_event->set_timestamp(42);
+    new_trace_event->set_flags(TRACE_EVENT_FLAG_HAS_GLOBAL_ID |
+                               TRACE_EVENT_FLAG_FLOW_OUT);
+
+    new_trace_event->set_process_id(2);
+    new_trace_event->set_thread_id(3);
+    new_trace_event->set_category_group_name("cat_name");
+    new_trace_event->set_phase(TRACE_EVENT_PHASE_COMPLETE);
+    new_trace_event->set_duration(4);
+    new_trace_event->set_thread_duration(5);
+    new_trace_event->set_thread_timestamp(6);
+    new_trace_event->set_id(7);
+    new_trace_event->set_bind_id(8);
+
+    std::string scope;
+    scope += TRACE_EVENT_SCOPE_NAME_GLOBAL;
+    new_trace_event->set_scope(scope);
+  }
+
+  void FinalizePacket(const perfetto::protos::TracePacket& trace_packet_proto) {
+    perfetto::TracePacket trace_packet;
+    std::string ser_buf = trace_packet_proto.SerializeAsString();
+    trace_packet.AddSlice(&ser_buf[0], ser_buf.size());
+
+    std::vector<perfetto::TracePacket> packets;
+    packets.emplace_back(std::move(trace_packet));
+
+    json_trace_exporter_->OnTraceData(std::move(packets), false);
+  }
+
+  const trace_analyzer::TraceEvent* ValidateAndGetBasicTestPacket() {
+    const trace_analyzer::TraceEvent* trace_event =
+        trace_analyzer_->FindFirstOf(
+            trace_analyzer::Query(trace_analyzer::Query::EVENT_NAME) ==
+            trace_analyzer::Query::String("foo_name"));
+    EXPECT_TRUE(trace_event);
+
+    EXPECT_EQ(2, trace_event->thread.process_id);
+    EXPECT_EQ(3, trace_event->thread.thread_id);
+    EXPECT_EQ(42, trace_event->timestamp);
+    EXPECT_EQ('X', trace_event->phase);
+    EXPECT_EQ("foo_name", trace_event->name);
+    EXPECT_EQ("cat_name", trace_event->category);
+    EXPECT_EQ(4, trace_event->duration);
+    EXPECT_EQ(5, trace_event->thread_duration);
+    EXPECT_EQ(6, trace_event->thread_timestamp);
+    EXPECT_EQ("g", trace_event->scope);
+    EXPECT_EQ("0x7", trace_event->global_id2);
+    EXPECT_EQ("0x8", trace_event->bind_id);
+    EXPECT_TRUE(trace_event->flow_out);
+
+    return trace_event;
+  }
+
+  trace_analyzer::TraceAnalyzer* trace_analyzer() {
+    return trace_analyzer_.get();
+  }
+
+  const base::DictionaryValue* parsed_trace_data() const {
+    return parsed_trace_data_.get();
+  }
+
+  std::string unparsed_trace_data_;
+
+ private:
+  std::unique_ptr<ChromeEventBundleJsonExporter> json_trace_exporter_;
+  std::unique_ptr<base::MessageLoop> message_loop_;
+  std::unique_ptr<trace_analyzer::TraceAnalyzer> trace_analyzer_;
+  std::unique_ptr<base::DictionaryValue> parsed_trace_data_;
+};
+
+TEST_F(ChromeEventBundleJsonExporterTest, TestMetadata) {
+  perfetto::protos::TracePacket trace_packet_proto;
+
+  {
+    auto* new_metadata =
+        trace_packet_proto.mutable_chrome_events()->add_metadata();
+    new_metadata->set_name("int_metadata");
+    new_metadata->set_int_value(42);
+  }
+
+  {
+    auto* new_metadata =
+        trace_packet_proto.mutable_chrome_events()->add_metadata();
+    new_metadata->set_name("string_metadata");
+    new_metadata->set_string_value("met_val");
+  }
+
+  {
+    auto* new_metadata =
+        trace_packet_proto.mutable_chrome_events()->add_metadata();
+    new_metadata->set_name("bool_metadata");
+    new_metadata->set_bool_value(true);
+  }
+
+  {
+    auto* new_metadata =
+        trace_packet_proto.mutable_chrome_events()->add_metadata();
+    new_metadata->set_name("dict_metadata");
+    new_metadata->set_json_value("{\"child_dict\": \"foo\"}");
+  }
+
+  FinalizePacket(trace_packet_proto);
+
+  auto* metadata = parsed_trace_data()->FindKey("metadata");
+  EXPECT_TRUE(metadata);
+  EXPECT_EQ(metadata->FindKey("int_metadata")->GetInt(), 42);
+  EXPECT_EQ(metadata->FindKey("string_metadata")->GetString(), "met_val");
+  EXPECT_EQ(metadata->FindKey("bool_metadata")->GetBool(), true);
+  EXPECT_EQ(
+      metadata->FindKey("dict_metadata")->FindKey("child_dict")->GetString(),
+      "foo");
+}
+
+TEST_F(ChromeEventBundleJsonExporterTest, TestBasicEvent) {
+  perfetto::protos::TracePacket trace_packet_proto;
+  auto* new_trace_event =
+      trace_packet_proto.mutable_chrome_events()->add_trace_events();
+  SetTestPacketBasicData(new_trace_event);
+  FinalizePacket(trace_packet_proto);
+
+  ValidateAndGetBasicTestPacket();
+}
+
+TEST_F(ChromeEventBundleJsonExporterTest, TestStringTable) {
+  perfetto::protos::TracePacket trace_packet_proto;
+  auto* new_trace_event =
+      trace_packet_proto.mutable_chrome_events()->add_trace_events();
+
+  {
+    auto* string_table_entry =
+        trace_packet_proto.mutable_chrome_events()->add_string_table();
+    string_table_entry->set_index(1);
+    string_table_entry->set_value("foo_name");
+  }
+
+  {
+    auto* string_table_entry =
+        trace_packet_proto.mutable_chrome_events()->add_string_table();
+    string_table_entry->set_index(2);
+    string_table_entry->set_value("foo_cat");
+  }
+
+  {
+    auto* string_table_entry =
+        trace_packet_proto.mutable_chrome_events()->add_string_table();
+    string_table_entry->set_index(3);
+    string_table_entry->set_value("foo_arg");
+  }
+
+  new_trace_event->set_name_index(1);
+  new_trace_event->set_category_group_name_index(2);
+
+  auto* new_arg = new_trace_event->add_args();
+  new_arg->set_name_index(3);
+  new_arg->set_bool_value(true);
+
+  FinalizePacket(trace_packet_proto);
+
+  auto* trace_event = trace_analyzer()->FindFirstOf(
+      trace_analyzer::Query(trace_analyzer::Query::EVENT_NAME) ==
+      trace_analyzer::Query::String("foo_name"));
+  EXPECT_TRUE(trace_event);
+
+  EXPECT_EQ("foo_name", trace_event->name);
+  EXPECT_EQ("foo_cat", trace_event->category);
+
+  EXPECT_TRUE(trace_event->GetKnownArgAsBool("foo_arg"));
+}
+
+TEST_F(ChromeEventBundleJsonExporterTest, TestEventWithBoolArgs) {
+  perfetto::protos::TracePacket trace_packet_proto;
+  auto* new_trace_event =
+      trace_packet_proto.mutable_chrome_events()->add_trace_events();
+  SetTestPacketBasicData(new_trace_event);
+
+  {
+    auto* new_arg = new_trace_event->add_args();
+    new_arg->set_name("foo1");
+    new_arg->set_bool_value(true);
+  }
+
+  {
+    auto* new_arg = new_trace_event->add_args();
+    new_arg->set_name("foo2");
+    new_arg->set_bool_value(false);
+  }
+
+  FinalizePacket(trace_packet_proto);
+
+  auto* trace_event = ValidateAndGetBasicTestPacket();
+
+  EXPECT_TRUE(trace_event->GetKnownArgAsBool("foo1"));
+  EXPECT_FALSE(trace_event->GetKnownArgAsBool("foo2"));
+}
+
+TEST_F(ChromeEventBundleJsonExporterTest, TestEventWithUintArgs) {
+  perfetto::protos::TracePacket trace_packet_proto;
+  auto* new_trace_event =
+      trace_packet_proto.mutable_chrome_events()->add_trace_events();
+  SetTestPacketBasicData(new_trace_event);
+
+  {
+    auto* new_arg = new_trace_event->add_args();
+    new_arg->set_name("foo1");
+    new_arg->set_uint_value(1);
+  }
+
+  {
+    auto* new_arg = new_trace_event->add_args();
+    new_arg->set_name("foo2");
+    new_arg->set_uint_value(2);
+  }
+
+  FinalizePacket(trace_packet_proto);
+
+  auto* trace_event = ValidateAndGetBasicTestPacket();
+
+  EXPECT_EQ(1, trace_event->GetKnownArgAsDouble("foo1"));
+  EXPECT_EQ(2, trace_event->GetKnownArgAsDouble("foo2"));
+}
+
+TEST_F(ChromeEventBundleJsonExporterTest, TestEventWithIntArgs) {
+  perfetto::protos::TracePacket trace_packet_proto;
+  auto* new_trace_event =
+      trace_packet_proto.mutable_chrome_events()->add_trace_events();
+  SetTestPacketBasicData(new_trace_event);
+
+  {
+    auto* new_arg = new_trace_event->add_args();
+    new_arg->set_name("foo1");
+    new_arg->set_int_value(1);
+  }
+
+  {
+    auto* new_arg = new_trace_event->add_args();
+    new_arg->set_name("foo2");
+    new_arg->set_int_value(2);
+  }
+
+  FinalizePacket(trace_packet_proto);
+
+  auto* trace_event = ValidateAndGetBasicTestPacket();
+
+  EXPECT_EQ(1, trace_event->GetKnownArgAsDouble("foo1"));
+  EXPECT_EQ(2, trace_event->GetKnownArgAsDouble("foo2"));
+}
+
+TEST_F(ChromeEventBundleJsonExporterTest, TestEventWithDoubleArgs) {
+  perfetto::protos::TracePacket trace_packet_proto;
+  auto* new_trace_event =
+      trace_packet_proto.mutable_chrome_events()->add_trace_events();
+  SetTestPacketBasicData(new_trace_event);
+
+  {
+    auto* new_arg = new_trace_event->add_args();
+    new_arg->set_name("foo1");
+    new_arg->set_double_value(1.0);
+  }
+
+  {
+    auto* new_arg = new_trace_event->add_args();
+    new_arg->set_name("foo2");
+    new_arg->set_double_value(2.0);
+  }
+
+  FinalizePacket(trace_packet_proto);
+
+  auto* trace_event = ValidateAndGetBasicTestPacket();
+
+  EXPECT_EQ(1.0, trace_event->GetKnownArgAsDouble("foo1"));
+  EXPECT_EQ(2.0, trace_event->GetKnownArgAsDouble("foo2"));
+}
+
+TEST_F(ChromeEventBundleJsonExporterTest, TestEventWithStringArgs) {
+  perfetto::protos::TracePacket trace_packet_proto;
+  auto* new_trace_event =
+      trace_packet_proto.mutable_chrome_events()->add_trace_events();
+  SetTestPacketBasicData(new_trace_event);
+
+  {
+    auto* new_arg = new_trace_event->add_args();
+    new_arg->set_name("foo1");
+    new_arg->set_string_value("bar1");
+  }
+
+  {
+    auto* new_arg = new_trace_event->add_args();
+    new_arg->set_name("foo2");
+    new_arg->set_string_value("bar2");
+  }
+
+  FinalizePacket(trace_packet_proto);
+
+  auto* trace_event = ValidateAndGetBasicTestPacket();
+
+  EXPECT_EQ("bar1", trace_event->GetKnownArgAsString("foo1"));
+  EXPECT_EQ("bar2", trace_event->GetKnownArgAsString("foo2"));
+}
+
+TEST_F(ChromeEventBundleJsonExporterTest, TestEventWithPointerArgs) {
+  perfetto::protos::TracePacket trace_packet_proto;
+  auto* new_trace_event =
+      trace_packet_proto.mutable_chrome_events()->add_trace_events();
+  SetTestPacketBasicData(new_trace_event);
+
+  {
+    auto* new_arg = new_trace_event->add_args();
+    new_arg->set_name("foo1");
+    new_arg->set_pointer_value(0x1);
+  }
+
+  {
+    auto* new_arg = new_trace_event->add_args();
+    new_arg->set_name("foo2");
+    new_arg->set_pointer_value(0x2);
+  }
+
+  FinalizePacket(trace_packet_proto);
+
+  auto* trace_event = ValidateAndGetBasicTestPacket();
+
+  EXPECT_EQ("0x1", trace_event->GetKnownArgAsString("foo1"));
+  EXPECT_EQ("0x2", trace_event->GetKnownArgAsString("foo2"));
+}
+
+TEST_F(ChromeEventBundleJsonExporterTest, TestEventWithConvertableArgs) {
+  perfetto::protos::TracePacket trace_packet_proto;
+  auto* new_trace_event =
+      trace_packet_proto.mutable_chrome_events()->add_trace_events();
+  SetTestPacketBasicData(new_trace_event);
+
+  {
+    auto* new_arg = new_trace_event->add_args();
+    new_arg->set_name("foo1");
+    new_arg->set_json_value("\"conv_value1\"");
+  }
+
+  {
+    auto* new_arg = new_trace_event->add_args();
+    new_arg->set_name("foo2");
+    new_arg->set_json_value("\"conv_value2\"");
+  }
+
+  FinalizePacket(trace_packet_proto);
+
+  auto* trace_event = ValidateAndGetBasicTestPacket();
+
+  EXPECT_EQ("conv_value1", trace_event->GetKnownArgAsString("foo1"));
+  EXPECT_EQ("conv_value2", trace_event->GetKnownArgAsString("foo2"));
+}
+
+TEST_F(ChromeEventBundleJsonExporterTest, TestEventWithTracedValueArg) {
+  perfetto::protos::TracePacket trace_packet_proto;
+  auto* new_trace_event =
+      trace_packet_proto.mutable_chrome_events()->add_trace_events();
+  SetTestPacketBasicData(new_trace_event);
+
+  auto* new_arg = new_trace_event->add_args();
+  new_arg->set_name("foo1");
+  auto* traced_value = new_arg->mutable_traced_value();
+  traced_value->add_dict_keys("bool");
+  traced_value->add_dict_values()->set_bool_value(true);
+
+  FinalizePacket(trace_packet_proto);
+
+  auto* trace_event = ValidateAndGetBasicTestPacket();
+
+  auto arg_value = trace_event->GetKnownArgAsValue("foo1");
+  EXPECT_EQ(true, arg_value->FindKey("bool")->GetBool());
+}
+
+TEST_F(ChromeEventBundleJsonExporterTest, TracedValueFlatDictionary) {
+  perfetto::protos::TracePacket trace_packet_proto;
+  auto* new_trace_event =
+      trace_packet_proto.mutable_chrome_events()->add_trace_events();
+  SetTestPacketBasicData(new_trace_event);
+
+  auto* new_arg = new_trace_event->add_args();
+  new_arg->set_name("foo1");
+  perfetto::protos::ChromeTracedValue* traced_value =
+      new_arg->mutable_traced_value();
+
+  {
+    traced_value->add_dict_keys("bool");
+    traced_value->add_dict_values()->set_bool_value(true);
+  }
+
+  {
+    traced_value->add_dict_keys("double");
+    traced_value->add_dict_values()->set_double_value(8.0);
+  }
+
+  {
+    traced_value->add_dict_keys("int");
+    traced_value->add_dict_values()->set_int_value(2014);
+  }
+
+  {
+    traced_value->add_dict_keys("string");
+    traced_value->add_dict_values()->set_string_value("bar");
+  }
+
+  FinalizePacket(trace_packet_proto);
+  // Ensure the trace analyzer can get the data back out.
+  const trace_analyzer::TraceEvent* trace_event =
+      ValidateAndGetBasicTestPacket();
+  EXPECT_TRUE(trace_event);
+  auto arg_value = trace_event->GetKnownArgAsValue("foo1");
+  EXPECT_EQ(true, arg_value->FindKey("bool")->GetBool());
+  EXPECT_EQ(8.0, arg_value->FindKey("double")->GetDouble());
+  EXPECT_EQ(2014, arg_value->FindKey("int")->GetInt());
+  EXPECT_EQ("bar", arg_value->FindKey("string")->GetString());
+
+  const std::string expected_json =
+      "{\"bool\":true,\"double\":8.0,\"int\":2014,\"string\":\"bar\"}";
+  // The dictionary should only have been appended once.
+  auto dict_idx = unparsed_trace_data_.find(expected_json, 0);
+  EXPECT_NE(dict_idx, std::string::npos);
+  EXPECT_EQ(std::string::npos,
+            unparsed_trace_data_.find(expected_json, dict_idx + 1));
+}
+
+TEST_F(ChromeEventBundleJsonExporterTest, TracedValueHierarchy) {
+  perfetto::protos::TracePacket trace_packet_proto;
+  auto* new_trace_event =
+      trace_packet_proto.mutable_chrome_events()->add_trace_events();
+  SetTestPacketBasicData(new_trace_event);
+
+  auto* new_arg = new_trace_event->add_args();
+  new_arg->set_name("foo1");
+  perfetto::protos::ChromeTracedValue* traced_value =
+      new_arg->mutable_traced_value();
+
+  {
+    traced_value->add_dict_keys("a1");
+    auto* a1_array = traced_value->add_dict_values();
+    a1_array->set_nested_type(perfetto::protos::ChromeTracedValue::ARRAY);
+
+    a1_array->add_array_values()->set_int_value(1);
+    a1_array->add_array_values()->set_bool_value(true);
+
+    auto* sub_dict = a1_array->add_array_values();
+    sub_dict->set_nested_type(perfetto::protos::ChromeTracedValue::DICT);
+    sub_dict->add_dict_keys("i2");
+    sub_dict->add_dict_values()->set_int_value(3);
+  }
+
+  {
+    traced_value->add_dict_keys("b0");
+    traced_value->add_dict_values()->set_bool_value(true);
+  }
+
+  {
+    traced_value->add_dict_keys("d0");
+    traced_value->add_dict_values()->set_double_value(6.0);
+  }
+
+  {
+    traced_value->add_dict_keys("a1");
+    auto* dict1_subdict = traced_value->add_dict_values();
+    dict1_subdict->set_nested_type(perfetto::protos::ChromeTracedValue::DICT);
+
+    dict1_subdict->add_dict_keys("dict2");
+    auto* dict2_sub_sub_dict = dict1_subdict->add_dict_values();
+    dict2_sub_sub_dict->set_nested_type(
+        perfetto::protos::ChromeTracedValue::DICT);
+
+    dict2_sub_sub_dict->add_dict_keys("b2");
+    dict2_sub_sub_dict->add_dict_values()->set_bool_value(true);
+
+    dict1_subdict->add_dict_keys("i1");
+    dict1_subdict->add_dict_values()->set_int_value(2014);
+
+    dict1_subdict->add_dict_keys("s1");
+    dict1_subdict->add_dict_values()->set_string_value("foo");
+  }
+
+  {
+    traced_value->add_dict_keys("i0");
+    traced_value->add_dict_values()->set_int_value(2014);
+  }
+
+  {
+    traced_value->add_dict_keys("s0");
+    traced_value->add_dict_values()->set_string_value("foo");
+  }
+  FinalizePacket(trace_packet_proto);
+
+  const std::string expected_json =
+      "{\"a1\":[1,true,{\"i2\":3}],\"b0\":true,\"d0\":6.0,\"a1\":{\"dict2\":{"
+      "\"b2\":true},\"i1\":2014,\"s1\":\"foo\"},\"i0\":2014,\"s0\":\"foo\"}";
+  // The dictionary should only have been appended once.
+  auto dict_idx = unparsed_trace_data_.find(expected_json, 0);
+  EXPECT_NE(dict_idx, std::string::npos);
+  EXPECT_EQ(std::string::npos,
+            unparsed_trace_data_.find(expected_json, dict_idx + 1));
+}
+
+TEST_F(ChromeEventBundleJsonExporterTest, TestLegacyUserTrace) {
+  perfetto::protos::TracePacket trace_packet_proto;
+
+  auto* new_trace_event =
+      trace_packet_proto.mutable_chrome_events()->add_trace_events();
+  SetTestPacketBasicData(new_trace_event);
+
+  auto* json_trace =
+      trace_packet_proto.mutable_chrome_events()->add_legacy_json_trace();
+  json_trace->set_type(perfetto::protos::ChromeLegacyJsonTrace::USER_TRACE);
+  json_trace->set_data(
+      "{\"pid\":10,\"tid\":11,\"ts\":23,\"ph\":\"I\""
+      ",\"cat\":\"cat_name2\",\"name\":\"bar_name\""
+      ",\"id2\":{\"global\":\"0x5\"},\"args\":{}}");
+
+  FinalizePacket(trace_packet_proto);
+
+  ValidateAndGetBasicTestPacket();
+
+  const trace_analyzer::TraceEvent* trace_event = trace_analyzer()->FindFirstOf(
+      trace_analyzer::Query(trace_analyzer::Query::EVENT_NAME) ==
+      trace_analyzer::Query::String("bar_name"));
+  EXPECT_TRUE(trace_event);
+
+  EXPECT_EQ(10, trace_event->thread.process_id);
+  EXPECT_EQ(11, trace_event->thread.thread_id);
+  EXPECT_EQ(23, trace_event->timestamp);
+  EXPECT_EQ('I', trace_event->phase);
+  EXPECT_EQ("bar_name", trace_event->name);
+  EXPECT_EQ("cat_name2", trace_event->category);
+  EXPECT_EQ("0x5", trace_event->global_id2);
+}
+
+TEST_F(ChromeEventBundleJsonExporterTest, TestLegacySystemFtrace) {
+  std::string ftrace = "#dummy data";
+
+  perfetto::protos::TracePacket trace_packet_proto;
+  trace_packet_proto.mutable_chrome_events()->add_legacy_ftrace_output(ftrace);
+  FinalizePacket(trace_packet_proto);
+
+  auto* sys_trace = parsed_trace_data()->FindKey("systemTraceEvents");
+  EXPECT_TRUE(sys_trace);
+  EXPECT_EQ(sys_trace->GetString(), ftrace);
+}
+
+TEST_F(ChromeEventBundleJsonExporterTest, TestLegacySystemTraceEvents) {
+  perfetto::protos::TracePacket trace_packet_proto;
+
+  auto* json_trace =
+      trace_packet_proto.mutable_chrome_events()->add_legacy_json_trace();
+  json_trace->set_type(perfetto::protos::ChromeLegacyJsonTrace::SYSTEM_TRACE);
+  json_trace->set_data(
+      "\"name\":\"MySysTrace\",\"content\":["
+      "{\"pid\":10,\"tid\":11,\"ts\":23,\"ph\":\"I\""
+      ",\"cat\":\"cat_name2\",\"name\":\"bar_name\""
+      ",\"id2\":{\"global\":\"0x5\"},\"args\":{}}]");
+
+  FinalizePacket(trace_packet_proto);
+
+  auto* sys_trace = parsed_trace_data()->FindKey("systemTraceEvents");
+  EXPECT_TRUE(sys_trace);
+  EXPECT_EQ(sys_trace->FindKey("name")->GetString(), "MySysTrace");
+  auto* content = sys_trace->FindKey("content");
+  EXPECT_EQ(content->GetList().size(), 1u);
+  EXPECT_EQ(content->GetList()[0].FindKey("pid")->GetInt(), 10);
+  EXPECT_EQ(content->GetList()[0].FindKey("tid")->GetInt(), 11);
+  EXPECT_EQ(content->GetList()[0].FindKey("name")->GetString(), "bar_name");
+}
+
+TEST_F(ChromeEventBundleJsonExporterTest, ArgsWhitelisting) {
+  EnableArgumentFilter();
+
+  perfetto::protos::TracePacket trace_packet_proto;
+
+  {
+    auto* new_trace_event =
+        trace_packet_proto.mutable_chrome_events()->add_trace_events();
+    SetTestPacketBasicData(new_trace_event);
+    new_trace_event->set_name("event1");
+    new_trace_event->set_category_group_name("toplevel");
+    auto* new_arg = new_trace_event->add_args();
+    new_arg->set_name("int_one");
+    new_arg->set_uint_value(1);
+  }
+
+  {
+    auto* new_trace_event =
+        trace_packet_proto.mutable_chrome_events()->add_trace_events();
+    SetTestPacketBasicData(new_trace_event);
+    new_trace_event->set_name("event2");
+    new_trace_event->set_category_group_name("whitewashed");
+    auto* new_arg = new_trace_event->add_args();
+    new_arg->set_name("int_two");
+    new_arg->set_uint_value(1);
+  }
+
+  {
+    auto* new_trace_event =
+        trace_packet_proto.mutable_chrome_events()->add_trace_events();
+    SetTestPacketBasicData(new_trace_event);
+    new_trace_event->set_name("granularly_whitelisted");
+    new_trace_event->set_category_group_name("benchmark");
+    auto* new_arg1 = new_trace_event->add_args();
+    new_arg1->set_name("granular_arg_whitelisted");
+    new_arg1->set_string_value("whitelisted_value");
+    auto* new_arg2 = new_trace_event->add_args();
+    new_arg2->set_name("granular_arg_blacklisted");
+    new_arg2->set_string_value("blacklisted_value");
+  }
+
+  FinalizePacket(trace_packet_proto);
+
+  {
+    const auto* trace_event = trace_analyzer()->FindFirstOf(
+        trace_analyzer::Query(trace_analyzer::Query::EVENT_NAME) ==
+        trace_analyzer::Query::String("event1"));
+    EXPECT_TRUE(trace_event);
+    EXPECT_EQ(1, trace_event->GetKnownArgAsDouble("int_one"));
+  }
+
+  {
+    const auto* trace_event = trace_analyzer()->FindFirstOf(
+        trace_analyzer::Query(trace_analyzer::Query::EVENT_NAME) ==
+        trace_analyzer::Query::String("event2"));
+    EXPECT_TRUE(trace_event);
+    EXPECT_FALSE(trace_event->HasArg(("int_two")));
+  }
+
+  {
+    const auto* trace_event = trace_analyzer()->FindFirstOf(
+        trace_analyzer::Query(trace_analyzer::Query::EVENT_NAME) ==
+        trace_analyzer::Query::String("granularly_whitelisted"));
+    EXPECT_TRUE(trace_event);
+    EXPECT_EQ("whitelisted_value",
+              trace_event->GetKnownArgAsString(("granular_arg_whitelisted")));
+    EXPECT_EQ("__stripped__",
+              trace_event->GetKnownArgAsString(("granular_arg_blacklisted")));
+  }
+}
+
+}  // namespace tracing
diff --git a/services/tracing/perfetto/json_trace_exporter.cc b/services/tracing/perfetto/json_trace_exporter.cc
index ab92eb26..6d664c0 100644
--- a/services/tracing/perfetto/json_trace_exporter.cc
+++ b/services/tracing/perfetto/json_trace_exporter.cc
@@ -4,289 +4,141 @@
 
 #include "services/tracing/perfetto/json_trace_exporter.h"
 
-#include <unordered_map>
 #include <utility>
 
 #include "base/format_macros.h"
 #include "base/json/json_reader.h"
 #include "base/json/json_writer.h"
 #include "base/json/string_escape.h"
-#include "base/logging.h"
-#include "base/strings/stringprintf.h"
 #include "base/trace_event/trace_event.h"
+#include "third_party/perfetto/include/perfetto/tracing/core/trace_packet.h"
 #include "third_party/perfetto/protos/perfetto/trace/chrome/chrome_trace_event.pbzero.h"
 #include "third_party/perfetto/protos/perfetto/trace/chrome/chrome_trace_packet.pb.h"
 
-using TraceEvent = base::trace_event::TraceEvent;
-
 namespace tracing {
 
 namespace {
 
-const size_t kTraceEventBufferSizeInBytes = 100 * 1024;
+constexpr size_t kTraceEventBufferSizeInBytes = 100 * 1024;
 
-void AppendProtoArrayAsJSON(std::string* out,
-                            const perfetto::protos::ChromeTracedValue& array);
+}  // namespace
 
-void AppendProtoValueAsJSON(std::string* out,
-                            const perfetto::protos::ChromeTracedValue& value) {
-  base::trace_event::TraceEvent::TraceValue json_value;
-  if (value.has_int_value()) {
-    json_value.as_int = value.int_value();
-    TraceEvent::AppendValueAsJSON(TRACE_VALUE_TYPE_INT, json_value, out);
-  } else if (value.has_double_value()) {
-    json_value.as_double = value.double_value();
-    TraceEvent::AppendValueAsJSON(TRACE_VALUE_TYPE_DOUBLE, json_value, out);
-  } else if (value.has_bool_value()) {
-    json_value.as_bool = value.bool_value();
-    TraceEvent::AppendValueAsJSON(TRACE_VALUE_TYPE_BOOL, json_value, out);
-  } else if (value.has_string_value()) {
-    json_value.as_string = value.string_value().c_str();
-    TraceEvent::AppendValueAsJSON(TRACE_VALUE_TYPE_STRING, json_value, out);
-  } else if (value.has_nested_type()) {
-    if (value.nested_type() == perfetto::protos::ChromeTracedValue::ARRAY) {
-      AppendProtoArrayAsJSON(out, value);
-      return;
-    } else if (value.nested_type() ==
-               perfetto::protos::ChromeTracedValue::DICT) {
-      AppendProtoDictAsJSON(out, value);
-    } else {
-      NOTREACHED();
+JSONTraceExporter::JSONTraceExporter(
+    ArgumentFilterPredicate argument_filter_predicate,
+    OnTraceEventJSONCallback callback)
+    : out_(callback),
+      metadata_(std::make_unique<base::DictionaryValue>()),
+      argument_filter_predicate_(std::move(argument_filter_predicate)) {}
+
+JSONTraceExporter::~JSONTraceExporter() = default;
+
+void JSONTraceExporter::OnTraceData(std::vector<perfetto::TracePacket> packets,
+                                    bool has_more) {
+  DCHECK(!packets.empty() || !has_more);
+
+  if (label_filter_.empty() && !has_output_json_preamble_) {
+    out_ += "{\"traceEvents\":[";
+    has_output_json_preamble_ = true;
+  }
+
+  // Delegate to the subclasses to parse the packets. It will create
+  // ScopedJSONTraceEventAppenders to write the contained events along with
+  // other trace fields.
+  ProcessPackets(packets);
+
+  if (!has_more) {
+    if (label_filter_.empty()) {
+      // We are done adding events so now we close the traceEvents array and
+      // append the rest of the fields. The rest of the fields aren't very large
+      // so we don't need to check if we need to run the callback.
+      out_ += "]";
     }
+
+    if ((label_filter_.empty() || label_filter_ == "systemTraceEvents") &&
+        (!legacy_system_ftrace_output_.empty() ||
+         !legacy_system_trace_events_.empty())) {
+      DCHECK(legacy_system_ftrace_output_.empty() ||
+             legacy_system_trace_events_.empty());
+      out_ += ",\"systemTraceEvents\":";
+      if (!legacy_system_ftrace_output_.empty()) {
+        std::string escaped;
+        base::EscapeJSONString(legacy_system_ftrace_output_,
+                               true /* put_in_quotes */, &escaped);
+        out_ += escaped;
+      } else {
+        out_ += legacy_system_trace_events_ + "}";
+      }
+    }
+
+    if (label_filter_.empty()) {
+      if (!metadata_->empty()) {
+        out_ += ",\"metadata\":";
+        std::string json_value;
+        base::JSONWriter::Write(*metadata_, &json_value);
+        out_ += json_value;
+      }
+    }
+
+    // Finish the json object we started in the preamble.
+    out_ += "}";
+  }
+
+  // Send any remaining data. There is no harm issuing the callback with an
+  // empty string, so we do it unconditionally.
+  out_.Flush(metadata_.get(), has_more);
+}
+
+bool JSONTraceExporter::ShouldOutputTraceEvents() const {
+  return label_filter_.empty() || label_filter_ == "traceEvents";
+}
+
+void JSONTraceExporter::AddChromeLegacyJSONTrace(
+    const perfetto::protos::ChromeLegacyJsonTrace& json_trace) {
+  // Tracing agents should only add this field when there is some data.
+  DCHECK(!json_trace.data().empty());
+  switch (json_trace.type()) {
+    case perfetto::protos::ChromeLegacyJsonTrace::USER_TRACE:
+      *AddJSONTraceEvent() += json_trace.data();
+      return;
+    case perfetto::protos::ChromeLegacyJsonTrace::SYSTEM_TRACE:
+      if (!ShouldOutputTraceEvents()) {
+        return;
+      }
+      if (legacy_system_trace_events_.empty()) {
+        legacy_system_trace_events_ = "{";
+      } else {
+        legacy_system_trace_events_ += ",";
+      }
+      legacy_system_trace_events_ += json_trace.data();
+      return;
+    default:
+      NOTREACHED();
+  }
+}
+
+void JSONTraceExporter::AddLegacyFtrace(
+    const std::string& legacy_ftrace_output) {
+  legacy_system_ftrace_output_ += legacy_ftrace_output;
+}
+
+void JSONTraceExporter::AddChromeMetadata(
+    const perfetto::protos::ChromeMetadata& metadata) {
+  if (metadata.has_string_value()) {
+    metadata_->SetString(metadata.name(), metadata.string_value());
+  } else if (metadata.has_int_value()) {
+    metadata_->SetInteger(metadata.name(), metadata.int_value());
+  } else if (metadata.has_bool_value()) {
+    metadata_->SetBoolean(metadata.name(), metadata.bool_value());
+  } else if (metadata.has_json_value()) {
+    std::unique_ptr<base::Value> value(
+        base::JSONReader::ReadDeprecated(metadata.json_value()));
+    metadata_->Set(metadata.name(), std::move(value));
   } else {
     NOTREACHED();
   }
 }
 
-void AppendProtoArrayAsJSON(std::string* out,
-                            const perfetto::protos::ChromeTracedValue& array) {
-  out->append("[");
-
-  bool is_first_entry = true;
-  for (auto& value : array.array_values()) {
-    if (!is_first_entry) {
-      out->append(",");
-    } else {
-      is_first_entry = false;
-    }
-
-    AppendProtoValueAsJSON(out, value);
-  }
-
-  out->append("]");
-}
-
-const char* GetStringFromStringTable(
-    const std::unordered_map<int, std::string>& string_table,
-    int index) {
-  auto it = string_table.find(index);
-  DCHECK(it != string_table.end());
-
-  return it->second.c_str();
-}
-
-void OutputJSONFromArgumentValue(
-    const perfetto::protos::ChromeTraceEvent::Arg& arg,
-    std::string* out) {
-  TraceEvent::TraceValue value;
-  if (arg.has_bool_value()) {
-    value.as_bool = arg.bool_value();
-    TraceEvent::AppendValueAsJSON(TRACE_VALUE_TYPE_BOOL, value, out);
-    return;
-  }
-
-  if (arg.has_uint_value()) {
-    value.as_uint = arg.uint_value();
-    TraceEvent::AppendValueAsJSON(TRACE_VALUE_TYPE_UINT, value, out);
-    return;
-  }
-
-  if (arg.has_int_value()) {
-    value.as_int = arg.int_value();
-    TraceEvent::AppendValueAsJSON(TRACE_VALUE_TYPE_INT, value, out);
-    return;
-  }
-
-  if (arg.has_double_value()) {
-    value.as_double = arg.double_value();
-    TraceEvent::AppendValueAsJSON(TRACE_VALUE_TYPE_DOUBLE, value, out);
-    return;
-  }
-
-  if (arg.has_pointer_value()) {
-    value.as_pointer = reinterpret_cast<void*>(arg.pointer_value());
-    TraceEvent::AppendValueAsJSON(TRACE_VALUE_TYPE_POINTER, value, out);
-    return;
-  }
-
-  if (arg.has_string_value()) {
-    std::string str = arg.string_value();
-    value.as_string = &str[0];
-    TraceEvent::AppendValueAsJSON(TRACE_VALUE_TYPE_STRING, value, out);
-    return;
-  }
-
-  if (arg.has_json_value()) {
-    *out += arg.json_value();
-    return;
-  }
-
-  if (arg.has_traced_value()) {
-    AppendProtoDictAsJSON(out, arg.traced_value());
-    return;
-  }
-
-  NOTREACHED();
-}
-
-void OutputJSONFromTraceEventProto(
-    const perfetto::protos::ChromeTraceEvent& event,
-    const std::unordered_map<int, std::string>& string_table,
-    const JSONTraceExporter::ArgumentFilterPredicate& argument_filter_predicate,
-    std::string* out) {
-  char phase = static_cast<char>(event.phase());
-  const char* name =
-      event.has_name_index()
-          ? GetStringFromStringTable(string_table, event.name_index())
-          : event.name().c_str();
-  const char* category_group_name =
-      event.has_category_group_name_index()
-          ? GetStringFromStringTable(string_table,
-                                     event.category_group_name_index())
-          : event.category_group_name().c_str();
-
-  base::StringAppendF(out,
-                      "{\"pid\":%i,\"tid\":%i,\"ts\":%" PRId64
-                      ",\"ph\":\"%c\",\"cat\":\"%s\",\"name\":",
-                      event.process_id(), event.thread_id(), event.timestamp(),
-                      phase, category_group_name);
-  base::EscapeJSONString(name, true, out);
-
-  if (event.has_duration()) {
-    base::StringAppendF(out, ",\"dur\":%" PRId64, event.duration());
-  }
-
-  if (event.has_thread_duration()) {
-    base::StringAppendF(out, ",\"tdur\":%" PRId64, event.thread_duration());
-  }
-
-  if (event.has_thread_timestamp()) {
-    base::StringAppendF(out, ",\"tts\":%" PRId64, event.thread_timestamp());
-  }
-
-  // Output async tts marker field if flag is set.
-  if (event.flags() & TRACE_EVENT_FLAG_ASYNC_TTS) {
-    base::StringAppendF(out, ", \"use_async_tts\":1");
-  }
-
-  // If id_ is set, print it out as a hex string so we don't loose any
-  // bits (it might be a 64-bit pointer).
-  unsigned int id_flags =
-      event.flags() & (TRACE_EVENT_FLAG_HAS_ID | TRACE_EVENT_FLAG_HAS_LOCAL_ID |
-                       TRACE_EVENT_FLAG_HAS_GLOBAL_ID);
-  if (id_flags) {
-    if (event.has_scope()) {
-      base::StringAppendF(out, ",\"scope\":\"%s\"", event.scope().c_str());
-    }
-
-    DCHECK(event.has_id());
-    switch (id_flags) {
-      case TRACE_EVENT_FLAG_HAS_ID:
-        base::StringAppendF(out, ",\"id\":\"0x%" PRIx64 "\"",
-                            static_cast<uint64_t>(event.id()));
-        break;
-
-      case TRACE_EVENT_FLAG_HAS_LOCAL_ID:
-        base::StringAppendF(out, ",\"id2\":{\"local\":\"0x%" PRIx64 "\"}",
-                            static_cast<uint64_t>(event.id()));
-        break;
-
-      case TRACE_EVENT_FLAG_HAS_GLOBAL_ID:
-        base::StringAppendF(out, ",\"id2\":{\"global\":\"0x%" PRIx64 "\"}",
-                            static_cast<uint64_t>(event.id()));
-        break;
-
-      default:
-        NOTREACHED() << "More than one of the ID flags are set";
-        break;
-    }
-  }
-
-  if (event.flags() & TRACE_EVENT_FLAG_BIND_TO_ENCLOSING)
-    base::StringAppendF(out, ",\"bp\":\"e\"");
-
-  if (event.has_bind_id()) {
-    base::StringAppendF(out, ",\"bind_id\":\"0x%" PRIx64 "\"",
-                        static_cast<uint64_t>(event.bind_id()));
-  }
-
-  if (event.flags() & TRACE_EVENT_FLAG_FLOW_IN)
-    base::StringAppendF(out, ",\"flow_in\":true");
-  if (event.flags() & TRACE_EVENT_FLAG_FLOW_OUT)
-    base::StringAppendF(out, ",\"flow_out\":true");
-
-  // Instant events also output their scope.
-  if (phase == TRACE_EVENT_PHASE_INSTANT) {
-    char scope = '?';
-    switch (event.flags() & TRACE_EVENT_FLAG_SCOPE_MASK) {
-      case TRACE_EVENT_SCOPE_GLOBAL:
-        scope = TRACE_EVENT_SCOPE_NAME_GLOBAL;
-        break;
-
-      case TRACE_EVENT_SCOPE_PROCESS:
-        scope = TRACE_EVENT_SCOPE_NAME_PROCESS;
-        break;
-
-      case TRACE_EVENT_SCOPE_THREAD:
-        scope = TRACE_EVENT_SCOPE_NAME_THREAD;
-        break;
-    }
-    base::StringAppendF(out, ",\"s\":\"%c\"", scope);
-  }
-
-  *out += ",\"args\":";
-
-  JSONTraceExporter::ArgumentNameFilterPredicate argument_name_filter_predicate;
-  bool strip_args =
-      event.args_size() > 0 && !argument_filter_predicate.is_null() &&
-      !argument_filter_predicate.Run(category_group_name, name,
-                                     &argument_name_filter_predicate);
-
-  if (strip_args) {
-    *out += "\"__stripped__\"";
-  } else {
-    *out += "{";
-
-    for (int i = 0; i < event.args_size(); ++i) {
-      auto& arg = event.args(i);
-
-      if (i > 0) {
-        *out += ",";
-      }
-
-      *out += "\"";
-      std::string arg_name =
-          arg.has_name_index()
-              ? GetStringFromStringTable(string_table, arg.name_index())
-              : arg.name();
-      *out += arg_name;
-      *out += "\":";
-
-      if (!argument_name_filter_predicate.is_null() &&
-          !argument_name_filter_predicate.Run(arg_name.c_str())) {
-        *out += "\"__stripped__\"";
-        continue;
-      }
-      OutputJSONFromArgumentValue(arg, out);
-    }
-
-    *out += "}";
-  }
-
-  *out += "}";
-}
-
-std::unique_ptr<base::DictionaryValue> ConvertTraceStatsToDict(
+void JSONTraceExporter::SetTraceStatsMetadata(
     const perfetto::protos::TraceStats& trace_stats) {
   auto dict = std::make_unique<base::DictionaryValue>();
   dict->SetInteger("producers_connected", trace_stats.producers_connected());
@@ -328,179 +180,302 @@
     buf_list->GetList().push_back(std::move(buf_value));
   }
   dict->SetList("buffer_stats", std::move(buf_list));
-  return dict;
+  metadata_->SetDictionary("perfetto_trace_stats", std::move(dict));
 }
 
-}  // namespace
+JSONTraceExporter::ScopedJSONTraceEventAppender
+JSONTraceExporter::AddTraceEvent(const char* name,
+                                 const char* categories,
+                                 int32_t phase,
+                                 int64_t timestamp,
+                                 int32_t pid,
+                                 int32_t tid) {
+  DCHECK(ShouldOutputTraceEvents());
+  return JSONTraceExporter::ScopedJSONTraceEventAppender(
+      AddJSONTraceEvent(), argument_filter_predicate_, name, categories, phase,
+      timestamp, pid, tid);
+}
 
-void AppendProtoDictAsJSON(std::string* out,
-                           const perfetto::protos::ChromeTracedValue& dict) {
-  out->append("{");
-
-  DCHECK_EQ(dict.dict_keys_size(), dict.dict_values_size());
-  for (int i = 0; i < dict.dict_keys_size(); ++i) {
-    if (i != 0) {
-      out->append(",");
-    }
-
-    base::EscapeJSONString(dict.dict_keys(i), true, out);
-    out->append(":");
-
-    AppendProtoValueAsJSON(out, dict.dict_values(i));
+JSONTraceExporter::StringBuffer* JSONTraceExporter::AddJSONTraceEvent() {
+  // If we've already added the first value in the TraceEvent array then we need
+  // a comma and add a newline for readability. Otherwise we're fine but update
+  // so in future we know we've finished the first event.
+  if (has_output_first_event_) {
+    out_ += ",\n";
+  } else {
+    has_output_first_event_ = true;
   }
-
-  out->append("}");
+  return &out_;
 }
 
-JSONTraceExporter::JSONTraceExporter(
-    ArgumentFilterPredicate argument_filter_predicate,
-    OnTraceEventJSONCallback callback)
-    : json_callback_(callback),
-      metadata_(std::make_unique<base::DictionaryValue>()),
-      argument_filter_predicate_(argument_filter_predicate) {
-  DCHECK(json_callback_);
-}
-
-JSONTraceExporter::~JSONTraceExporter() = default;
-
-void JSONTraceExporter::OnTraceData(std::vector<perfetto::TracePacket> packets,
-                                    bool has_more) {
-  DCHECK(json_callback_);
-  DCHECK(!packets.empty() || !has_more);
-
-  // Since we write each event before checking the limit, we'll
+JSONTraceExporter::StringBuffer::StringBuffer(
+    JSONTraceExporter::OnTraceEventJSONCallback callback)
+    : callback_(std::move(callback)) {
+  // Since we write each string before checking the limit, we'll
   // always go slightly over and hence we reserve some extra space
   // to avoid most reallocs.
   const size_t kReserveCapacity = kTraceEventBufferSizeInBytes * 5 / 4;
-  std::string out;
-  out.reserve(kReserveCapacity);
-
-  if (label_filter_.empty() && !has_output_json_preamble_) {
-    out = "{\"traceEvents\":[";
-    has_output_json_preamble_ = true;
-  }
-
-  for (auto& encoded_packet : packets) {
-    perfetto::protos::ChromeTracePacket packet;
-    bool decoded = encoded_packet.Decode(&packet);
-    DCHECK(decoded);
-
-    if (packet.has_trace_stats()) {
-      metadata_->SetDictionary("perfetto_trace_stats",
-                               ConvertTraceStatsToDict(packet.trace_stats()));
-      continue;
-    }
-
-    if (!packet.has_chrome_events()) {
-      continue;
-    }
-
-    auto& bundle = packet.chrome_events();
-
-    if (label_filter_.empty() || label_filter_ == "traceEvents") {
-      std::unordered_map<int, std::string> string_table;
-      for (auto& string_table_entry : bundle.string_table()) {
-        string_table[string_table_entry.index()] = string_table_entry.value();
-      }
-
-      for (auto& event : bundle.trace_events()) {
-        if (out.size() > kTraceEventBufferSizeInBytes) {
-          json_callback_.Run(out, nullptr, true);
-          out.clear();
-        }
-
-        if (has_output_first_event_) {
-          out += ",\n";
-        } else {
-          has_output_first_event_ = true;
-        }
-
-        OutputJSONFromTraceEventProto(event, string_table,
-                                      argument_filter_predicate_, &out);
-      }
-    }
-
-    for (auto& metadata : bundle.metadata()) {
-      if (metadata.has_string_value()) {
-        metadata_->SetString(metadata.name(), metadata.string_value());
-      } else if (metadata.has_int_value()) {
-        metadata_->SetInteger(metadata.name(), metadata.int_value());
-      } else if (metadata.has_bool_value()) {
-        metadata_->SetBoolean(metadata.name(), metadata.bool_value());
-      } else if (metadata.has_json_value()) {
-        std::unique_ptr<base::Value> value(
-            base::JSONReader::ReadDeprecated(metadata.json_value()));
-        metadata_->Set(metadata.name(), std::move(value));
-      } else {
-        NOTREACHED();
-      }
-    }
-
-    for (auto& legacy_ftrace_output : bundle.legacy_ftrace_output()) {
-      legacy_system_ftrace_output_ += legacy_ftrace_output;
-    }
-
-    for (auto& legacy_json_trace : bundle.legacy_json_trace()) {
-      // Tracing agents should only add this field when there is some data.
-      DCHECK(!legacy_json_trace.data().empty());
-      switch (legacy_json_trace.type()) {
-        case perfetto::protos::ChromeLegacyJsonTrace::USER_TRACE:
-          if (has_output_first_event_) {
-            out += ",\n";
-          } else {
-            has_output_first_event_ = true;
-          }
-          out += legacy_json_trace.data();
-          break;
-        case perfetto::protos::ChromeLegacyJsonTrace::SYSTEM_TRACE:
-          if (legacy_system_trace_events_.empty()) {
-            legacy_system_trace_events_ = "{";
-          } else {
-            legacy_system_trace_events_ += ",";
-          }
-          legacy_system_trace_events_ += legacy_json_trace.data();
-          break;
-        default:
-          NOTREACHED();
-      }
-    }
-  }
-
-  if (!has_more) {
-    if (label_filter_.empty()) {
-      out += "]";
-    }
-
-    if ((label_filter_.empty() || label_filter_ == "systemTraceEvents") &&
-        (!legacy_system_ftrace_output_.empty() ||
-         !legacy_system_trace_events_.empty())) {
-      // Should only have system events (e.g. ETW) or system ftrace output.
-      DCHECK(legacy_system_ftrace_output_.empty() ||
-             legacy_system_trace_events_.empty());
-      out += ",\"systemTraceEvents\":";
-      if (!legacy_system_ftrace_output_.empty()) {
-        std::string escaped;
-        base::EscapeJSONString(legacy_system_ftrace_output_,
-                               true /* put_in_quotes */, &escaped);
-        out += escaped;
-      } else {
-        out += legacy_system_trace_events_ + "}";
-      }
-    }
-
-    if (label_filter_.empty()) {
-      if (!metadata_->empty()) {
-        out += ",\"metadata\":";
-        std::string json_value;
-        base::JSONWriter::Write(*metadata_, &json_value);
-        out += json_value;
-      }
-
-      out += "}";
-    }
-  }
-
-  json_callback_.Run(out, metadata_.get(), has_more);
+  out_.reserve(kReserveCapacity);
 }
 
+JSONTraceExporter::StringBuffer::~StringBuffer() {}
+
+JSONTraceExporter::StringBuffer& JSONTraceExporter::StringBuffer::operator+=(
+    const std::string& input) {
+  MaybeRunCallback();
+  out_ += input;
+  return *this;
+}
+
+JSONTraceExporter::StringBuffer& JSONTraceExporter::StringBuffer::operator+=(
+    std::string&& input) {
+  MaybeRunCallback();
+  out_ += std::move(input);
+  return *this;
+}
+
+JSONTraceExporter::StringBuffer& JSONTraceExporter::StringBuffer::operator+=(
+    const char* input) {
+  MaybeRunCallback();
+  out_ += input;
+  return *this;
+}
+
+std::string* JSONTraceExporter::StringBuffer::mutable_out() {
+  return &out_;
+}
+
+const std::string& JSONTraceExporter::StringBuffer::out() {
+  return out_;
+}
+
+void JSONTraceExporter::StringBuffer::EscapeJSONAndAppend(
+    const std::string& unescaped) {
+  MaybeRunCallback();
+  base::EscapeJSONString(unescaped, true, &out_);
+}
+
+void JSONTraceExporter::StringBuffer::Flush(base::DictionaryValue* metadata,
+                                            bool has_more) {
+  callback_.Run(out_, metadata, has_more);
+  if (has_more) {
+    // We clear |out_| because we've processed all the current data in |out_|
+    // and we don't want any data to be repeated. We have to protect this by
+    // checking |has_more| because the callback could have deleted |this| in
+    // which cause |out_| is a destroyed as well.
+    out_.clear();
+  }
+}
+
+void JSONTraceExporter::StringBuffer::MaybeRunCallback() {
+  // If the string has exceeded the threshold we send the current part back
+  // through the callback and clear the string to prevent it from getting to
+  // large.
+  if (out_.size() > kTraceEventBufferSizeInBytes) {
+    Flush(nullptr, true);
+  }
+}
+
+JSONTraceExporter::ArgumentBuilder::ArgumentBuilder(
+    const ArgumentFilterPredicate& argument_filter_predicate,
+    const char* name,
+    const char* category_group_name,
+    StringBuffer* out)
+    : out_(out) {
+  JSONTraceExporter::ArgumentNameFilterPredicate argument_name_filter_predicate;
+  strip_args_ =
+      !argument_filter_predicate.is_null() &&
+      !argument_filter_predicate.Run(category_group_name, name,
+                                     &argument_name_filter_predicate_);
+  *out_ += ",\"args\":";
+}
+
+JSONTraceExporter::ArgumentBuilder::~ArgumentBuilder() {
+  if (strip_args_ && has_args_) {
+    *out_ += "\"__stripped__\"";
+  } else if (!has_args_) {
+    *out_ += "{}";
+  } else {
+    *out_ += "}";
+  }
+}
+
+JSONTraceExporter::StringBuffer*
+JSONTraceExporter::ArgumentBuilder::MaybeAddArg(const std::string& name) {
+  if (SkipBecauseStripped(name)) {
+    return nullptr;
+  }
+  auto* buffer = AddArg();
+  buffer->AppendF("\"%s\":", name.c_str());
+  return buffer;
+}
+
+JSONTraceExporter::StringBuffer* JSONTraceExporter::ArgumentBuilder::AddArg() {
+  if (has_args_) {
+    *out_ += ",";
+  } else {
+    *out_ += "{";
+    has_args_ = true;
+  }
+  return out_;
+}
+
+bool JSONTraceExporter::ArgumentBuilder::ArgumentNameIsStripped(
+    const std::string& name) {
+  return !argument_name_filter_predicate_.is_null() &&
+         !argument_name_filter_predicate_.Run(name.c_str());
+}
+
+bool JSONTraceExporter::ArgumentBuilder::SkipBecauseStripped(
+    const std::string& name) {
+  if (strip_args_) {
+    has_args_ = true;
+    return true;
+  }
+  if (ArgumentNameIsStripped(name)) {
+    AddArg()->AppendF("\"%s\":\"__stripped__\"", name.c_str());
+    return true;
+  }
+  return false;
+}
+
+JSONTraceExporter::ScopedJSONTraceEventAppender::ScopedJSONTraceEventAppender(
+    JSONTraceExporter::StringBuffer* out,
+    JSONTraceExporter::ArgumentFilterPredicate argument_filter_predicate,
+    const char* name,
+    const char* categories,
+    int32_t phase,
+    int64_t timestamp,
+    int32_t pid,
+    int32_t tid)
+    : phase_(static_cast<char>(phase)),
+      added_args_(false),
+      out_(out),
+      event_name_(name),
+      category_group_name_(categories),
+      argument_filter_predicate_(std::move(argument_filter_predicate)) {
+  out_->AppendF("{\"pid\":%i,\"tid\":%i,\"ts\":%" PRId64
+                ",\"ph\":\"%c\",\"cat\":\"%s\",\"name\":",
+                pid, tid, timestamp, phase_, categories);
+  out_->EscapeJSONAndAppend(name);
+}
+
+JSONTraceExporter::ScopedJSONTraceEventAppender::ScopedJSONTraceEventAppender(
+    JSONTraceExporter::ScopedJSONTraceEventAppender&& move) {
+  out_ = move.out_;
+  phase_ = move.phase_;
+  argument_filter_predicate_ = std::move(move.argument_filter_predicate_);
+  // We null out the string so that the destructor knows not to append the
+  // closing brace for the json.
+  move.out_ = nullptr;
+}
+
+JSONTraceExporter::ScopedJSONTraceEventAppender::
+    ~ScopedJSONTraceEventAppender() {
+  if (out_) {
+    // TraceEventAnalyzer expects |args| to exist even if it's empty.
+    if (!added_args_) {
+      *out_ += ",\"args\":{}";
+    }
+    // Close out the starting bracket.
+    *out_ += "}";
+  }
+}
+
+void JSONTraceExporter::ScopedJSONTraceEventAppender::AddDuration(
+    int64_t duration) {
+  out_->AppendF(",\"dur\":%" PRId64, duration);
+}
+
+void JSONTraceExporter::ScopedJSONTraceEventAppender::AddThreadDuration(
+    int64_t thread_duration) {
+  out_->AppendF(",\"tdur\":%" PRId64, thread_duration);
+}
+
+void JSONTraceExporter::ScopedJSONTraceEventAppender::AddThreadTimestamp(
+    int64_t thread_timestamp) {
+  out_->AppendF(",\"tts\":%" PRId64, thread_timestamp);
+}
+
+void JSONTraceExporter::ScopedJSONTraceEventAppender::AddBindId(
+    uint64_t bind_id) {
+  out_->AppendF(",\"bind_id\":\"0x%" PRIx64 "\"",
+                static_cast<uint64_t>(bind_id));
+}
+
+void JSONTraceExporter::ScopedJSONTraceEventAppender::AddFlags(
+    uint32_t flags,
+    base::Optional<uint64_t> id,
+    const std::string& scope) {
+  if (flags & TRACE_EVENT_FLAG_ASYNC_TTS) {
+    *out_ += ",\"use_async_tts\":1";
+  }
+
+  // If |id| is set, print it out as a hex string so we don't loose any
+  // bits (it might be a 64-bit pointer).
+  unsigned int id_flags =
+      flags & (TRACE_EVENT_FLAG_HAS_ID | TRACE_EVENT_FLAG_HAS_LOCAL_ID |
+               TRACE_EVENT_FLAG_HAS_GLOBAL_ID);
+  if (id_flags) {
+    if (!scope.empty()) {
+      out_->AppendF(",\"scope\":\"%s\"", scope.c_str());
+    }
+
+    DCHECK(id);
+    switch (id_flags) {
+      case TRACE_EVENT_FLAG_HAS_ID:
+        out_->AppendF(",\"id\":\"0x%" PRIx64 "\"", static_cast<uint64_t>(*id));
+        break;
+
+      case TRACE_EVENT_FLAG_HAS_LOCAL_ID:
+        out_->AppendF(",\"id2\":{\"local\":\"0x%" PRIx64 "\"}",
+                      static_cast<uint64_t>(*id));
+        break;
+
+      case TRACE_EVENT_FLAG_HAS_GLOBAL_ID:
+        out_->AppendF(",\"id2\":{\"global\":\"0x%" PRIx64 "\"}",
+                      static_cast<uint64_t>(*id));
+        break;
+
+      default:
+        NOTREACHED() << "More than one of the ID flags are set";
+        break;
+    }
+  }
+
+  if (flags & TRACE_EVENT_FLAG_BIND_TO_ENCLOSING)
+    *out_ += ",\"bp\":\"e\"";
+
+  if (flags & TRACE_EVENT_FLAG_FLOW_IN)
+    *out_ += ",\"flow_in\":true";
+  if (flags & TRACE_EVENT_FLAG_FLOW_OUT)
+    *out_ += ",\"flow_out\":true";
+
+  // Instant events also output their scope.
+  if (phase_ == TRACE_EVENT_PHASE_INSTANT) {
+    char scope = '?';
+    switch (flags & TRACE_EVENT_FLAG_SCOPE_MASK) {
+      case TRACE_EVENT_SCOPE_GLOBAL:
+        scope = TRACE_EVENT_SCOPE_NAME_GLOBAL;
+        break;
+
+      case TRACE_EVENT_SCOPE_PROCESS:
+        scope = TRACE_EVENT_SCOPE_NAME_PROCESS;
+        break;
+
+      case TRACE_EVENT_SCOPE_THREAD:
+        scope = TRACE_EVENT_SCOPE_NAME_THREAD;
+        break;
+    }
+    out_->AppendF(",\"s\":\"%c\"", scope);
+  }
+}
+
+std::unique_ptr<JSONTraceExporter::ArgumentBuilder>
+JSONTraceExporter::ScopedJSONTraceEventAppender::BuildArgs() {
+  DCHECK(!added_args_);
+  added_args_ = true;
+  return std::make_unique<ArgumentBuilder>(
+      argument_filter_predicate_, event_name_, category_group_name_, out_);
+}
 }  // namespace tracing
diff --git a/services/tracing/perfetto/json_trace_exporter.h b/services/tracing/perfetto/json_trace_exporter.h
index 8db05e2..2b1878b 100644
--- a/services/tracing/perfetto/json_trace_exporter.h
+++ b/services/tracing/perfetto/json_trace_exporter.h
@@ -7,26 +7,26 @@
 
 #include <memory>
 #include <string>
+#include <unordered_map>
+#include <utility>
 #include <vector>
 
 #include "base/callback.h"
 #include "base/macros.h"
+#include "base/strings/stringprintf.h"
 #include "base/values.h"
 #include "third_party/perfetto/include/perfetto/tracing/core/trace_packet.h"
 
 namespace perfetto {
 namespace protos {
-class ChromeTracedValue;
+class ChromeLegacyJsonTrace;
+class ChromeMetadata;
+class TraceStats;
 }  // namespace protos
 }  // namespace perfetto
 
 namespace tracing {
 
-// Serializes the supplied proto into JSON, using the same
-// format as TracedValue::AppendAsTraceFormat.
-void AppendProtoDictAsJSON(std::string* out,
-                           const perfetto::protos::ChromeTracedValue& dict);
-
 // Converts proto-encoded trace data into the legacy JSON trace format.
 // Conversion happens on-the-fly as new trace packets are received.
 class JSONTraceExporter {
@@ -48,7 +48,8 @@
                                    base::DictionaryValue* metadata,
                                    bool has_more)>;
 
-  JSONTraceExporter(ArgumentFilterPredicate, OnTraceEventJSONCallback callback);
+  JSONTraceExporter(ArgumentFilterPredicate argument_filter_predicate,
+                    OnTraceEventJSONCallback callback);
   virtual ~JSONTraceExporter();
 
   // Called to notify the exporter of new trace packets. Will call the
@@ -63,15 +64,190 @@
     label_filter_ = label_filter;
   }
 
+ protected:
+  class StringBuffer {
+   public:
+    StringBuffer(OnTraceEventJSONCallback callback);
+    StringBuffer(const StringBuffer& copy) = delete;
+    StringBuffer(StringBuffer&& move);
+    ~StringBuffer();
+
+    StringBuffer& operator+=(const std::string& input);
+
+    StringBuffer& operator+=(std::string&& input);
+
+    StringBuffer& operator+=(const char* input);
+
+    std::string* mutable_out();
+    const std::string& out();
+
+    template <typename... Args>
+    void AppendF(const char* format, Args&&... args) {
+      MaybeRunCallback();
+      base::StringAppendF(&out_, format, std::forward<Args>(args)...);
+    }
+
+    void EscapeJSONAndAppend(const std::string& unescaped);
+
+    void Flush(base::DictionaryValue* metadata, bool has_more);
+
+   private:
+    // Depending on the size of the current output we might need to send a part
+    // of it back.
+    void MaybeRunCallback();
+
+    std::string out_;
+    OnTraceEventJSONCallback callback_;
+  };
+
+  class ArgumentBuilder {
+   public:
+    ArgumentBuilder(const ArgumentFilterPredicate& argument_filter_predicate,
+                    const char* name,
+                    const char* category_group_name,
+                    StringBuffer* out);
+    ~ArgumentBuilder();
+
+    // Takes an arg name, and returns nullptr if
+    //
+    // a) all args are being stripped
+    // b) if this arg name was stripped.
+    //
+    // If the StringBuffer pointer is valid then you should append a string that
+    // is properly formatted json for this arg value.
+    StringBuffer* MaybeAddArg(const std::string& name);
+
+   private:
+    StringBuffer* AddArg();
+    bool ArgumentNameIsStripped(const std::string& name);
+    bool SkipBecauseStripped(const std::string& name);
+
+    StringBuffer* out_;
+    bool strip_args_ = false;
+    bool has_args_ = false;
+    ArgumentNameFilterPredicate argument_name_filter_predicate_;
+  };
+
+  // Adds all required fields to |out| in proper JSON format. Only one
+  // ScopedJSONTraceEventAppender should exist per |out| string at a time,
+  // since the TraceEvent will not be finished until the
+  // ScopedJSONTraceEventAppender goes out of scope.
+  class ScopedJSONTraceEventAppender {
+   public:
+    // Only one reference should exist at a time. So moving is okay but copying
+    // is disallowed.
+    ScopedJSONTraceEventAppender(ScopedJSONTraceEventAppender&& move);
+    ScopedJSONTraceEventAppender(const ScopedJSONTraceEventAppender& copy) =
+        delete;
+
+    // Ensures that the JSON object is properly closed.
+    ~ScopedJSONTraceEventAppender();
+
+    // Optional traceEvent fields can also be set with the methods below. All
+    // methods should only be called once.
+
+    void AddDuration(int64_t duration);
+    void AddThreadDuration(int64_t thread_duration);
+    void AddThreadTimestamp(int64_t thread_timestamp);
+    void AddBindId(uint64_t bind_id);
+    // A set of bit flags for this trace event, along with a |scope|. |scope| is
+    // ignored if empty.
+    void AddFlags(uint32_t flags,
+                  base::Optional<uint64_t> id,
+                  const std::string& scope);
+
+    // Begins constructing the args sections, and finishes when ArgumentBuilder
+    // is destroyed. No other Add* function should be called until
+    // ArgumentBuilder goes out of scope.
+    //
+    // This should be used as follows.
+    // {
+    //   auto arg_builder = scoped_appender.BuildArgs();
+    //   for (const auto& arg : args) {
+    //     JSONTraceExporter::StringBuffer* maybe_arg =
+    //         arg_builder->MaybeAddArg(arg.name);
+    //     if (maybe_arg) {
+    //       // Then one of the following to add the value in |arg|.
+    //       *maybe_arg += "\"json_formatted_raw_value\"";
+    //       maybe_arg->AppendF("\"%d\"", arg.integer);
+    //       maybe_arg->EscapeJSONAndAppend("json_that will be : escaped");
+    //     }
+    //   }
+    // }
+    //
+    // IMPORTANT: ArgumentBuilder must be deconstructed before the
+    // ScopedJSONTraceEventAppender that created it is.
+    std::unique_ptr<ArgumentBuilder> BuildArgs();
+
+   private:
+    // Subclasses of JSONTraceExporter can create a new instance by calling
+    // AddTraceEvent().
+    ScopedJSONTraceEventAppender(
+        StringBuffer* out,
+        ArgumentFilterPredicate argument_filter_predicate,
+        const char* name,
+        const char* categories,
+        int32_t phase,
+        int64_t timestamp,
+        int32_t pid,
+        int32_t tid);
+    friend class JSONTraceExporter;
+
+    char phase_;
+    bool added_args_;
+    StringBuffer* out_;
+    const char* event_name_;
+    const char* category_group_name_;
+    ArgumentFilterPredicate argument_filter_predicate_;
+  };
+
+  // Subclasses implement this to add data from |packets| to the JSON output.
+  // For example they can add traceEvents through AddTraceEvent(), or add
+  // metadata through AddChromeMetadata().
+  virtual void ProcessPackets(
+      const std::vector<perfetto::TracePacket>& packets) = 0;
+
+  // If true then all trace events should be skipped. AddTraceEvent should not
+  // be called.
+  bool ShouldOutputTraceEvents() const;
+
+  // Used for passing legacy JSON traces. This will update either the
+  // traceEvents directly if needed by calling AddJSONTraceEvent or will store
+  // the system trace information to be appended after the packets have been
+  // processed.
+  void AddChromeLegacyJSONTrace(
+      const perfetto::protos::ChromeLegacyJsonTrace& json_trace);
+  // Adds system Ftrace data to be appended to the trace JSON after all the
+  // traceEvents have been processed.
+  void AddLegacyFtrace(const std::string& legacy_ftrace_output);
+  // Used to append ChromeMetadata to the trace. Can be called at any point.
+  // Metadata is always appended after all packets have been processed.
+  void AddChromeMetadata(const perfetto::protos::ChromeMetadata& metadata);
+  // Writes (overwriting if already set) the perfetto trace stats to the
+  // metadata that will be appended after all packets have been processed.
+  void SetTraceStatsMetadata(const perfetto::protos::TraceStats& stats);
+
+  // Used when sub-classes are adding a new trace event to the traceEvents
+  // array. This will ensure that only proper json is appended.
+  ScopedJSONTraceEventAppender AddTraceEvent(const char* name,
+                                             const char* categories,
+                                             int32_t phase,
+                                             int64_t timestamp,
+                                             int32_t pid,
+                                             int32_t tid);
+
  private:
-  OnTraceEventJSONCallback json_callback_;
-  bool has_output_json_preamble_ = false;
+  // Used by the implementation to ensure the proper separators exist between
+  // trace events in the array.
+  StringBuffer* AddJSONTraceEvent();
+
+  StringBuffer out_;
   bool has_output_first_event_ = false;
-  std::unique_ptr<base::DictionaryValue> metadata_;
-  std::string legacy_system_ftrace_output_;
+  bool has_output_json_preamble_ = false;
   std::string legacy_system_trace_events_;
   std::string label_filter_;
-
+  std::string legacy_system_ftrace_output_;
+  std::unique_ptr<base::DictionaryValue> metadata_;
   ArgumentFilterPredicate argument_filter_predicate_;
 
   DISALLOW_COPY_AND_ASSIGN(JSONTraceExporter);
diff --git a/services/tracing/perfetto/json_trace_exporter_unittest.cc b/services/tracing/perfetto/json_trace_exporter_unittest.cc
index 52f24b1..da512a135 100644
--- a/services/tracing/perfetto/json_trace_exporter_unittest.cc
+++ b/services/tracing/perfetto/json_trace_exporter_unittest.cc
@@ -14,15 +14,15 @@
 #include "base/message_loop/message_loop.h"
 #include "base/run_loop.h"
 #include "base/strings/pattern.h"
+#include "base/strings/stringprintf.h"
 #include "base/test/trace_event_analyzer.h"
 #include "base/trace_event/trace_event.h"
 #include "base/values.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
 #include "services/tracing/public/mojom/perfetto_service.mojom.h"
+#include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/perfetto/include/perfetto/tracing/core/trace_config.h"
 #include "third_party/perfetto/include/perfetto/tracing/core/trace_packet.h"
-#include "third_party/perfetto/protos/perfetto/trace/chrome/chrome_trace_event.pbzero.h"
+#include "third_party/perfetto/protos/perfetto/trace/chrome/chrome_trace_event.pb.h"
 #include "third_party/perfetto/protos/perfetto/trace/trace_packet.pb.h"
 
 using base::trace_event::TraceLog;
@@ -31,55 +31,177 @@
 
 namespace {
 
-bool IsArgNameWhitelisted(const char* arg_name) {
-  return base::MatchPattern(arg_name, "granular_arg_whitelisted");
-}
+struct ArgValue {
+  ArgValue(int64_t val) : int_val(val), type(ArgType::kInt) {}
+  ArgValue(uint64_t val) : uint_val(val), type(ArgType::kUint) {}
+  ArgValue(double val) : double_val(val), type(ArgType::kDouble) {}
+  ArgValue(std::string val) : string_val(val), type(ArgType::kString) {}
+  ArgValue(bool val) : bool_val(val), type(ArgType::kBool) {}
+  ArgValue(base::Value* val) : value_val(val), type(ArgType::kValue) {}
 
-bool IsTraceEventArgsWhitelisted(
-    const char* category_group_name,
-    const char* event_name,
-    base::trace_event::ArgumentNameFilterPredicate* arg_filter) {
-  if (base::MatchPattern(category_group_name, "toplevel") &&
-      base::MatchPattern(event_name, "*")) {
-    return true;
+  int64_t int_val;
+  uint64_t uint_val;
+  double double_val;
+  std::string string_val;
+  bool bool_val;
+  base::Value* value_val;
+  enum class ArgType { kInt, kUint, kDouble, kString, kBool, kValue };
+  ArgType type;
+};
+
+struct FakeTraceInfo {
+  FakeTraceInfo(const std::string& e,
+                const std::string& c,
+                char p,
+                int64_t t,
+                int32_t pi,
+                int32_t ti)
+      : event_name(e),
+        category_group_names(c),
+        phase(p),
+        timestamp(t),
+        pid(pi),
+        tid(ti) {}
+
+  // Required values.
+  std::string event_name;
+  std::string category_group_names;
+  char phase;
+  int64_t timestamp;
+  int32_t pid;
+  int32_t tid;
+
+  // Optional values.
+  base::Optional<int64_t> duration;
+  base::Optional<int64_t> thread_duration;
+  base::Optional<int64_t> thread_timestamp;
+  base::Optional<int64_t> bind_id;
+  base::Optional<int32_t> flags;
+  base::Optional<int64_t> id;
+  base::Optional<std::string> scope;
+  std::vector<std::pair<std::string, ArgValue>> args;
+};
+
+class TestJSONTraceExporter : public JSONTraceExporter {
+ public:
+  TestJSONTraceExporter(ArgumentFilterPredicate argument_filter_predicate,
+                        OnTraceEventJSONCallback callback)
+      : JSONTraceExporter(std::move(argument_filter_predicate),
+                          std::move(callback)) {}
+  ~TestJSONTraceExporter() override = default;
+
+  int process_packets_calls() const { return process_packets_calls_; }
+
+  void SetFakeTraceEvents(const std::vector<FakeTraceInfo>& values) {
+    infos_ = values;
   }
 
-  if (base::MatchPattern(category_group_name, "benchmark") &&
-      base::MatchPattern(event_name, "granularly_whitelisted")) {
-    *arg_filter = base::BindRepeating(&IsArgNameWhitelisted);
-    return true;
+  std::vector<perfetto::protos::ChromeLegacyJsonTrace> json_traces;
+  std::vector<std::string> legacy_ftrace_output;
+  std::vector<perfetto::protos::ChromeMetadata> metadata;
+  std::vector<perfetto::protos::TraceStats> stats;
+
+ protected:
+  void ProcessPackets(
+      const std::vector<perfetto::TracePacket>& packets) override {
+    ++process_packets_calls_;
+    DCHECK(packets.size() == infos_.size())
+        << " different sizes of packets versus expected behaviour test set up "
+        << "error.";
+    for (const auto& event : infos_) {
+      auto scoped_builder = AddTraceEvent(
+          event.event_name.c_str(), event.category_group_names.c_str(),
+          event.phase, event.timestamp, event.pid, event.tid);
+      if (event.duration) {
+        scoped_builder.AddDuration(*event.duration);
+      }
+      if (event.thread_duration) {
+        scoped_builder.AddThreadDuration(*event.thread_duration);
+      }
+      if (event.thread_timestamp) {
+        scoped_builder.AddThreadTimestamp(*event.thread_timestamp);
+      }
+      if (event.bind_id) {
+        scoped_builder.AddBindId(*event.bind_id);
+      }
+      if (event.flags) {
+        scoped_builder.AddFlags(*event.flags, event.id.value_or(0),
+                                event.scope.value_or(""));
+      }
+      if (!event.args.empty()) {
+        auto args_builder = scoped_builder.BuildArgs();
+        for (const auto& arg : event.args) {
+          const std::string& name = std::get<0>(arg);
+          const ArgValue val = std::get<1>(arg);
+          auto* maybe_arg = args_builder->MaybeAddArg(name);
+          std::string temp;
+          if (maybe_arg) {
+            switch (val.type) {
+              case ArgValue::ArgType::kInt:
+                *maybe_arg += std::to_string(val.int_val);
+                break;
+              case ArgValue::ArgType::kUint:
+                *maybe_arg += std::to_string(val.uint_val);
+                break;
+              case ArgValue::ArgType::kDouble:
+                *maybe_arg += std::to_string(val.double_val);
+                break;
+              case ArgValue::ArgType::kString:
+                *maybe_arg += val.string_val;
+                break;
+              case ArgValue::ArgType::kBool:
+                *maybe_arg += val.bool_val ? "true" : "false";
+                break;
+              case ArgValue::ArgType::kValue:
+                temp.clear();
+                base::JSONWriter::Write(*val.value_val, &temp);
+                *maybe_arg += temp;
+                break;
+            }
+          }
+        }
+      }
+    }
+
+    for (const auto& value : json_traces) {
+      AddChromeLegacyJSONTrace(value);
+    }
+    for (const auto& value : legacy_ftrace_output) {
+      AddLegacyFtrace(value);
+    }
+    for (const auto& value : metadata) {
+      AddChromeMetadata(value);
+    }
+    for (const auto& value : stats) {
+      SetTraceStatsMetadata(value);
+    }
   }
 
-  return false;
-}
+ private:
+  int process_packets_calls_ = 0;
+  std::vector<FakeTraceInfo> infos_;
+};
 
 }  // namespace
 
-class JSONTraceExporterTest : public testing::Test {
+class JsonTraceExporterTest : public testing::Test {
  public:
-  void SetUp() override {
-    json_trace_exporter_.reset(new JSONTraceExporter(
-        JSONTraceExporter::ArgumentFilterPredicate(),
-        base::BindRepeating(&JSONTraceExporterTest::OnTraceEventJSON,
-                            base::Unretained(this))));
-  }
-
-  void TearDown() override {
-    json_trace_exporter_.reset();
-  }
-
-  void EnableArgumentFilter() {
-    json_trace_exporter_->SetArgumentFilterForTesting(
-        base::BindRepeating(&IsTraceEventArgsWhitelisted));
-  }
+  JsonTraceExporterTest()
+      : json_trace_exporter_(new TestJSONTraceExporter(
+            JSONTraceExporter::ArgumentFilterPredicate(),
+            base::BindRepeating(&JsonTraceExporterTest::OnTraceEventJSON,
+                                base::Unretained(this)))) {}
 
   void OnTraceEventJSON(const std::string& json,
                         base::DictionaryValue* metadata,
                         bool has_more) {
-    CHECK(!has_more);
-
-    parsed_trace_data_ =
-        base::DictionaryValue::From(base::JSONReader::ReadDeprecated(json));
+    unparsed_trace_data_ += json;
+    unparsed_trace_data_sequence_.push_back(json);
+    if (has_more) {
+      return;
+    }
+    parsed_trace_data_ = base::DictionaryValue::From(
+        base::JSONReader::ReadDeprecated(unparsed_trace_data_));
     EXPECT_TRUE(parsed_trace_data_);
     if (!parsed_trace_data_) {
       LOG(ERROR) << "Couldn't parse json: \n" << json;
@@ -96,613 +218,443 @@
     EXPECT_TRUE(trace_analyzer_);
   }
 
-  void SetTestPacketBasicData(
-      perfetto::protos::ChromeTraceEvent* new_trace_event) {
-    new_trace_event->set_name("foo_name");
-    new_trace_event->set_timestamp(42);
-    new_trace_event->set_flags(TRACE_EVENT_FLAG_HAS_GLOBAL_ID |
-                               TRACE_EVENT_FLAG_FLOW_OUT);
-
-    new_trace_event->set_process_id(2);
-    new_trace_event->set_thread_id(3);
-    new_trace_event->set_category_group_name("cat_name");
-    new_trace_event->set_phase(TRACE_EVENT_PHASE_COMPLETE);
-    new_trace_event->set_duration(4);
-    new_trace_event->set_thread_duration(5);
-    new_trace_event->set_thread_timestamp(6);
-    new_trace_event->set_id(7);
-    new_trace_event->set_bind_id(8);
-
-    std::string scope;
-    scope += TRACE_EVENT_SCOPE_NAME_GLOBAL;
-    new_trace_event->set_scope(scope);
-  }
-
-  void FinalizePacket(const perfetto::protos::TracePacket& trace_packet_proto) {
-    perfetto::TracePacket trace_packet;
-    std::string ser_buf = trace_packet_proto.SerializeAsString();
-    trace_packet.AddSlice(&ser_buf[0], ser_buf.size());
-
-    std::vector<perfetto::TracePacket> packets;
-    packets.emplace_back(std::move(trace_packet));
-
-    json_trace_exporter_->OnTraceData(std::move(packets), false);
-  }
-
-  const trace_analyzer::TraceEvent* ValidateAndGetBasicTestPacket() {
-    const trace_analyzer::TraceEvent* trace_event =
-        trace_analyzer_->FindFirstOf(
-            trace_analyzer::Query(trace_analyzer::Query::EVENT_NAME) ==
-            trace_analyzer::Query::String("foo_name"));
-    EXPECT_TRUE(trace_event);
-
-    EXPECT_EQ(2, trace_event->thread.process_id);
-    EXPECT_EQ(3, trace_event->thread.thread_id);
-    EXPECT_EQ(42, trace_event->timestamp);
-    EXPECT_EQ('X', trace_event->phase);
-    EXPECT_EQ("foo_name", trace_event->name);
-    EXPECT_EQ("cat_name", trace_event->category);
-    EXPECT_EQ(4, trace_event->duration);
-    EXPECT_EQ(5, trace_event->thread_duration);
-    EXPECT_EQ(6, trace_event->thread_timestamp);
-    EXPECT_EQ("g", trace_event->scope);
-    EXPECT_EQ("0x7", trace_event->global_id2);
-    EXPECT_EQ("0x8", trace_event->bind_id);
-    EXPECT_TRUE(trace_event->flow_out);
-
-    return trace_event;
-  }
-
-  trace_analyzer::TraceAnalyzer* trace_analyzer() {
-    return trace_analyzer_.get();
-  }
-
-  const base::DictionaryValue* parsed_trace_data() const {
-    return parsed_trace_data_.get();
-  }
-
- private:
-  std::unique_ptr<JSONTraceExporter> json_trace_exporter_;
-  std::unique_ptr<base::MessageLoop> message_loop_;
+  std::string unparsed_trace_data_;
+  std::vector<std::string> unparsed_trace_data_sequence_;
+  std::unique_ptr<TestJSONTraceExporter> json_trace_exporter_;
   std::unique_ptr<trace_analyzer::TraceAnalyzer> trace_analyzer_;
   std::unique_ptr<base::DictionaryValue> parsed_trace_data_;
 };
 
-TEST_F(JSONTraceExporterTest, TestMetadata) {
-  perfetto::protos::TracePacket trace_packet_proto;
+TEST_F(JsonTraceExporterTest, TestNoTraceEvents) {
+  json_trace_exporter_->OnTraceData(std::vector<perfetto::TracePacket>(),
+                                    false);
+  EXPECT_EQ("{\"traceEvents\":[]}", unparsed_trace_data_);
+}
 
-  {
-    auto* new_metadata =
-        trace_packet_proto.mutable_chrome_events()->add_metadata();
-    new_metadata->set_name("int_metadata");
-    new_metadata->set_int_value(42);
-  }
-
-  {
-    auto* new_metadata =
-        trace_packet_proto.mutable_chrome_events()->add_metadata();
-    new_metadata->set_name("string_metadata");
-    new_metadata->set_string_value("met_val");
-  }
-
-  {
-    auto* new_metadata =
-        trace_packet_proto.mutable_chrome_events()->add_metadata();
-    new_metadata->set_name("bool_metadata");
-    new_metadata->set_bool_value(true);
-  }
-
-  {
-    auto* new_metadata =
-        trace_packet_proto.mutable_chrome_events()->add_metadata();
-    new_metadata->set_name("dict_metadata");
-    new_metadata->set_json_value("{\"child_dict\": \"foo\"}");
-  }
-
-  FinalizePacket(trace_packet_proto);
-
-  auto* metadata = parsed_trace_data()->FindKey("metadata");
-  EXPECT_TRUE(metadata);
-  EXPECT_EQ(metadata->FindKey("int_metadata")->GetInt(), 42);
-  EXPECT_EQ(metadata->FindKey("string_metadata")->GetString(), "met_val");
-  EXPECT_EQ(metadata->FindKey("bool_metadata")->GetBool(), true);
+TEST_F(JsonTraceExporterTest, TestBasic) {
+  std::vector<FakeTraceInfo> infos = {FakeTraceInfo(
+      "name", "cat", 'B', /* timestamp = */ 1, /* pid = */ 2, /* tid = */ 3)};
+  json_trace_exporter_->SetFakeTraceEvents(infos);
+  json_trace_exporter_->OnTraceData(
+      std::vector<perfetto::TracePacket>(infos.size()), false);
   EXPECT_EQ(
-      metadata->FindKey("dict_metadata")->FindKey("child_dict")->GetString(),
-      "foo");
-}
-
-TEST_F(JSONTraceExporterTest, TestBasicEvent) {
-  perfetto::protos::TracePacket trace_packet_proto;
-  auto* new_trace_event =
-      trace_packet_proto.mutable_chrome_events()->add_trace_events();
-  SetTestPacketBasicData(new_trace_event);
-  FinalizePacket(trace_packet_proto);
-
-  ValidateAndGetBasicTestPacket();
-}
-
-TEST_F(JSONTraceExporterTest, TestStringTable) {
-  perfetto::protos::TracePacket trace_packet_proto;
-  auto* new_trace_event =
-      trace_packet_proto.mutable_chrome_events()->add_trace_events();
-
-  {
-    auto* string_table_entry =
-        trace_packet_proto.mutable_chrome_events()->add_string_table();
-    string_table_entry->set_index(1);
-    string_table_entry->set_value("foo_name");
-  }
-
-  {
-    auto* string_table_entry =
-        trace_packet_proto.mutable_chrome_events()->add_string_table();
-    string_table_entry->set_index(2);
-    string_table_entry->set_value("foo_cat");
-  }
-
-  {
-    auto* string_table_entry =
-        trace_packet_proto.mutable_chrome_events()->add_string_table();
-    string_table_entry->set_index(3);
-    string_table_entry->set_value("foo_arg");
-  }
-
-  new_trace_event->set_name_index(1);
-  new_trace_event->set_category_group_name_index(2);
-
-  auto* new_arg = new_trace_event->add_args();
-  new_arg->set_name_index(3);
-  new_arg->set_bool_value(true);
-
-  FinalizePacket(trace_packet_proto);
-
-  auto* trace_event = trace_analyzer()->FindFirstOf(
+      "{\"traceEvents\":[{\"pid\":2,\"tid\":3,\"ts\":1,\"ph\":\"B\","
+      "\"cat\":\"cat\",\"name\":\"name\",\"args\":{}}]}",
+      unparsed_trace_data_);
+  const trace_analyzer::TraceEvent* trace_event = trace_analyzer_->FindFirstOf(
       trace_analyzer::Query(trace_analyzer::Query::EVENT_NAME) ==
-      trace_analyzer::Query::String("foo_name"));
+      trace_analyzer::Query::String("name"));
   EXPECT_TRUE(trace_event);
-
-  EXPECT_EQ("foo_name", trace_event->name);
-  EXPECT_EQ("foo_cat", trace_event->category);
-
-  EXPECT_TRUE(trace_event->GetKnownArgAsBool("foo_arg"));
+  EXPECT_EQ(2, trace_event->thread.process_id);
+  EXPECT_EQ(3, trace_event->thread.thread_id);
+  EXPECT_EQ(1, trace_event->timestamp);
+  EXPECT_EQ('B', trace_event->phase);
+  EXPECT_EQ("name", trace_event->name);
+  EXPECT_EQ("cat", trace_event->category);
 }
 
-TEST_F(JSONTraceExporterTest, TestEventWithBoolArgs) {
-  perfetto::protos::TracePacket trace_packet_proto;
-  auto* new_trace_event =
-      trace_packet_proto.mutable_chrome_events()->add_trace_events();
-  SetTestPacketBasicData(new_trace_event);
-
-  {
-    auto* new_arg = new_trace_event->add_args();
-    new_arg->set_name("foo1");
-    new_arg->set_bool_value(true);
-  }
-
-  {
-    auto* new_arg = new_trace_event->add_args();
-    new_arg->set_name("foo2");
-    new_arg->set_bool_value(false);
-  }
-
-  FinalizePacket(trace_packet_proto);
-
-  auto* trace_event = ValidateAndGetBasicTestPacket();
-
-  EXPECT_TRUE(trace_event->GetKnownArgAsBool("foo1"));
-  EXPECT_FALSE(trace_event->GetKnownArgAsBool("foo2"));
-}
-
-TEST_F(JSONTraceExporterTest, TestEventWithUintArgs) {
-  perfetto::protos::TracePacket trace_packet_proto;
-  auto* new_trace_event =
-      trace_packet_proto.mutable_chrome_events()->add_trace_events();
-  SetTestPacketBasicData(new_trace_event);
-
-  {
-    auto* new_arg = new_trace_event->add_args();
-    new_arg->set_name("foo1");
-    new_arg->set_uint_value(1);
-  }
-
-  {
-    auto* new_arg = new_trace_event->add_args();
-    new_arg->set_name("foo2");
-    new_arg->set_uint_value(2);
-  }
-
-  FinalizePacket(trace_packet_proto);
-
-  auto* trace_event = ValidateAndGetBasicTestPacket();
-
-  EXPECT_EQ(1, trace_event->GetKnownArgAsDouble("foo1"));
-  EXPECT_EQ(2, trace_event->GetKnownArgAsDouble("foo2"));
-}
-
-TEST_F(JSONTraceExporterTest, TestEventWithIntArgs) {
-  perfetto::protos::TracePacket trace_packet_proto;
-  auto* new_trace_event =
-      trace_packet_proto.mutable_chrome_events()->add_trace_events();
-  SetTestPacketBasicData(new_trace_event);
-
-  {
-    auto* new_arg = new_trace_event->add_args();
-    new_arg->set_name("foo1");
-    new_arg->set_int_value(1);
-  }
-
-  {
-    auto* new_arg = new_trace_event->add_args();
-    new_arg->set_name("foo2");
-    new_arg->set_int_value(2);
-  }
-
-  FinalizePacket(trace_packet_proto);
-
-  auto* trace_event = ValidateAndGetBasicTestPacket();
-
-  EXPECT_EQ(1, trace_event->GetKnownArgAsDouble("foo1"));
-  EXPECT_EQ(2, trace_event->GetKnownArgAsDouble("foo2"));
-}
-
-TEST_F(JSONTraceExporterTest, TestEventWithDoubleArgs) {
-  perfetto::protos::TracePacket trace_packet_proto;
-  auto* new_trace_event =
-      trace_packet_proto.mutable_chrome_events()->add_trace_events();
-  SetTestPacketBasicData(new_trace_event);
-
-  {
-    auto* new_arg = new_trace_event->add_args();
-    new_arg->set_name("foo1");
-    new_arg->set_double_value(1.0);
-  }
-
-  {
-    auto* new_arg = new_trace_event->add_args();
-    new_arg->set_name("foo2");
-    new_arg->set_double_value(2.0);
-  }
-
-  FinalizePacket(trace_packet_proto);
-
-  auto* trace_event = ValidateAndGetBasicTestPacket();
-
-  EXPECT_EQ(1.0, trace_event->GetKnownArgAsDouble("foo1"));
-  EXPECT_EQ(2.0, trace_event->GetKnownArgAsDouble("foo2"));
-}
-
-TEST_F(JSONTraceExporterTest, TestEventWithStringArgs) {
-  perfetto::protos::TracePacket trace_packet_proto;
-  auto* new_trace_event =
-      trace_packet_proto.mutable_chrome_events()->add_trace_events();
-  SetTestPacketBasicData(new_trace_event);
-
-  {
-    auto* new_arg = new_trace_event->add_args();
-    new_arg->set_name("foo1");
-    new_arg->set_string_value("bar1");
-  }
-
-  {
-    auto* new_arg = new_trace_event->add_args();
-    new_arg->set_name("foo2");
-    new_arg->set_string_value("bar2");
-  }
-
-  FinalizePacket(trace_packet_proto);
-
-  auto* trace_event = ValidateAndGetBasicTestPacket();
-
-  EXPECT_EQ("bar1", trace_event->GetKnownArgAsString("foo1"));
-  EXPECT_EQ("bar2", trace_event->GetKnownArgAsString("foo2"));
-}
-
-TEST_F(JSONTraceExporterTest, TestEventWithPointerArgs) {
-  perfetto::protos::TracePacket trace_packet_proto;
-  auto* new_trace_event =
-      trace_packet_proto.mutable_chrome_events()->add_trace_events();
-  SetTestPacketBasicData(new_trace_event);
-
-  {
-    auto* new_arg = new_trace_event->add_args();
-    new_arg->set_name("foo1");
-    new_arg->set_pointer_value(0x1);
-  }
-
-  {
-    auto* new_arg = new_trace_event->add_args();
-    new_arg->set_name("foo2");
-    new_arg->set_pointer_value(0x2);
-  }
-
-  FinalizePacket(trace_packet_proto);
-
-  auto* trace_event = ValidateAndGetBasicTestPacket();
-
-  EXPECT_EQ("0x1", trace_event->GetKnownArgAsString("foo1"));
-  EXPECT_EQ("0x2", trace_event->GetKnownArgAsString("foo2"));
-}
-
-TEST_F(JSONTraceExporterTest, TestEventWithConvertableArgs) {
-  perfetto::protos::TracePacket trace_packet_proto;
-  auto* new_trace_event =
-      trace_packet_proto.mutable_chrome_events()->add_trace_events();
-  SetTestPacketBasicData(new_trace_event);
-
-  {
-    auto* new_arg = new_trace_event->add_args();
-    new_arg->set_name("foo1");
-    new_arg->set_json_value("\"conv_value1\"");
-  }
-
-  {
-    auto* new_arg = new_trace_event->add_args();
-    new_arg->set_name("foo2");
-    new_arg->set_json_value("\"conv_value2\"");
-  }
-
-  FinalizePacket(trace_packet_proto);
-
-  auto* trace_event = ValidateAndGetBasicTestPacket();
-
-  EXPECT_EQ("conv_value1", trace_event->GetKnownArgAsString("foo1"));
-  EXPECT_EQ("conv_value2", trace_event->GetKnownArgAsString("foo2"));
-}
-
-TEST_F(JSONTraceExporterTest, TestEventWithTracedValueArg) {
-  perfetto::protos::TracePacket trace_packet_proto;
-  auto* new_trace_event =
-      trace_packet_proto.mutable_chrome_events()->add_trace_events();
-  SetTestPacketBasicData(new_trace_event);
-
-  auto* new_arg = new_trace_event->add_args();
-  new_arg->set_name("foo1");
-  auto* traced_value = new_arg->mutable_traced_value();
-  traced_value->add_dict_keys("bool");
-  traced_value->add_dict_values()->set_bool_value(true);
-
-  FinalizePacket(trace_packet_proto);
-
-  auto* trace_event = ValidateAndGetBasicTestPacket();
-
-  auto arg_value = trace_event->GetKnownArgAsValue("foo1");
-  EXPECT_EQ(true, arg_value->FindKey("bool")->GetBool());
-}
-
-TEST_F(JSONTraceExporterTest, TracedValueFlatDictionary) {
-  perfetto::protos::ChromeTracedValue traced_value;
-
-  {
-    traced_value.add_dict_keys("bool");
-    traced_value.add_dict_values()->set_bool_value(true);
-  }
-
-  {
-    traced_value.add_dict_keys("double");
-    traced_value.add_dict_values()->set_double_value(8.0);
-  }
-
-  {
-    traced_value.add_dict_keys("int");
-    traced_value.add_dict_values()->set_int_value(2014);
-  }
-
-  {
-    traced_value.add_dict_keys("string");
-    traced_value.add_dict_values()->set_string_value("bar");
-  }
-
-  std::string json;
-  AppendProtoDictAsJSON(&json, traced_value);
-
-  EXPECT_EQ("{\"bool\":true,\"double\":8.0,\"int\":2014,\"string\":\"bar\"}",
-            json);
-}
-
-TEST_F(JSONTraceExporterTest, TracedValueHierarchy) {
-  perfetto::protos::ChromeTracedValue traced_value;
-
-  {
-    traced_value.add_dict_keys("a1");
-    auto* a1_array = traced_value.add_dict_values();
-    a1_array->set_nested_type(perfetto::protos::ChromeTracedValue::ARRAY);
-
-    a1_array->add_array_values()->set_int_value(1);
-    a1_array->add_array_values()->set_bool_value(true);
-
-    auto* sub_dict = a1_array->add_array_values();
-    sub_dict->set_nested_type(perfetto::protos::ChromeTracedValue::DICT);
-    sub_dict->add_dict_keys("i2");
-    sub_dict->add_dict_values()->set_int_value(3);
-  }
-
-  {
-    traced_value.add_dict_keys("b0");
-    traced_value.add_dict_values()->set_bool_value(true);
-  }
-
-  {
-    traced_value.add_dict_keys("d0");
-    traced_value.add_dict_values()->set_double_value(6.0);
-  }
-
-  {
-    traced_value.add_dict_keys("a1");
-    auto* dict1_subdict = traced_value.add_dict_values();
-    dict1_subdict->set_nested_type(perfetto::protos::ChromeTracedValue::DICT);
-
-    dict1_subdict->add_dict_keys("dict2");
-    auto* dict2_sub_sub_dict = dict1_subdict->add_dict_values();
-    dict2_sub_sub_dict->set_nested_type(
-        perfetto::protos::ChromeTracedValue::DICT);
-
-    dict2_sub_sub_dict->add_dict_keys("b2");
-    dict2_sub_sub_dict->add_dict_values()->set_bool_value(true);
-
-    dict1_subdict->add_dict_keys("i1");
-    dict1_subdict->add_dict_values()->set_int_value(2014);
-
-    dict1_subdict->add_dict_keys("s1");
-    dict1_subdict->add_dict_values()->set_string_value("foo");
-  }
-
-  {
-    traced_value.add_dict_keys("i0");
-    traced_value.add_dict_values()->set_int_value(2014);
-  }
-
-  {
-    traced_value.add_dict_keys("s0");
-    traced_value.add_dict_values()->set_string_value("foo");
-  }
-
-  std::string json;
-  AppendProtoDictAsJSON(&json, traced_value);
-
+TEST_F(JsonTraceExporterTest, TestAllTraceEventButFlagsAndArgs) {
+  // Duration is only parsed if the phase is correct in this case 'X'.
+  std::vector<FakeTraceInfo> infos = {FakeTraceInfo("name_all", "cat_all", 'X',
+                                                    /* timestamp = */ 1,
+                                                    /* pid = */ 2,
+                                                    /* tid = */ 3)};
+  infos[0].duration = 4;
+  infos[0].thread_duration = 5;
+  infos[0].thread_timestamp = 6;
+  infos[0].bind_id = 7;
+  json_trace_exporter_->SetFakeTraceEvents(infos);
+  json_trace_exporter_->OnTraceData(
+      std::vector<perfetto::TracePacket>(infos.size()), false);
   EXPECT_EQ(
-      "{\"a1\":[1,true,{\"i2\":3}],\"b0\":true,\"d0\":6.0,\"a1\":{\"dict2\":{"
-      "\"b2\":true},\"i1\":2014,\"s1\":\"foo\"},\"i0\":2014,\"s0\":\"foo\"}",
-      json);
+      "{\"traceEvents\":[{\"pid\":2,\"tid\":3,\"ts\":1,\"ph\":\"X\","
+      "\"cat\":\"cat_all\",\"name\":\"name_all\",\"dur\":4,\"tdur\":5,"
+      "\"tts\":6,\"bind_id\":\"0x7\",\"args\":{}}]}",
+      unparsed_trace_data_);
+  const trace_analyzer::TraceEvent* trace_event = trace_analyzer_->FindFirstOf(
+      trace_analyzer::Query(trace_analyzer::Query::EVENT_NAME) ==
+      trace_analyzer::Query::String("name_all"));
+  EXPECT_TRUE(trace_event);
+  EXPECT_EQ(2, trace_event->thread.process_id);
+  EXPECT_EQ(3, trace_event->thread.thread_id);
+  EXPECT_EQ(1, trace_event->timestamp);
+  EXPECT_EQ('X', trace_event->phase);
+  EXPECT_EQ("name_all", trace_event->name);
+  EXPECT_EQ("cat_all", trace_event->category);
+  EXPECT_EQ(4, trace_event->duration);
+  EXPECT_EQ(5, trace_event->thread_duration);
+  EXPECT_EQ(6, trace_event->thread_timestamp);
+  EXPECT_EQ("0x7", trace_event->bind_id);
 }
 
-TEST_F(JSONTraceExporterTest, TestLegacyUserTrace) {
-  perfetto::protos::TracePacket trace_packet_proto;
-
-  auto* new_trace_event =
-      trace_packet_proto.mutable_chrome_events()->add_trace_events();
-  SetTestPacketBasicData(new_trace_event);
-
-  auto* json_trace =
-      trace_packet_proto.mutable_chrome_events()->add_legacy_json_trace();
-  json_trace->set_type(perfetto::protos::ChromeLegacyJsonTrace::USER_TRACE);
-  json_trace->set_data(
-      "{\"pid\":10,\"tid\":11,\"ts\":23,\"ph\":\"I\""
-      ",\"cat\":\"cat_name2\",\"name\":\"bar_name\""
-      ",\"id2\":{\"global\":\"0x5\"},\"args\":{}}");
-
-  FinalizePacket(trace_packet_proto);
-
-  ValidateAndGetBasicTestPacket();
-
-  const trace_analyzer::TraceEvent* trace_event = trace_analyzer()->FindFirstOf(
+TEST_F(JsonTraceExporterTest, TestAddFlagsAllButIdFlagsAndPhaseScope) {
+  std::vector<FakeTraceInfo> infos = {FakeTraceInfo(
+      "name", "cat", 'I', /* timestamp = */ 1, /* pid = */ 2, /* tid = */ 3)};
+  infos[0].flags = TRACE_EVENT_FLAG_ASYNC_TTS |
+                   TRACE_EVENT_FLAG_BIND_TO_ENCLOSING |
+                   TRACE_EVENT_FLAG_FLOW_IN | TRACE_EVENT_FLAG_FLOW_OUT |
+                   TRACE_EVENT_SCOPE_GLOBAL;
+  json_trace_exporter_->SetFakeTraceEvents(infos);
+  json_trace_exporter_->OnTraceData(
+      std::vector<perfetto::TracePacket>(infos.size()), false);
+  EXPECT_EQ(
+      "{\"traceEvents\":[{\"pid\":2,\"tid\":3,\"ts\":1,\"ph\":\"I\","
+      "\"cat\":\"cat\",\"name\":\"name\",\"use_async_tts\":1,\"bp\":\"e\","
+      "\"flow_in\":true,\"flow_out\":true,\"s\":\"g\",\"args\":{}}]}",
+      unparsed_trace_data_);
+  const trace_analyzer::TraceEvent* trace_event = trace_analyzer_->FindFirstOf(
       trace_analyzer::Query(trace_analyzer::Query::EVENT_NAME) ==
-      trace_analyzer::Query::String("bar_name"));
+      trace_analyzer::Query::String("name"));
   EXPECT_TRUE(trace_event);
+  EXPECT_TRUE(trace_event->flow_in);
+  EXPECT_TRUE(trace_event->flow_out);
+}
 
-  EXPECT_EQ(10, trace_event->thread.process_id);
-  EXPECT_EQ(11, trace_event->thread.thread_id);
-  EXPECT_EQ(23, trace_event->timestamp);
+TEST_F(JsonTraceExporterTest, TestAddFlagsJustIdFlags) {
+  std::vector<FakeTraceInfo> infos = {
+      // TRACE_EVENT_FLAG_HAS_ID is only parsed if it is required by the phase
+      // like 'S'.
+      FakeTraceInfo("name_1", "cat", 'S', /* timestamp = */ 1, /* pid = */ 2,
+                    /* tid = */ 3),
+      FakeTraceInfo("name_2", "cat", 'B', /* timestamp = */ 1, /* pid = */ 2,
+                    /* tid = */ 3),
+      FakeTraceInfo("name_3", "cat", 'B', /* timestamp = */ 1, /* pid = */ 2,
+                    /* tid = */ 3),
+  };
+  infos[0].flags = TRACE_EVENT_FLAG_HAS_ID;
+  infos[0].id = 1;
+  infos[1].flags = TRACE_EVENT_FLAG_HAS_LOCAL_ID;
+  infos[1].id = 2;
+  infos[1].scope = "id2";
+  infos[2].flags = TRACE_EVENT_FLAG_HAS_GLOBAL_ID;
+  infos[2].id = 3;
+  infos[2].scope = "id3";
+  json_trace_exporter_->SetFakeTraceEvents(infos);
+  json_trace_exporter_->OnTraceData(
+      std::vector<perfetto::TracePacket>(infos.size()), false);
+  EXPECT_EQ(
+      "{\"traceEvents\":[{\"pid\":2,\"tid\":3,\"ts\":1,\"ph\":\"S\","
+      "\"cat\":\"cat\",\"name\":\"name_1\",\"id\":\"0x1\",\"args\":{}},\n"
+      "{\"pid\":2,\"tid\":3,\"ts\":1,\"ph\":\"B\",\"cat\":\"cat\","
+      "\"name\":\"name_2\",\"scope\":\"id2\",\"id2\":{\"local\":\"0x2\"},"
+      "\"args\":{}},\n"
+      "{\"pid\":2,\"tid\":3,\"ts\":1,\"ph\":\"B\",\"cat\":\"cat\","
+      "\"name\":\"name_3\",\"scope\":\"id3\",\"id2\":{\"global\":\"0x3\"},"
+      "\"args\":{}}]}",
+      unparsed_trace_data_);
+  const trace_analyzer::TraceEvent* trace_event = trace_analyzer_->FindFirstOf(
+      trace_analyzer::Query(trace_analyzer::Query::EVENT_NAME) ==
+      trace_analyzer::Query::String("name_1"));
+  ASSERT_TRUE(trace_event);
+  EXPECT_EQ("0x1", trace_event->id);
+  EXPECT_EQ("", trace_event->global_id2);
+  EXPECT_EQ("", trace_event->local_id2);
+  EXPECT_EQ("", trace_event->scope);
+  const trace_analyzer::TraceEvent* trace_event_2 =
+      trace_analyzer_->FindFirstOf(
+          trace_analyzer::Query(trace_analyzer::Query::EVENT_NAME) ==
+          trace_analyzer::Query::String("name_2"));
+  EXPECT_EQ("", trace_event_2->id);
+  EXPECT_EQ("0x2", trace_event_2->local_id2);
+  EXPECT_EQ("", trace_event_2->global_id2);
+  EXPECT_EQ("id2", trace_event_2->scope);
+  ASSERT_TRUE(trace_event);
+  const trace_analyzer::TraceEvent* trace_event_3 =
+      trace_analyzer_->FindFirstOf(
+          trace_analyzer::Query(trace_analyzer::Query::EVENT_NAME) ==
+          trace_analyzer::Query::String("name_3"));
+  ASSERT_TRUE(trace_event);
+  EXPECT_EQ("", trace_event_3->id);
+  EXPECT_EQ("", trace_event_3->local_id2);
+  EXPECT_EQ("0x3", trace_event_3->global_id2);
+  EXPECT_EQ("id3", trace_event_3->scope);
+}
+
+TEST_F(JsonTraceExporterTest, TestAddFlagsJustPhaseScope) {
+  std::vector<FakeTraceInfo> infos = {
+      FakeTraceInfo("name_1", "cat", 'I', /* timestamp = */ 1, /* pid = */ 2,
+                    /* tid = */ 3),
+      FakeTraceInfo("name_2", "cat", 'I', /* timestamp = */ 1, /* pid = */ 2,
+                    /* tid = */ 3),
+      FakeTraceInfo("name_3", "cat", 'I', /* timestamp = */ 1, /* pid = */ 2,
+                    /* tid = */ 3),
+  };
+  infos[0].flags = TRACE_EVENT_SCOPE_GLOBAL;
+  infos[1].flags = TRACE_EVENT_SCOPE_PROCESS;
+  infos[2].flags = TRACE_EVENT_SCOPE_THREAD;
+  json_trace_exporter_->SetFakeTraceEvents(infos);
+  json_trace_exporter_->OnTraceData(
+      std::vector<perfetto::TracePacket>(infos.size()), false);
+  EXPECT_EQ(
+      "{\"traceEvents\":[{\"pid\":2,\"tid\":3,\"ts\":1,\"ph\":\"I\","
+      "\"cat\":\"cat\",\"name\":\"name_1\",\"s\":\"g\",\"args\":{}},\n"
+      "{\"pid\":2,\"tid\":3,\"ts\":1,\"ph\":\"I\",\"cat\":\"cat\","
+      "\"name\":\"name_2\",\"s\":\"p\",\"args\":{}},\n"
+      "{\"pid\":2,\"tid\":3,\"ts\":1,\"ph\":\"I\",\"cat\":\"cat\","
+      "\"name\":\"name_3\",\"s\":\"t\",\"args\":{}}]}",
+      unparsed_trace_data_);
+  const trace_analyzer::TraceEvent* trace_event = trace_analyzer_->FindFirstOf(
+      trace_analyzer::Query(trace_analyzer::Query::EVENT_NAME) ==
+      trace_analyzer::Query::String("name_1"));
+  EXPECT_TRUE(trace_event);
+  EXPECT_EQ(2, trace_event->thread.process_id);
+  EXPECT_EQ(3, trace_event->thread.thread_id);
+  EXPECT_EQ(1, trace_event->timestamp);
   EXPECT_EQ('I', trace_event->phase);
-  EXPECT_EQ("bar_name", trace_event->name);
-  EXPECT_EQ("cat_name2", trace_event->category);
-  EXPECT_EQ("0x5", trace_event->global_id2);
+  EXPECT_EQ("name_1", trace_event->name);
+  EXPECT_EQ("cat", trace_event->category);
 }
 
-TEST_F(JSONTraceExporterTest, TestLegacySystemFtrace) {
-  std::string ftrace = "#dummy data";
+TEST_F(JsonTraceExporterTest, TestAddArgs) {
+  std::vector<FakeTraceInfo> infos = {FakeTraceInfo(
+      "name", "cat", 'B', /* timestamp = */ 1, /* pid = */ 2, /* tid = */ 3)};
+  infos[0].args.emplace_back("bool", bool(true));
+  infos[0].args.emplace_back("double", double(8.0));
+  base::Value dict(base::Value::Type::DICTIONARY);
+  dict.SetKey("key", base::Value("value"));
+  infos[0].args.emplace_back("dict", &dict);
 
-  perfetto::protos::TracePacket trace_packet_proto;
-  trace_packet_proto.mutable_chrome_events()->add_legacy_ftrace_output(ftrace);
-  FinalizePacket(trace_packet_proto);
+  // We will set up this dictionary below so that all the args are easily
+  // visible together.
+  infos[0].args.emplace_back("json", std::string("{\"json_dict\":\"val\"}"));
 
-  auto* sys_trace = parsed_trace_data()->FindKey("systemTraceEvents");
-  EXPECT_TRUE(sys_trace);
-  EXPECT_EQ(sys_trace->GetString(), ftrace);
+  json_trace_exporter_->SetFakeTraceEvents(infos);
+  json_trace_exporter_->OnTraceData(
+      std::vector<perfetto::TracePacket>(infos.size()), false);
+  EXPECT_EQ(
+      "{\"traceEvents\":[{\"pid\":2,\"tid\":3,\"ts\":1,\"ph\":\"B\","
+      "\"cat\":\"cat\",\"name\":\"name\",\"args\":{\"bool\":true"
+      ",\"double\":8.000000,\"dict\":{\"key\":\"value\"}"
+      ",\"json\":{\"json_dict\":\"val\"}}}]}",
+      unparsed_trace_data_);
+  const trace_analyzer::TraceEvent* trace_event = trace_analyzer_->FindFirstOf(
+      trace_analyzer::Query(trace_analyzer::Query::EVENT_NAME) ==
+      trace_analyzer::Query::String("name"));
+  EXPECT_TRUE(trace_event);
+  EXPECT_EQ(true, trace_event->GetKnownArgAsBool("bool"));
+  EXPECT_TRUE(trace_event);
+  EXPECT_EQ(8.0, trace_event->GetKnownArgAsDouble("double"));
+  auto value = trace_event->GetKnownArgAsValue("json");
+  ASSERT_TRUE(value);
+  EXPECT_TRUE(value->is_dict());
+  EXPECT_EQ("val", value->FindKey("json_dict")->GetString());
 }
 
-TEST_F(JSONTraceExporterTest, TestLegacySystemTraceEvents) {
-  perfetto::protos::TracePacket trace_packet_proto;
+TEST_F(JsonTraceExporterTest, TestAddArgsArgumentStripping) {
+  std::vector<FakeTraceInfo> infos = {
+      FakeTraceInfo("event1", "toplevel", 'B', /* timestamp = */ 1,
+                    /* pid = */ 2,
+                    /* tid = */ 3),
+      FakeTraceInfo("event2", "whitewashed", 'B', /* timestamp = */ 1,
+                    /* pid = */ 2, /* tid = */ 3),
+      FakeTraceInfo("event3", "granular_whitelisted", 'B',
+                    /* timestamp = */ 1, /* pid = */ 2, /* tid = */ 3)};
+  infos[0].args.emplace_back("int_one", int64_t(1));
 
-  auto* json_trace =
-      trace_packet_proto.mutable_chrome_events()->add_legacy_json_trace();
-  json_trace->set_type(perfetto::protos::ChromeLegacyJsonTrace::SYSTEM_TRACE);
-  json_trace->set_data(
-      "\"name\":\"MySysTrace\",\"content\":["
-      "{\"pid\":10,\"tid\":11,\"ts\":23,\"ph\":\"I\""
-      ",\"cat\":\"cat_name2\",\"name\":\"bar_name\""
-      ",\"id2\":{\"global\":\"0x5\"},\"args\":{}}]");
+  infos[1].args.emplace_back("int_two", uint64_t(2));
 
-  FinalizePacket(trace_packet_proto);
+  // Third arg only has index into the string table.
+  infos[2].args.emplace_back("granular_arg_whitelisted",
+                             std::string("\"whitelisted_value\""));
+  infos[2].args.emplace_back("granular_arg_blacklisted",
+                             std::string("\"blacklisted_value\""));
 
-  auto* sys_trace = parsed_trace_data()->FindKey("systemTraceEvents");
-  EXPECT_TRUE(sys_trace);
-  EXPECT_EQ(sys_trace->FindKey("name")->GetString(), "MySysTrace");
-  auto* content = sys_trace->FindKey("content");
-  EXPECT_EQ(content->GetList().size(), 1u);
-  EXPECT_EQ(content->GetList()[0].FindKey("pid")->GetInt(), 10);
-  EXPECT_EQ(content->GetList()[0].FindKey("tid")->GetInt(), 11);
-  EXPECT_EQ(content->GetList()[0].FindKey("name")->GetString(), "bar_name");
+  json_trace_exporter_->SetArgumentFilterForTesting(base::BindRepeating(
+      [](const char* category_group_name, const char* event_name,
+         base::trace_event::ArgumentNameFilterPredicate* arg_filter) {
+        if (base::MatchPattern(category_group_name, "toplevel") &&
+            base::MatchPattern(event_name, "*")) {
+          return true;
+        }
+        if (base::MatchPattern(category_group_name, "granular_whitelisted") &&
+            base::MatchPattern(event_name, "event3")) {
+          *arg_filter = base::BindRepeating([](const char* arg_name) {
+            return base::MatchPattern(arg_name, "granular_arg_whitelisted");
+          });
+          return true;
+        }
+        return false;
+      }));
+
+  json_trace_exporter_->SetFakeTraceEvents(infos);
+  json_trace_exporter_->OnTraceData(
+      std::vector<perfetto::TracePacket>(infos.size()), false);
+  EXPECT_EQ(
+      "{\"traceEvents\":[{\"pid\":2,\"tid\":3,\"ts\":1,\"ph\":\"B\","
+      "\"cat\":\"toplevel\",\"name\":\"event1\",\"args\":{\"int_one\":1}},\n"
+      "{\"pid\":2,\"tid\":3,\"ts\":1,\"ph\":\"B\",\"cat\":\"whitewashed\","
+      "\"name\":\"event2\",\"args\":\"__stripped__\"},\n"
+      "{\"pid\":2,\"tid\":3,\"ts\":1,\"ph\":\"B\","
+      "\"cat\":\"granular_whitelisted\",\"name\":\"event3\","
+      "\"args\":{\"granular_arg_whitelisted\":\"whitelisted_value\","
+      "\"granular_arg_blacklisted\":\"__stripped__\"}}]}",
+      unparsed_trace_data_);
+
+  const trace_analyzer::TraceEvent* trace_event = trace_analyzer_->FindFirstOf(
+      trace_analyzer::Query(trace_analyzer::Query::EVENT_NAME) ==
+      trace_analyzer::Query::String("event1"));
+  EXPECT_TRUE(trace_event);
+  EXPECT_EQ(1, trace_event->GetKnownArgAsDouble("int_one"));
+  trace_event = trace_analyzer_->FindFirstOf(
+      trace_analyzer::Query(trace_analyzer::Query::EVENT_NAME) ==
+      trace_analyzer::Query::String("event2"));
+  EXPECT_FALSE(trace_event->HasArg("int_two"));
+  trace_event = trace_analyzer_->FindFirstOf(
+      trace_analyzer::Query(trace_analyzer::Query::EVENT_NAME) ==
+      trace_analyzer::Query::String("event3"));
+  EXPECT_EQ("whitelisted_value",
+            trace_event->GetKnownArgAsString(("granular_arg_whitelisted")));
+  EXPECT_EQ("__stripped__",
+            trace_event->GetKnownArgAsString(("granular_arg_blacklisted")));
 }
 
-TEST_F(JSONTraceExporterTest, ArgsWhitelisting) {
-  EnableArgumentFilter();
-
-  perfetto::protos::TracePacket trace_packet_proto;
-
-  {
-    auto* new_trace_event =
-        trace_packet_proto.mutable_chrome_events()->add_trace_events();
-    SetTestPacketBasicData(new_trace_event);
-    new_trace_event->set_name("event1");
-    new_trace_event->set_category_group_name("toplevel");
-    auto* new_arg = new_trace_event->add_args();
-    new_arg->set_name("int_one");
-    new_arg->set_uint_value(1);
-  }
-
-  {
-    auto* new_trace_event =
-        trace_packet_proto.mutable_chrome_events()->add_trace_events();
-    SetTestPacketBasicData(new_trace_event);
-    new_trace_event->set_name("event2");
-    new_trace_event->set_category_group_name("whitewashed");
-    auto* new_arg = new_trace_event->add_args();
-    new_arg->set_name("int_two");
-    new_arg->set_uint_value(1);
-  }
-
-  {
-    auto* new_trace_event =
-        trace_packet_proto.mutable_chrome_events()->add_trace_events();
-    SetTestPacketBasicData(new_trace_event);
-    new_trace_event->set_name("granularly_whitelisted");
-    new_trace_event->set_category_group_name("benchmark");
-    auto* new_arg1 = new_trace_event->add_args();
-    new_arg1->set_name("granular_arg_whitelisted");
-    new_arg1->set_string_value("whitelisted_value");
-    auto* new_arg2 = new_trace_event->add_args();
-    new_arg2->set_name("granular_arg_blacklisted");
-    new_arg2->set_string_value("blacklisted_value");
-  }
-
-  FinalizePacket(trace_packet_proto);
-
-  {
-    const auto* trace_event = trace_analyzer()->FindFirstOf(
-        trace_analyzer::Query(trace_analyzer::Query::EVENT_NAME) ==
-        trace_analyzer::Query::String("event1"));
-    EXPECT_TRUE(trace_event);
-    EXPECT_EQ(1, trace_event->GetKnownArgAsDouble("int_one"));
-  }
-
-  {
-    const auto* trace_event = trace_analyzer()->FindFirstOf(
-        trace_analyzer::Query(trace_analyzer::Query::EVENT_NAME) ==
-        trace_analyzer::Query::String("event2"));
-    EXPECT_TRUE(trace_event);
-    EXPECT_FALSE(trace_event->HasArg(("int_two")));
-  }
-
-  {
-    const auto* trace_event = trace_analyzer()->FindFirstOf(
-        trace_analyzer::Query(trace_analyzer::Query::EVENT_NAME) ==
-        trace_analyzer::Query::String("granularly_whitelisted"));
-    EXPECT_TRUE(trace_event);
-    EXPECT_EQ("whitelisted_value",
-              trace_event->GetKnownArgAsString(("granular_arg_whitelisted")));
-    EXPECT_EQ("__stripped__",
-              trace_event->GetKnownArgAsString(("granular_arg_blacklisted")));
-  }
+TEST_F(JsonTraceExporterTest, TestFtraceLegacyOutput) {
+  json_trace_exporter_->legacy_ftrace_output.push_back("legacy_trace_data1");
+  json_trace_exporter_->legacy_ftrace_output.push_back(",legacy\"_trace_data2");
+  json_trace_exporter_->OnTraceData(std::vector<perfetto::TracePacket>(),
+                                    false);
+  EXPECT_EQ(
+      "{\"traceEvents\":[],"
+      "\"systemTraceEvents\":\"legacy_trace_data1,legacy\\\"_trace_data2\"}",
+      unparsed_trace_data_);
 }
 
+TEST_F(JsonTraceExporterTest, TestLegacySystemTrace) {
+  json_trace_exporter_->json_traces.emplace_back();
+  auto& trace = json_trace_exporter_->json_traces.back();
+  trace.set_type(perfetto::protos::ChromeLegacyJsonTrace::SYSTEM_TRACE);
+  trace.set_data("\"bool\":true");
+  json_trace_exporter_->json_traces.push_back(trace);
+  json_trace_exporter_->json_traces.back().set_data("\"double\":8.0");
+  json_trace_exporter_->OnTraceData(std::vector<perfetto::TracePacket>(),
+                                    false);
+  EXPECT_EQ(
+      "{\"traceEvents\":[],"
+      "\"systemTraceEvents\":{\"bool\":true,\"double\":8.0}}",
+      unparsed_trace_data_);
+}
+
+TEST_F(JsonTraceExporterTest, TestLegacyJsonTrace) {
+  json_trace_exporter_->json_traces.emplace_back();
+  auto& trace = json_trace_exporter_->json_traces.back();
+  trace.set_type(perfetto::protos::ChromeLegacyJsonTrace::USER_TRACE);
+  trace.set_data(
+      "{\"pid\":2,\"tid\":3,\"ts\":1,\"ph\":\"B\","
+      "\"cat\":\"cat\",\"name\":\"name_1\",\"args\":{}}");
+  json_trace_exporter_->json_traces.push_back(trace);
+  json_trace_exporter_->json_traces.back().set_data(
+      "{\"pid\":2,\"tid\":3,\"ts\":1,\"ph\":\"B\","
+      "\"cat\":\"cat\",\"name\":\"name_2\",\"args\":{}}");
+  json_trace_exporter_->OnTraceData(std::vector<perfetto::TracePacket>(),
+                                    false);
+  EXPECT_EQ(
+      "{\"traceEvents\":[{\"pid\":2,\"tid\":3,\"ts\":1,\"ph\":\"B\","
+      "\"cat\":\"cat\",\"name\":\"name_1\",\"args\":{}},\n{\"pid\":2,"
+      "\"tid\":3,\"ts\":1,\"ph\":\"B\",\"cat\":\"cat\","
+      "\"name\":\"name_2\",\"args\":{}}]}",
+      unparsed_trace_data_);
+  const trace_analyzer::TraceEvent* trace_event = trace_analyzer_->FindFirstOf(
+      trace_analyzer::Query(trace_analyzer::Query::EVENT_NAME) ==
+      trace_analyzer::Query::String("name_1"));
+  EXPECT_TRUE(trace_event);
+  EXPECT_EQ(2, trace_event->thread.process_id);
+  EXPECT_EQ(3, trace_event->thread.thread_id);
+  EXPECT_EQ(1, trace_event->timestamp);
+  EXPECT_EQ('B', trace_event->phase);
+  EXPECT_EQ("name_1", trace_event->name);
+  EXPECT_EQ("cat", trace_event->category);
+  trace_event = trace_analyzer_->FindFirstOf(
+      trace_analyzer::Query(trace_analyzer::Query::EVENT_NAME) ==
+      trace_analyzer::Query::String("name_2"));
+  EXPECT_TRUE(trace_event);
+  EXPECT_EQ("name_2", trace_event->name);
+}
+
+TEST_F(JsonTraceExporterTest, TestTraceStats) {
+  // Only the last trace stats matters.
+  json_trace_exporter_->stats.emplace_back();
+  json_trace_exporter_->stats.back().set_producers_connected(2);
+  json_trace_exporter_->stats.emplace_back();
+  auto& stats = json_trace_exporter_->stats.back();
+  stats.set_producers_connected(4);
+  auto* buf_stats = stats.add_buffer_stats();
+  buf_stats->set_buffer_size(1024);
+  json_trace_exporter_->OnTraceData(std::vector<perfetto::TracePacket>(),
+                                    false);
+
+  // To avoid making a brittle test we don't check all fields are outputted in
+  // the trace stats just the ones we explicitly set.
+  EXPECT_EQ(std::string::npos,
+            unparsed_trace_data_.find("\"producers_connected\":2"));
+  EXPECT_NE(std::string::npos,
+            unparsed_trace_data_.find("\"producers_connected\":4"));
+  EXPECT_NE(std::string::npos,
+            unparsed_trace_data_.find("\"buffer_size\":1024"));
+  EXPECT_TRUE(base::StartsWith(unparsed_trace_data_,
+                               "{\"traceEvents\":[],"
+                               "\"metadata\":{\"perfetto_trace_stats\":{\"",
+                               base::CompareCase::SENSITIVE));
+  EXPECT_TRUE(
+      base::EndsWith(unparsed_trace_data_, "}}", base::CompareCase::SENSITIVE));
+}
+
+TEST_F(JsonTraceExporterTest, TestMetadata) {
+  json_trace_exporter_->metadata.emplace_back();
+  auto& m1 = json_trace_exporter_->metadata.back();
+  m1.set_name("metadata_1");
+  m1.set_bool_value(true);
+  json_trace_exporter_->metadata.emplace_back();
+  auto& m2 = json_trace_exporter_->metadata.back();
+  m2.set_name("metadata_2");
+  m2.set_json_value("{\"dict\":{\"bool\":true}}");
+  json_trace_exporter_->OnTraceData(std::vector<perfetto::TracePacket>(),
+                                    false);
+  EXPECT_EQ(unparsed_trace_data_,
+            "{\"traceEvents\":[],"
+            "\"metadata\":{\"metadata_1\":true,"
+            "\"metadata_2\":{\"dict\":{\"bool\":true}}}}");
+}
+
+TEST_F(JsonTraceExporterTest, ComplexMultipleCallback) {
+  // Allocate a string that will cause the buffer to be flushed after this
+  // event.
+  constexpr size_t kTraceEventBufferSizeInBytes = 100 * 1024;
+  std::string big_string(kTraceEventBufferSizeInBytes, 'a');
+  std::vector<FakeTraceInfo> infos = {
+      FakeTraceInfo(big_string, "cat", 'B',
+                    /* timestamp = */ 1, /* pid = */ 2,
+                    /* tid = */ 3),
+      FakeTraceInfo("name", "cat", 'B',
+                    /* timestamp = */ 1, /* pid = */ 2,
+                    /* tid = */ 3)};
+  json_trace_exporter_->SetFakeTraceEvents(infos);
+  json_trace_exporter_->OnTraceData(
+      std::vector<perfetto::TracePacket>(infos.size()), true);
+  json_trace_exporter_->SetFakeTraceEvents({infos[1]});
+  json_trace_exporter_->OnTraceData(std::vector<perfetto::TracePacket>(1),
+                                    false);
+  ASSERT_EQ(static_cast<std::size_t>(3), unparsed_trace_data_sequence_.size());
+  EXPECT_EQ(base::StringPrintf(
+                "{\"traceEvents\":[{\"pid\":2,\"tid\":3,\"ts\":1,\"ph\":\"B\","
+                "\"cat\":\"cat\",\"name\":\"%s\"",
+                big_string.c_str()),
+            unparsed_trace_data_sequence_[0]);
+  EXPECT_EQ(
+      ",\"args\":{}},\n{\"pid\":2,\"tid\":3,\"ts\":1,\"ph\":\"B\","
+      "\"cat\":\"cat\",\"name\":\"name\",\"args\":{}}",
+      unparsed_trace_data_sequence_[1]);
+  EXPECT_EQ(
+      ",\n{\"pid\":2,\"tid\":3,\"ts\":1,\"ph\":\"B\","
+      "\"cat\":\"cat\",\"name\":\"name\",\"args\":{}}]}",
+      unparsed_trace_data_sequence_[2]);
+  EXPECT_EQ(base::StringPrintf(
+                "{\"traceEvents\":[{\"pid\":2,\"tid\":3,\"ts\":1,\"ph\":\"B\","
+                "\"cat\":\"cat\",\"name\":\"%s\",\"args\":{}}"
+                ",\n{\"pid\":2,\"tid\":3,\"ts\":1,\"ph\":\"B\","
+                "\"cat\":\"cat\",\"name\":\"name\",\"args\":{}}"
+                ",\n{\"pid\":2,\"tid\":3,\"ts\":1,\"ph\":\"B\","
+                "\"cat\":\"cat\",\"name\":\"name\",\"args\":{}}]}",
+                big_string.c_str()),
+            unparsed_trace_data_);
+}
 }  // namespace tracing
diff --git a/services/tracing/perfetto/perfetto_tracing_coordinator.cc b/services/tracing/perfetto/perfetto_tracing_coordinator.cc
index e10fd5d..16e4210 100644
--- a/services/tracing/perfetto/perfetto_tracing_coordinator.cc
+++ b/services/tracing/perfetto/perfetto_tracing_coordinator.cc
@@ -14,7 +14,7 @@
 #include "base/trace_event/trace_log.h"
 #include "build/build_config.h"
 #include "mojo/public/cpp/system/data_pipe_utils.h"
-#include "services/tracing/perfetto/json_trace_exporter.h"
+#include "services/tracing/perfetto/chrome_event_bundle_json_exporter.h"
 #include "services/tracing/perfetto/perfetto_service.h"
 #include "services/tracing/public/mojom/constants.mojom.h"
 #include "services/tracing/public/mojom/perfetto_service.mojom.h"
@@ -37,13 +37,13 @@
                  base::OnceClosure tracing_over_callback)
       : tracing_over_callback_(std::move(tracing_over_callback)) {
     base::trace_event::TraceConfig chrome_trace_config_obj(config);
-    json_trace_exporter_.reset(new JSONTraceExporter(
+    json_trace_exporter_ = std::make_unique<ChromeEventBundleJsonExporter>(
         chrome_trace_config_obj.IsArgumentFilterEnabled()
             ? base::trace_event::TraceLog::GetInstance()
                   ->GetArgumentFilterPredicate()
             : JSONTraceExporter::ArgumentFilterPredicate(),
         base::BindRepeating(&TracingSession::OnJSONTraceEventCallback,
-                            base::Unretained(this))));
+                            base::Unretained(this)));
     perfetto::TracingService* service =
         PerfettoService::GetInstance()->GetService();
     consumer_endpoint_ = service->ConnectConsumer(this, /*uid=*/0);
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 a13c8b9..2b67f4f 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
@@ -26,6 +26,8 @@
 #include "third_party/perfetto/protos/perfetto/trace/trace_packet.pb.h"
 #include "third_party/perfetto/protos/perfetto/trace/trace_packet.pbzero.h"
 
+using TrackEvent = perfetto::protos::TrackEvent;
+
 namespace tracing {
 
 namespace {
@@ -164,9 +166,7 @@
     return perfetto::WriterID(0);
   }
 
-  uint64_t written() const override {
-    return 0u;
-  }
+  uint64_t written() const override { return 0u; }
 
  private:
   perfetto::protos::pbzero::TracePacket trace_packet_;
@@ -190,9 +190,7 @@
     return perfetto::WriterID(0);
   }
 
-  uint64_t written() const override {
-    return 0u;
-  }
+  uint64_t written() const override { return 0u; }
 
  private:
   MockProducerClient* producer_client_;
@@ -214,9 +212,7 @@
 
 class TraceEventDataSourceTest : public testing::Test {
  public:
-  void SetUp() override {
-    ProducerClient::ResetTaskRunnerForTesting();
-  }
+  void SetUp() override { ProducerClient::ResetTaskRunnerForTesting(); }
 
   void TearDown() override {
     if (base::trace_event::TraceLog::GetInstance()->IsEnabled()) {
@@ -693,16 +689,84 @@
     EXPECT_EQ(packet->track_event().category_iids_size(), 1);
     EXPECT_EQ(packet->track_event().category_iids(0), category_iid);
     EXPECT_TRUE(packet->track_event().has_legacy_event());
-    EXPECT_EQ(packet->track_event().legacy_event().name_iid(), name_iid);
-    EXPECT_EQ(packet->track_event().legacy_event().phase(), phase);
-    EXPECT_EQ(packet->track_event().legacy_event().flags(), flags);
-    EXPECT_EQ(packet->track_event().legacy_event().id(), id);
-    EXPECT_EQ(packet->track_event().legacy_event().duration(), duration);
 
-    EXPECT_EQ(packet->track_event().legacy_event().tid_override(),
-              tid_override);
-    EXPECT_EQ(packet->track_event().legacy_event().pid_override(),
-              pid_override);
+    const auto& legacy_event = packet->track_event().legacy_event();
+    EXPECT_EQ(legacy_event.name_iid(), name_iid);
+    EXPECT_EQ(legacy_event.phase(), phase);
+    EXPECT_EQ(legacy_event.duration_us(), duration);
+
+    if (phase == TRACE_EVENT_PHASE_INSTANT) {
+      switch (flags & TRACE_EVENT_FLAG_SCOPE_MASK) {
+        case TRACE_EVENT_SCOPE_GLOBAL:
+          EXPECT_EQ(legacy_event.instant_event_scope(),
+                    TrackEvent::LegacyEvent::SCOPE_GLOBAL);
+          break;
+
+        case TRACE_EVENT_SCOPE_PROCESS:
+          EXPECT_EQ(legacy_event.instant_event_scope(),
+                    TrackEvent::LegacyEvent::SCOPE_PROCESS);
+          break;
+
+        case TRACE_EVENT_SCOPE_THREAD:
+          EXPECT_EQ(legacy_event.instant_event_scope(),
+                    TrackEvent::LegacyEvent::SCOPE_THREAD);
+          break;
+      }
+    } else {
+      EXPECT_EQ(legacy_event.instant_event_scope(),
+                TrackEvent::LegacyEvent::SCOPE_UNSPECIFIED);
+    }
+
+    switch (flags & (TRACE_EVENT_FLAG_HAS_ID | TRACE_EVENT_FLAG_HAS_LOCAL_ID |
+                     TRACE_EVENT_FLAG_HAS_GLOBAL_ID)) {
+      case TRACE_EVENT_FLAG_HAS_ID:
+        EXPECT_EQ(legacy_event.unscoped_id(), id);
+        EXPECT_EQ(legacy_event.local_id(), 0u);
+        EXPECT_EQ(legacy_event.global_id(), 0u);
+        break;
+      case TRACE_EVENT_FLAG_HAS_LOCAL_ID:
+        EXPECT_EQ(legacy_event.unscoped_id(), 0u);
+        EXPECT_EQ(legacy_event.local_id(), id);
+        EXPECT_EQ(legacy_event.global_id(), 0u);
+        break;
+      case TRACE_EVENT_FLAG_HAS_GLOBAL_ID:
+        EXPECT_EQ(legacy_event.unscoped_id(), 0u);
+        EXPECT_EQ(legacy_event.local_id(), 0u);
+        EXPECT_EQ(legacy_event.global_id(), id);
+        break;
+      default:
+        EXPECT_EQ(legacy_event.unscoped_id(), 0u);
+        EXPECT_EQ(legacy_event.local_id(), 0u);
+        EXPECT_EQ(legacy_event.global_id(), 0u);
+        break;
+    }
+
+    EXPECT_EQ(legacy_event.use_async_tts(), flags & TRACE_EVENT_FLAG_ASYNC_TTS);
+
+    switch (flags & (TRACE_EVENT_FLAG_FLOW_OUT | TRACE_EVENT_FLAG_FLOW_IN)) {
+      case TRACE_EVENT_FLAG_FLOW_OUT | TRACE_EVENT_FLAG_FLOW_IN:
+        EXPECT_EQ(legacy_event.flow_direction(),
+                  TrackEvent::LegacyEvent::FLOW_INOUT);
+        break;
+      case TRACE_EVENT_FLAG_FLOW_OUT:
+        EXPECT_EQ(legacy_event.flow_direction(),
+                  TrackEvent::LegacyEvent::FLOW_OUT);
+        break;
+      case TRACE_EVENT_FLAG_FLOW_IN:
+        EXPECT_EQ(legacy_event.flow_direction(),
+                  TrackEvent::LegacyEvent::FLOW_IN);
+        break;
+      default:
+        EXPECT_EQ(legacy_event.flow_direction(),
+                  TrackEvent::LegacyEvent::FLOW_UNSPECIFIED);
+        break;
+    }
+
+    EXPECT_EQ(legacy_event.bind_to_enclosing(),
+              flags & TRACE_EVENT_FLAG_BIND_TO_ENCLOSING);
+
+    EXPECT_EQ(legacy_event.tid_override(), tid_override);
+    EXPECT_EQ(legacy_event.pid_override(), pid_override);
   }
 
   void ExpectEventCategories(
diff --git a/services/tracing/public/cpp/perfetto/track_event_thread_local_event_sink.cc b/services/tracing/public/cpp/perfetto/track_event_thread_local_event_sink.cc
index a24eac39..3a87a0ee 100644
--- a/services/tracing/public/cpp/perfetto/track_event_thread_local_event_sink.cc
+++ b/services/tracing/public/cpp/perfetto/track_event_thread_local_event_sink.cc
@@ -21,6 +21,7 @@
 #include "third_party/perfetto/protos/perfetto/trace/track_event/track_event.pbzero.h"
 
 using TraceLog = base::trace_event::TraceLog;
+using TrackEvent = perfetto::protos::pbzero::TrackEvent;
 
 namespace tracing {
 
@@ -260,32 +261,82 @@
   legacy_event->set_phase(phase);
 
   if (phase == TRACE_EVENT_PHASE_COMPLETE) {
-    legacy_event->set_duration(trace_event->duration().InMicroseconds());
+    legacy_event->set_duration_us(trace_event->duration().InMicroseconds());
 
     if (!trace_event->thread_timestamp().is_null()) {
       int64_t thread_duration = trace_event->thread_duration().InMicroseconds();
       if (thread_duration != -1) {
-        legacy_event->set_thread_duration(thread_duration);
+        legacy_event->set_thread_duration_us(thread_duration);
       }
     }
+  } else if (phase == TRACE_EVENT_PHASE_INSTANT) {
+    switch (flags & TRACE_EVENT_FLAG_SCOPE_MASK) {
+      case TRACE_EVENT_SCOPE_GLOBAL:
+        legacy_event->set_instant_event_scope(
+            TrackEvent::LegacyEvent::SCOPE_GLOBAL);
+        break;
+
+      case TRACE_EVENT_SCOPE_PROCESS:
+        legacy_event->set_instant_event_scope(
+            TrackEvent::LegacyEvent::SCOPE_PROCESS);
+        break;
+
+      case TRACE_EVENT_SCOPE_THREAD:
+        legacy_event->set_instant_event_scope(
+            TrackEvent::LegacyEvent::SCOPE_THREAD);
+        break;
+    }
   }
 
-  legacy_event->set_flags(flags);
-
-  if (flags & (TRACE_EVENT_FLAG_HAS_ID | TRACE_EVENT_FLAG_HAS_LOCAL_ID |
-               TRACE_EVENT_FLAG_HAS_GLOBAL_ID)) {
-    legacy_event->set_id(trace_event->id());
+  uint32_t id_flags =
+      flags & (TRACE_EVENT_FLAG_HAS_ID | TRACE_EVENT_FLAG_HAS_LOCAL_ID |
+               TRACE_EVENT_FLAG_HAS_GLOBAL_ID);
+  switch (id_flags) {
+    case TRACE_EVENT_FLAG_HAS_ID:
+      legacy_event->set_unscoped_id(trace_event->id());
+      break;
+    case TRACE_EVENT_FLAG_HAS_LOCAL_ID:
+      legacy_event->set_local_id(trace_event->id());
+      break;
+    case TRACE_EVENT_FLAG_HAS_GLOBAL_ID:
+      legacy_event->set_global_id(trace_event->id());
+      break;
+    default:
+      break;
   }
 
-  if (trace_event->scope() != trace_event_internal::kGlobalScope) {
-    legacy_event->set_scope(trace_event->scope());
+  if (id_flags && trace_event->scope() != trace_event_internal::kGlobalScope) {
+    legacy_event->set_id_scope(trace_event->scope());
   }
 
-  if ((flags & TRACE_EVENT_FLAG_FLOW_OUT) ||
-      (flags & TRACE_EVENT_FLAG_FLOW_IN)) {
+  if (flags & TRACE_EVENT_FLAG_ASYNC_TTS) {
+    legacy_event->set_use_async_tts(true);
+  }
+
+  uint32_t flow_flags =
+      flags & (TRACE_EVENT_FLAG_FLOW_OUT | TRACE_EVENT_FLAG_FLOW_IN);
+  switch (flow_flags) {
+    case TRACE_EVENT_FLAG_FLOW_OUT | TRACE_EVENT_FLAG_FLOW_IN:
+      legacy_event->set_flow_direction(TrackEvent::LegacyEvent::FLOW_INOUT);
+      break;
+    case TRACE_EVENT_FLAG_FLOW_OUT:
+      legacy_event->set_flow_direction(TrackEvent::LegacyEvent::FLOW_OUT);
+      break;
+    case TRACE_EVENT_FLAG_FLOW_IN:
+      legacy_event->set_flow_direction(TrackEvent::LegacyEvent::FLOW_IN);
+      break;
+    default:
+      break;
+  }
+
+  if (flow_flags) {
     legacy_event->set_bind_id(trace_event->bind_id());
   }
 
+  if (flags & TRACE_EVENT_FLAG_BIND_TO_ENCLOSING) {
+    legacy_event->set_bind_to_enclosing(true);
+  }
+
   if ((flags & TRACE_EVENT_FLAG_HAS_PROCESS_ID) &&
       trace_event->process_id() != base::kNullProcessId) {
     legacy_event->set_pid_override(trace_event->process_id());
diff --git a/services/video_capture/broadcasting_receiver.cc b/services/video_capture/broadcasting_receiver.cc
index 64860cab..1f8efce 100644
--- a/services/video_capture/broadcasting_receiver.cc
+++ b/services/video_capture/broadcasting_receiver.cc
@@ -49,7 +49,7 @@
   if (on_started_using_gpu_decode_has_been_called_)
     return;
   on_started_using_gpu_decode_has_been_called_ = true;
-  client_->OnStarted();
+  client_->OnStartedUsingGpuDecode();
 }
 
 BroadcastingReceiver::BufferContext::BufferContext(
@@ -156,8 +156,10 @@
   if (status_ == Status::kOnStartedHasBeenCalled) {
     added_client_context.OnStarted();
   }
-  if (status_ == Status::kOnStartedUsingGpuDecodeHasBeenCalled)
+  if (status_ == Status::kOnStartedUsingGpuDecodeHasBeenCalled) {
+    added_client_context.OnStarted();
     added_client_context.OnStartedUsingGpuDecode();
+  }
 
   for (auto& buffer_context : buffer_contexts_) {
     added_client_context.client()->OnNewBuffer(
diff --git a/services/video_capture/device_factory_media_to_mojo_adapter.h b/services/video_capture/device_factory_media_to_mojo_adapter.h
index 0122958..ebdb1d41 100644
--- a/services/video_capture/device_factory_media_to_mojo_adapter.h
+++ b/services/video_capture/device_factory_media_to_mojo_adapter.h
@@ -12,6 +12,7 @@
 #include "mojo/public/cpp/bindings/binding.h"
 #include "services/service_manager/public/cpp/service_context_ref.h"
 #include "services/video_capture/device_factory.h"
+#include "services/video_capture/public/mojom/devices_changed_observer.mojom.h"
 
 #if defined(OS_CHROMEOS)
 #include "media/capture/video/chromeos/mojo/cros_image_capture.mojom.h"
diff --git a/services/video_capture/device_factory_provider_impl.cc b/services/video_capture/device_factory_provider_impl.cc
index c73f826..cbb9cfb 100644
--- a/services/video_capture/device_factory_provider_impl.cc
+++ b/services/video_capture/device_factory_provider_impl.cc
@@ -110,8 +110,6 @@
     mojom::DeviceFactoryRequest request) {
   DCHECK(service_ref_);
   LazyInitializeDeviceFactory();
-  if (factory_bindings_.empty())
-    device_factory_->SetServiceRef(service_ref_->Clone());
   factory_bindings_.AddBinding(device_factory_.get(), std::move(request));
 }
 
@@ -156,6 +154,7 @@
               &GpuDependenciesContext::CreateJpegDecodeAccelerator,
               gpu_dependencies_context_->GetWeakPtr()),
           gpu_dependencies_context_->GetTaskRunner()));
+  device_factory_->SetServiceRef(service_ref_->Clone());
 }
 
 void DeviceFactoryProviderImpl::LazyInitializeVideoSourceProvider() {
diff --git a/services/video_capture/public/cpp/mock_device_factory.h b/services/video_capture/public/cpp/mock_device_factory.h
index bfb3827..dcbbe01f 100644
--- a/services/video_capture/public/cpp/mock_device_factory.h
+++ b/services/video_capture/public/cpp/mock_device_factory.h
@@ -6,6 +6,7 @@
 #define SERVICES_VIDEO_CAPTURE_PUBLIC_CPP_MOCK_DEVICE_FACTORY_H_
 
 #include "services/video_capture/public/mojom/device_factory.mojom.h"
+#include "services/video_capture/public/mojom/devices_changed_observer.mojom.h"
 #include "services/video_capture/public/mojom/producer.mojom.h"
 #include "testing/gmock/include/gmock/gmock.h"
 
diff --git a/services/video_capture/public/cpp/mock_video_source_provider.h b/services/video_capture/public/cpp/mock_video_source_provider.h
index 9b0282d..9e7977d 100644
--- a/services/video_capture/public/cpp/mock_video_source_provider.h
+++ b/services/video_capture/public/cpp/mock_video_source_provider.h
@@ -5,6 +5,7 @@
 #ifndef SERVICES_VIDEO_CAPTURE_PUBLIC_CPP_MOCK_VIDEO_SOURCE_PROVIDER_H_
 #define SERVICES_VIDEO_CAPTURE_PUBLIC_CPP_MOCK_VIDEO_SOURCE_PROVIDER_H_
 
+#include "services/video_capture/public/mojom/devices_changed_observer.mojom.h"
 #include "services/video_capture/public/mojom/producer.mojom.h"
 #include "services/video_capture/public/mojom/video_source_provider.mojom.h"
 #include "testing/gmock/include/gmock/gmock.h"
@@ -33,6 +34,11 @@
   void AddTextureVirtualDevice(const media::VideoCaptureDeviceInfo& device_info,
                                video_capture::mojom::TextureVirtualDeviceRequest
                                    virtual_device) override;
+  void RegisterVirtualDevicesChangedObserver(
+      video_capture::mojom::DevicesChangedObserverPtr observer,
+      bool raise_event_if_virtual_devices_already_present) override {
+    NOTIMPLEMENTED();
+  }
 
   MOCK_METHOD1(DoGetSourceInfos, void(GetSourceInfosCallback& callback));
   MOCK_METHOD2(DoGetVideoSource,
diff --git a/services/video_capture/public/mojom/BUILD.gn b/services/video_capture/public/mojom/BUILD.gn
index 9691e3a4..a3dde4e 100644
--- a/services/video_capture/public/mojom/BUILD.gn
+++ b/services/video_capture/public/mojom/BUILD.gn
@@ -9,6 +9,7 @@
     "device.mojom",
     "device_factory.mojom",
     "device_factory_provider.mojom",
+    "devices_changed_observer.mojom",
     "producer.mojom",
     "receiver.mojom",
     "scoped_access_permission.mojom",
diff --git a/services/video_capture/public/mojom/device_factory.mojom b/services/video_capture/public/mojom/device_factory.mojom
index 2e2dee6..e92b240 100644
--- a/services/video_capture/public/mojom/device_factory.mojom
+++ b/services/video_capture/public/mojom/device_factory.mojom
@@ -6,6 +6,7 @@
 
 import "media/capture/mojom/video_capture_types.mojom";
 import "services/video_capture/public/mojom/device.mojom";
+import "services/video_capture/public/mojom/devices_changed_observer.mojom";
 import "services/video_capture/public/mojom/producer.mojom";
 import "services/video_capture/public/mojom/virtual_device.mojom";
 
@@ -15,10 +16,6 @@
   ERROR_DEVICE_NOT_FOUND
 };
 
-interface DevicesChangedObserver {
-  OnDevicesChanged();
-};
-
 // Enables access to a set of video capture devices.
 // Typical operation is to first call GetDeviceInfos() to obtain
 // information about available devices. The |device_id| of the infos can
diff --git a/services/video_capture/public/mojom/devices_changed_observer.mojom b/services/video_capture/public/mojom/devices_changed_observer.mojom
new file mode 100644
index 0000000..535aa09
--- /dev/null
+++ b/services/video_capture/public/mojom/devices_changed_observer.mojom
@@ -0,0 +1,11 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+module video_capture.mojom;
+
+// Callback interface for clients who wnat to get notified when virtual devices
+// are added or removed.
+interface DevicesChangedObserver {
+  OnDevicesChanged();
+};
diff --git a/services/video_capture/public/mojom/video_source_provider.mojom b/services/video_capture/public/mojom/video_source_provider.mojom
index 682182db..dbb1b65 100644
--- a/services/video_capture/public/mojom/video_source_provider.mojom
+++ b/services/video_capture/public/mojom/video_source_provider.mojom
@@ -5,6 +5,7 @@
 module video_capture.mojom;
 
 import "media/capture/mojom/video_capture_types.mojom";
+import "services/video_capture/public/mojom/devices_changed_observer.mojom";
 import "services/video_capture/public/mojom/producer.mojom";
 import "services/video_capture/public/mojom/video_source.mojom";
 import "services/video_capture/public/mojom/virtual_device.mojom";
@@ -41,4 +42,12 @@
   AddTextureVirtualDevice(
       media.mojom.VideoCaptureDeviceInfo device_info,
       TextureVirtualDevice& virtual_device);
+
+  // Registered observers will get notified whenever a virtual device is added
+  // or removed. Note: Changes to non-virtual devices are currently being
+  // monitored outside the video capture service, and therefore the service
+  // does not offer such monitoring.
+  RegisterVirtualDevicesChangedObserver(
+      DevicesChangedObserver observer,
+      bool raise_event_if_virtual_devices_already_present);
 };
diff --git a/services/video_capture/test/mock_devices_changed_observer.h b/services/video_capture/test/mock_devices_changed_observer.h
index 850dfc0..460fd84 100644
--- a/services/video_capture/test/mock_devices_changed_observer.h
+++ b/services/video_capture/test/mock_devices_changed_observer.h
@@ -6,6 +6,7 @@
 #define SERVICES_VIDEO_CAPTURE_TEST_MOCK_DEVICES_CHANGED_OBSERVER_H_
 
 #include "services/video_capture/public/mojom/device_factory.mojom.h"
+#include "services/video_capture/public/mojom/devices_changed_observer.mojom.h"
 #include "testing/gmock/include/gmock/gmock.h"
 
 namespace video_capture {
diff --git a/services/video_capture/video_source_provider_impl.cc b/services/video_capture/video_source_provider_impl.cc
index 3c395a3c..a50404a 100644
--- a/services/video_capture/video_source_provider_impl.cc
+++ b/services/video_capture/video_source_provider_impl.cc
@@ -64,6 +64,13 @@
                                            std::move(virtual_device));
 }
 
+void VideoSourceProviderImpl::RegisterVirtualDevicesChangedObserver(
+    mojom::DevicesChangedObserverPtr observer,
+    bool raise_event_if_virtual_devices_already_present) {
+  device_factory_->RegisterVirtualDevicesChangedObserver(
+      std::move(observer), raise_event_if_virtual_devices_already_present);
+}
+
 void VideoSourceProviderImpl::OnVideoSourceLastClientDisconnected(
     const std::string& device_id) {
   sources_.erase(device_id);
diff --git a/services/video_capture/video_source_provider_impl.h b/services/video_capture/video_source_provider_impl.h
index ad59231..5ee8588a 100644
--- a/services/video_capture/video_source_provider_impl.h
+++ b/services/video_capture/video_source_provider_impl.h
@@ -34,6 +34,9 @@
   void AddTextureVirtualDevice(
       const media::VideoCaptureDeviceInfo& device_info,
       mojom::TextureVirtualDeviceRequest virtual_device) override;
+  void RegisterVirtualDevicesChangedObserver(
+      mojom::DevicesChangedObserverPtr observer,
+      bool raise_event_if_virtual_devices_already_present) override;
 
  private:
   void OnVideoSourceLastClientDisconnected(const std::string& device_id);
diff --git a/services/video_capture/virtual_device_enabled_device_factory.h b/services/video_capture/virtual_device_enabled_device_factory.h
index ac6a644..0d74f64 100644
--- a/services/video_capture/virtual_device_enabled_device_factory.h
+++ b/services/video_capture/virtual_device_enabled_device_factory.h
@@ -12,6 +12,7 @@
 #include "services/service_manager/public/cpp/service_context_ref.h"
 #include "services/video_capture/device_factory.h"
 #include "services/video_capture/public/mojom/device.mojom.h"
+#include "services/video_capture/public/mojom/devices_changed_observer.mojom.h"
 #include "services/video_capture/public/mojom/virtual_device.mojom.h"
 
 #if defined(OS_CHROMEOS)
diff --git a/services/viz/public/cpp/compositing/quads_struct_traits.cc b/services/viz/public/cpp/compositing/quads_struct_traits.cc
index e054298..d288d62f 100644
--- a/services/viz/public/cpp/compositing/quads_struct_traits.cc
+++ b/services/viz/public/cpp/compositing/quads_struct_traits.cc
@@ -178,7 +178,7 @@
     viz::DrawQuad* out) {
   viz::VideoHoleDrawQuad* video_hole_quad =
       static_cast<viz::VideoHoleDrawQuad*>(out);
-  return data.ReadOverlayId(&video_hole_quad->overlay_id);
+  return data.ReadOverlayPlaneId(&video_hole_quad->overlay_plane_id);
 }
 
 // static
diff --git a/services/viz/public/cpp/compositing/quads_struct_traits.h b/services/viz/public/cpp/compositing/quads_struct_traits.h
index 156b134..ab57c29c 100644
--- a/services/viz/public/cpp/compositing/quads_struct_traits.h
+++ b/services/viz/public/cpp/compositing/quads_struct_traits.h
@@ -165,10 +165,11 @@
 
 template <>
 struct StructTraits<viz::mojom::VideoHoleQuadStateDataView, viz::DrawQuad> {
-  static const base::UnguessableToken& overlay_id(const viz::DrawQuad& input) {
+  static const base::UnguessableToken& overlay_plane_id(
+      const viz::DrawQuad& input) {
     const viz::VideoHoleDrawQuad* quad =
         viz::VideoHoleDrawQuad::MaterialCast(&input);
-    return quad->overlay_id;
+    return quad->overlay_plane_id;
   }
 
   static bool Read(viz::mojom::VideoHoleQuadStateDataView data,
diff --git a/services/viz/public/interfaces/compositing/quads.mojom b/services/viz/public/interfaces/compositing/quads.mojom
index ebfe1b5..05e51d1 100644
--- a/services/viz/public/interfaces/compositing/quads.mojom
+++ b/services/viz/public/interfaces/compositing/quads.mojom
@@ -110,7 +110,7 @@
 };
 
 struct VideoHoleQuadState {
-  mojo_base.mojom.UnguessableToken overlay_id;
+  mojo_base.mojom.UnguessableToken overlay_plane_id;
 };
 
 union DrawQuadState {
diff --git a/skia/BUILD.gn b/skia/BUILD.gn
index 95a10335..793d9b1 100644
--- a/skia/BUILD.gn
+++ b/skia/BUILD.gn
@@ -119,7 +119,7 @@
 
 # Internal-facing config for Skia library code.
 config("skia_library_config") {
-  defines = [ "SK_IGNORE_MAC_BLENDING_MATCH_FIX" ]
+  defines = []
 
   # These include directories are only included for Skia code and are not
   # exported to dependents.
diff --git a/storage/browser/blob/blob_registry_impl.h b/storage/browser/blob/blob_registry_impl.h
index 213fb2a..3460984 100644
--- a/storage/browser/blob/blob_registry_impl.h
+++ b/storage/browser/blob/blob_registry_impl.h
@@ -32,6 +32,7 @@
     virtual bool CanReadFile(const base::FilePath& file) = 0;
     virtual bool CanReadFileSystemFile(const FileSystemURL& url) = 0;
     virtual bool CanCommitURL(const GURL& url) = 0;
+    virtual bool IsProcessValid() = 0;
   };
 
   BlobRegistryImpl(base::WeakPtr<BlobStorageContext> context,
diff --git a/storage/browser/blob/blob_url_store_impl.cc b/storage/browser/blob/blob_url_store_impl.cc
index 7e93aef..539508f 100644
--- a/storage/browser/blob/blob_url_store_impl.cc
+++ b/storage/browser/blob/blob_url_store_impl.cc
@@ -105,7 +105,12 @@
     mojo::ReportBadMessage("Invalid scheme passed to BlobURLStore::Revoke");
     return;
   }
-  if (!delegate_->CanCommitURL(url)) {
+  // Only report errors when we don't have permission to commit and
+  // the process is valid. The process check is a temporary solution to
+  // handle cases where this method is run after the
+  // process associated with |delegate_| has been destroyed.
+  // See https://crbug.com/933089 for details.
+  if (!delegate_->CanCommitURL(url) && delegate_->IsProcessValid()) {
     mojo::ReportBadMessage(
         "Non committable URL passed to BlobURLStore::Revoke");
     return;
diff --git a/storage/browser/blob/blob_url_store_impl_unittest.cc b/storage/browser/blob/blob_url_store_impl_unittest.cc
index 5fbd785..ffafe3d 100644
--- a/storage/browser/blob/blob_url_store_impl_unittest.cc
+++ b/storage/browser/blob/blob_url_store_impl_unittest.cc
@@ -206,6 +206,17 @@
   EXPECT_EQ(1u, bad_messages_.size());
 }
 
+TEST_F(BlobURLStoreImplTest, RevokeCantCommit_ProcessNotValid) {
+  delegate_.can_commit_url_result = false;
+  delegate_.is_process_valid_result = false;
+
+  BlobURLStorePtr url_store = CreateURLStore();
+  url_store->Revoke(kValidUrl);
+  url_store.FlushForTesting();
+  EXPECT_TRUE(bad_messages_.empty());
+  EXPECT_FALSE(context_->GetBlobDataFromPublicURL(kValidUrl));
+}
+
 TEST_F(BlobURLStoreImplTest, RevokeURLWithFragment) {
   BlobURLStorePtr url_store = CreateURLStore();
   url_store->Revoke(kFragmentUrl);
diff --git a/storage/browser/quota/client_usage_tracker.cc b/storage/browser/quota/client_usage_tracker.cc
index fbd545c..b26d5e4d 100644
--- a/storage/browser/quota/client_usage_tracker.cc
+++ b/storage/browser/quota/client_usage_tracker.cc
@@ -63,15 +63,13 @@
     blink::mojom::StorageType type,
     SpecialStoragePolicy* special_storage_policy,
     StorageMonitor* storage_monitor)
-    : tracker_(tracker),
-      client_(client),
+    : client_(client),
       type_(type),
       storage_monitor_(storage_monitor),
       global_limited_usage_(0),
       global_unlimited_usage_(0),
       global_usage_retrieved_(false),
       special_storage_policy_(special_storage_policy) {
-  DCHECK(tracker_);
   DCHECK(client_);
   if (special_storage_policy_.get())
     special_storage_policy_->AddObserver(this);
@@ -84,6 +82,7 @@
 }
 
 void ClientUsageTracker::GetGlobalLimitedUsage(UsageCallback callback) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   if (!global_usage_retrieved_) {
     GetGlobalUsage(
         base::BindOnce(&DidGetGlobalClientUsageForLimitedGlobalClientUsage,
@@ -111,6 +110,7 @@
 }
 
 void ClientUsageTracker::GetGlobalUsage(GlobalUsageCallback callback) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   if (global_usage_retrieved_ &&
       non_cached_limited_origins_by_host_.empty() &&
       non_cached_unlimited_origins_by_host_.empty()) {
@@ -126,6 +126,7 @@
 
 void ClientUsageTracker::GetHostUsage(const std::string& host,
                                       UsageCallback callback) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   if (base::ContainsKey(cached_hosts_, host) &&
       !base::ContainsKey(non_cached_limited_origins_by_host_, host) &&
       !base::ContainsKey(non_cached_unlimited_origins_by_host_, host)) {
@@ -145,6 +146,7 @@
 
 void ClientUsageTracker::UpdateUsageCache(const url::Origin& origin,
                                           int64_t delta) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   std::string host = net::GetHostOrSpecFromURL(origin.GetURL());
   if (base::ContainsKey(cached_hosts_, host)) {
     if (!IsUsageCacheEnabledForOrigin(origin))
@@ -174,6 +176,7 @@
 }
 
 int64_t ClientUsageTracker::GetCachedUsage() const {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   int64_t usage = 0;
   for (const auto& host_and_usage_map : cached_usage_by_host_) {
     for (const auto& origin_and_usage : host_and_usage_map.second)
@@ -184,6 +187,7 @@
 
 void ClientUsageTracker::GetCachedHostsUsage(
     std::map<std::string, int64_t>* host_usage) const {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   DCHECK(host_usage);
   for (const auto& host_and_usage_map : cached_usage_by_host_) {
     const std::string& host = host_and_usage_map.first;
@@ -193,6 +197,7 @@
 
 void ClientUsageTracker::GetCachedOriginsUsage(
     std::map<url::Origin, int64_t>* origin_usage) const {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   DCHECK(origin_usage);
   for (const auto& host_and_usage_map : cached_usage_by_host_) {
     for (const auto& origin_and_usage : host_and_usage_map.second)
@@ -202,6 +207,7 @@
 
 void ClientUsageTracker::GetCachedOrigins(
     std::set<url::Origin>* origins) const {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   DCHECK(origins);
   for (const auto& host_and_usage_map : cached_usage_by_host_) {
     for (const auto& origin_and_usage : host_and_usage_map.second)
@@ -211,6 +217,7 @@
 
 void ClientUsageTracker::SetUsageCacheEnabled(const url::Origin& origin,
                                               bool enabled) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   std::string host = net::GetHostOrSpecFromURL(origin.GetURL());
   if (!enabled) {
     // Erase |origin| from cache and subtract its usage.
@@ -250,6 +257,7 @@
 void ClientUsageTracker::AccumulateLimitedOriginUsage(AccumulateInfo* info,
                                                       UsageCallback callback,
                                                       int64_t usage) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   info->limited_usage += usage;
   if (--info->pending_jobs)
     return;
@@ -260,6 +268,7 @@
 void ClientUsageTracker::DidGetOriginsForGlobalUsage(
     GlobalUsageCallback callback,
     const std::set<url::Origin>& origins) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   OriginSetByHost origins_by_host;
   for (const auto& origin : origins) {
     GURL origin_url = origin.GetURL();
@@ -292,6 +301,7 @@
                                              GlobalUsageCallback callback,
                                              int64_t limited_usage,
                                              int64_t unlimited_usage) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   info->limited_usage += limited_usage;
   info->unlimited_usage += unlimited_usage;
   if (--info->pending_jobs)
@@ -308,12 +318,14 @@
 void ClientUsageTracker::DidGetOriginsForHostUsage(
     const std::string& host,
     const std::set<url::Origin>& origins) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   GetUsageForOrigins(host, origins);
 }
 
 void ClientUsageTracker::GetUsageForOrigins(
     const std::string& host,
     const std::set<url::Origin>& origins) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   AccumulateInfo* info = new AccumulateInfo;
   // Getting origin usage may synchronously return the result if the usage is
   // cached, which may in turn dispatch the completion callback before we finish
@@ -346,6 +358,7 @@
     const std::string& host,
     const base::Optional<url::Origin>& origin,
     int64_t usage) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   if (origin.has_value()) {
     DCHECK(!origin->GetURL().is_empty());
     if (usage < 0)
@@ -368,6 +381,7 @@
 
 void ClientUsageTracker::DidGetHostUsageAfterUpdate(const url::Origin& origin,
                                                     int64_t usage) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   if (!storage_monitor_)
     return;
 
@@ -377,6 +391,7 @@
 
 void ClientUsageTracker::AddCachedOrigin(const url::Origin& origin,
                                          int64_t new_usage) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   DCHECK(IsUsageCacheEnabledForOrigin(origin));
 
   std::string host = net::GetHostOrSpecFromURL(origin.GetURL());
@@ -391,10 +406,12 @@
 }
 
 void ClientUsageTracker::AddCachedHost(const std::string& host) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   cached_hosts_.insert(host);
 }
 
 int64_t ClientUsageTracker::GetCachedHostUsage(const std::string& host) const {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   auto found = cached_usage_by_host_.find(host);
   if (found == cached_usage_by_host_.end())
     return 0;
@@ -408,6 +425,7 @@
 
 bool ClientUsageTracker::GetCachedOriginUsage(const url::Origin& origin,
                                               int64_t* usage) const {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   std::string host = net::GetHostOrSpecFromURL(origin.GetURL());
   auto found_host = cached_usage_by_host_.find(host);
   if (found_host == cached_usage_by_host_.end())
@@ -424,6 +442,7 @@
 
 bool ClientUsageTracker::IsUsageCacheEnabledForOrigin(
     const url::Origin& origin) const {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   std::string host = net::GetHostOrSpecFromURL(origin.GetURL());
   return !OriginSetContainsOrigin(non_cached_limited_origins_by_host_,
                                   host, origin) &&
@@ -480,6 +499,7 @@
 
 void ClientUsageTracker::UpdateGlobalUsageValue(int64_t* usage_value,
                                                 int64_t delta) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   *usage_value += delta;
   if (*usage_value >= 0)
     return;
@@ -499,6 +519,7 @@
 }
 
 bool ClientUsageTracker::IsStorageUnlimited(const url::Origin& origin) const {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   if (type_ == blink::mojom::StorageType::kSyncable)
     return false;
   return special_storage_policy_.get() &&
diff --git a/storage/browser/quota/client_usage_tracker.h b/storage/browser/quota/client_usage_tracker.h
index 7d358d0..c3f31fbd 100644
--- a/storage/browser/quota/client_usage_tracker.h
+++ b/storage/browser/quota/client_usage_tracker.h
@@ -104,7 +104,6 @@
 
   bool IsStorageUnlimited(const url::Origin& origin) const;
 
-  UsageTracker* tracker_;
   QuotaClient* client_;
   const blink::mojom::StorageType type_;
   StorageMonitor* storage_monitor_;
diff --git a/storage/browser/quota/usage_tracker.cc b/storage/browser/quota/usage_tracker.cc
index 85a4072..64fb591 100644
--- a/storage/browser/quota/usage_tracker.cc
+++ b/storage/browser/quota/usage_tracker.cc
@@ -46,9 +46,12 @@
   }
 }
 
-UsageTracker::~UsageTracker() = default;
+UsageTracker::~UsageTracker() {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+}
 
 ClientUsageTracker* UsageTracker::GetClientTracker(QuotaClient::ID client_id) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   auto found = client_tracker_map_.find(client_id);
   if (found != client_tracker_map_.end())
     return found->second.get();
@@ -56,6 +59,7 @@
 }
 
 void UsageTracker::GetGlobalLimitedUsage(UsageCallback callback) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   if (!global_usage_callbacks_.empty()) {
     global_usage_callbacks_.emplace_back(base::BindOnce(
         &DidGetGlobalUsageForLimitedGlobalUsage, std::move(callback)));
@@ -87,6 +91,7 @@
 }
 
 void UsageTracker::GetGlobalUsage(GlobalUsageCallback callback) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   global_usage_callbacks_.emplace_back(std::move(callback));
   if (global_usage_callbacks_.size() > 1)
     return;
@@ -113,6 +118,7 @@
 
 void UsageTracker::GetHostUsage(const std::string& host,
                                 UsageCallback callback) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   UsageTracker::GetHostUsageWithBreakdown(
       host,
       base::BindOnce(&StripUsageWithBreakdownCallback, std::move(callback)));
@@ -121,6 +127,7 @@
 void UsageTracker::GetHostUsageWithBreakdown(
     const std::string& host,
     UsageWithBreakdownCallback callback) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   std::vector<UsageWithBreakdownCallback>& host_callbacks =
       host_usage_callbacks_[host];
   host_callbacks.emplace_back(std::move(callback));
@@ -145,12 +152,14 @@
 void UsageTracker::UpdateUsageCache(QuotaClient::ID client_id,
                                     const url::Origin& origin,
                                     int64_t delta) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   ClientUsageTracker* client_tracker = GetClientTracker(client_id);
   DCHECK(client_tracker);
   client_tracker->UpdateUsageCache(origin, delta);
 }
 
 int64_t UsageTracker::GetCachedUsage() const {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   int64_t usage = 0;
   for (const auto& client_id_and_tracker : client_tracker_map_)
     usage += client_id_and_tracker.second->GetCachedUsage();
@@ -159,6 +168,7 @@
 
 void UsageTracker::GetCachedHostsUsage(
     std::map<std::string, int64_t>* host_usage) const {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   DCHECK(host_usage);
   host_usage->clear();
   for (const auto& client_id_and_tracker : client_tracker_map_)
@@ -167,6 +177,7 @@
 
 void UsageTracker::GetCachedOriginsUsage(
     std::map<url::Origin, int64_t>* origin_usage) const {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   DCHECK(origin_usage);
   origin_usage->clear();
   for (const auto& client_id_and_tracker : client_tracker_map_)
@@ -174,6 +185,7 @@
 }
 
 void UsageTracker::GetCachedOrigins(std::set<url::Origin>* origins) const {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   DCHECK(origins);
   origins->clear();
   for (const auto& client_id_and_tracker : client_tracker_map_)
@@ -183,6 +195,7 @@
 void UsageTracker::SetUsageCacheEnabled(QuotaClient::ID client_id,
                                         const url::Origin& origin,
                                         bool enabled) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   ClientUsageTracker* client_tracker = GetClientTracker(client_id);
   DCHECK(client_tracker);
 
@@ -195,6 +208,7 @@
 
 void UsageTracker::AccumulateClientGlobalLimitedUsage(AccumulateInfo* info,
                                                       int64_t limited_usage) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   info->usage += limited_usage;
   if (--info->pending_clients)
     return;
@@ -210,6 +224,7 @@
 void UsageTracker::AccumulateClientGlobalUsage(AccumulateInfo* info,
                                                int64_t usage,
                                                int64_t unlimited_usage) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   info->usage += usage;
   info->unlimited_usage += unlimited_usage;
   if (--info->pending_clients)
@@ -240,6 +255,7 @@
     const std::string& host,
     QuotaClient::ID client,
     int64_t usage) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   info->usage += usage;
   // Defend against confusing inputs from clients.
   if (info->usage < 0)
@@ -279,6 +295,7 @@
 
 void UsageTracker::FinallySendHostUsageWithBreakdown(AccumulateInfo* info,
                                                      const std::string& host) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   auto host_it = host_usage_callbacks_.find(host);
   if (host_it == host_usage_callbacks_.end())
     return;
diff --git a/storage/browser/quota/usage_tracker.h b/storage/browser/quota/usage_tracker.h
index 1fbcf48..1d19e8d 100644
--- a/storage/browser/quota/usage_tracker.h
+++ b/storage/browser/quota/usage_tracker.h
@@ -16,6 +16,7 @@
 #include "base/callback.h"
 #include "base/component_export.h"
 #include "base/macros.h"
+#include "base/sequence_checker.h"
 #include "storage/browser/quota/quota_callbacks.h"
 #include "storage/browser/quota/quota_client.h"
 #include "storage/browser/quota/quota_task.h"
@@ -40,7 +41,10 @@
                StorageMonitor* storage_monitor);
   ~UsageTracker() override;
 
-  blink::mojom::StorageType type() const { return type_; }
+  blink::mojom::StorageType type() const {
+    DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+    return type_;
+  }
   ClientUsageTracker* GetClientTracker(QuotaClient::ID client_id);
 
   void GetGlobalLimitedUsage(UsageCallback callback);
@@ -57,6 +61,7 @@
       std::map<url::Origin, int64_t>* origin_usage) const;
   void GetCachedOrigins(std::set<url::Origin>* origins) const;
   bool IsWorking() const {
+    DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
     return !global_usage_callbacks_.empty() || !host_usage_callbacks_.empty();
   }
 
@@ -100,6 +105,8 @@
 
   StorageMonitor* storage_monitor_;
 
+  SEQUENCE_CHECKER(sequence_checker_);
+
   base::WeakPtrFactory<UsageTracker> weak_factory_;
   DISALLOW_COPY_AND_ASSIGN(UsageTracker);
 };
diff --git a/storage/browser/test/mock_blob_registry_delegate.cc b/storage/browser/test/mock_blob_registry_delegate.cc
index 54489261..f8e14fd 100644
--- a/storage/browser/test/mock_blob_registry_delegate.cc
+++ b/storage/browser/test/mock_blob_registry_delegate.cc
@@ -15,5 +15,8 @@
 bool MockBlobRegistryDelegate::CanCommitURL(const GURL& url) {
   return can_commit_url_result;
 }
+bool MockBlobRegistryDelegate::IsProcessValid() {
+  return is_process_valid_result;
+}
 
 }  // namespace storage
diff --git a/storage/browser/test/mock_blob_registry_delegate.h b/storage/browser/test/mock_blob_registry_delegate.h
index db68558fe..734ebde 100644
--- a/storage/browser/test/mock_blob_registry_delegate.h
+++ b/storage/browser/test/mock_blob_registry_delegate.h
@@ -17,10 +17,12 @@
   bool CanReadFile(const base::FilePath& file) override;
   bool CanReadFileSystemFile(const FileSystemURL& url) override;
   bool CanCommitURL(const GURL& url) override;
+  bool IsProcessValid() override;
 
   bool can_read_file_result = true;
   bool can_read_file_system_file_result = true;
   bool can_commit_url_result = true;
+  bool is_process_valid_result = true;
 };
 
 }  // namespace storage
diff --git a/testing/buildbot/chromium.chrome.json b/testing/buildbot/chromium.chrome.json
index 8aff5afe..8842fbd 100644
--- a/testing/buildbot/chromium.chrome.json
+++ b/testing/buildbot/chromium.chrome.json
@@ -1,4 +1,9 @@
 {
   "AAAAA1 AUTOGENERATED FILE DO NOT EDIT": {},
-  "AAAAA2 See generate_buildbot_json.py to make changes": {}
+  "AAAAA2 See generate_buildbot_json.py to make changes": {},
+  "chromeos-amd64-generic-google-rel": {
+    "additional_compile_targets": [
+      "chrome"
+    ]
+  }
 }
diff --git a/testing/buildbot/chromium.chromiumos.json b/testing/buildbot/chromium.chromiumos.json
index 1b83212..653ca22 100644
--- a/testing/buildbot/chromium.chromiumos.json
+++ b/testing/buildbot/chromium.chromiumos.json
@@ -580,6 +580,17 @@
       },
       {
         "args": [
+          "--disable-features=NetworkService"
+        ],
+        "name": "non_network_service_browser_tests",
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "shards": 10
+        },
+        "test": "browser_tests"
+      },
+      {
+        "args": [
           "--disable-features=SingleProcessMash",
           "--override-use-software-gl-for-tests"
         ],
@@ -671,6 +682,16 @@
         "test": "components_browsertests"
       },
       {
+        "args": [
+          "--disable-features=NetworkService"
+        ],
+        "name": "non_network_service_components_browsertests",
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "components_browsertests"
+      },
+      {
         "swarming": {
           "can_use_on_swarming_builders": true
         },
@@ -691,6 +712,16 @@
       },
       {
         "args": [
+          "--disable-features=NetworkService"
+        ],
+        "name": "non_network_service_content_browsertests",
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "content_browsertests"
+      },
+      {
+        "args": [
           "--disable-features=SingleProcessMash",
           "--override-use-software-gl-for-tests"
         ],
@@ -802,6 +833,16 @@
         "test": "extensions_browsertests"
       },
       {
+        "args": [
+          "--disable-features=NetworkService"
+        ],
+        "name": "non_network_service_extensions_browsertests",
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "extensions_browsertests"
+      },
+      {
         "swarming": {
           "can_use_on_swarming_builders": true
         },
@@ -858,6 +899,17 @@
       },
       {
         "args": [
+          "--disable-features=NetworkService"
+        ],
+        "name": "non_network_service_interactive_ui_tests",
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "shards": 3
+        },
+        "test": "interactive_ui_tests"
+      },
+      {
+        "args": [
           "--disable-features=SingleProcessMash"
         ],
         "name": "non_single_process_mash_interactive_ui_tests",
@@ -1081,6 +1133,16 @@
         "test": "sync_integration_tests"
       },
       {
+        "args": [
+          "--disable-features=NetworkService"
+        ],
+        "name": "non_network_service_sync_integration_tests",
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "sync_integration_tests"
+      },
+      {
         "swarming": {
           "can_use_on_swarming_builders": true
         },
diff --git a/testing/buildbot/chromium.clang.json b/testing/buildbot/chromium.clang.json
index e8c33d71..0f3b55d 100644
--- a/testing/buildbot/chromium.clang.json
+++ b/testing/buildbot/chromium.clang.json
@@ -2664,1091 +2664,6 @@
       }
     ]
   },
-  "CrWinAsanCov": {
-    "gtest_tests": [
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Windows-10-15063"
-            }
-          ]
-        },
-        "test": "accessibility_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Windows-10-15063"
-            }
-          ]
-        },
-        "test": "angle_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Windows-10-15063"
-            }
-          ]
-        },
-        "test": "app_shell_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Windows-10-15063"
-            }
-          ]
-        },
-        "test": "aura_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Windows-10-15063"
-            }
-          ]
-        },
-        "test": "base_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Windows-10-15063"
-            }
-          ]
-        },
-        "test": "blink_common_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Windows-10-15063"
-            }
-          ]
-        },
-        "test": "blink_fuzzer_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Windows-10-15063"
-            }
-          ]
-        },
-        "test": "blink_heap_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Windows-10-15063"
-            }
-          ]
-        },
-        "test": "blink_platform_unittests"
-      },
-      {
-        "name": "webkit_unit_tests",
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Windows-10-15063"
-            }
-          ]
-        },
-        "test": "blink_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Windows-10-15063"
-            }
-          ]
-        },
-        "test": "boringssl_crypto_tests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Windows-10-15063"
-            }
-          ]
-        },
-        "test": "boringssl_ssl_tests"
-      },
-      {
-        "args": [
-          "--disable-features=WebUIPolymer2",
-          "--test-launcher-filter-file=../../testing/buildbot/filters/webui_polymer1_browser_tests.filter"
-        ],
-        "name": "webui_polymer1_browser_tests",
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Windows-10-15063"
-            }
-          ],
-          "shards": 4
-        },
-        "test": "browser_tests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Windows-10-15063"
-            }
-          ]
-        },
-        "test": "cacheinvalidation_unittests"
-      },
-      {
-        "args": [
-          "--gtest_filter=-*UsingRealWebcam*"
-        ],
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Windows-10-15063"
-            }
-          ]
-        },
-        "test": "capture_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Windows-10-15063"
-            }
-          ]
-        },
-        "test": "cast_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Windows-10-15063"
-            }
-          ]
-        },
-        "test": "cc_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Windows-10-15063"
-            }
-          ]
-        },
-        "test": "chrome_app_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Windows-10-15063"
-            }
-          ]
-        },
-        "test": "chrome_cleaner_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Windows-10-15063"
-            }
-          ]
-        },
-        "test": "chrome_elf_import_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Windows-10-15063"
-            }
-          ]
-        },
-        "test": "chrome_elf_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Windows-10-15063"
-            }
-          ]
-        },
-        "test": "chromedriver_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Windows-10-15063"
-            }
-          ]
-        },
-        "test": "components_browsertests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Windows-10-15063"
-            }
-          ]
-        },
-        "test": "components_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Windows-10-15063"
-            }
-          ]
-        },
-        "test": "compositor_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Windows-10-15063"
-            }
-          ],
-          "shards": 6
-        },
-        "test": "content_browsertests"
-      },
-      {
-        "args": [
-          "--enable-perfetto",
-          "--gtest_filter=TracingControllerTest.*"
-        ],
-        "name": "perfetto_content_browsertests",
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Windows-10-15063"
-            }
-          ]
-        },
-        "test": "content_browsertests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Windows-10-15063"
-            }
-          ]
-        },
-        "test": "content_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Windows-10-15063"
-            }
-          ]
-        },
-        "test": "courgette_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Windows-10-15063"
-            }
-          ]
-        },
-        "test": "crashpad_tests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Windows-10-15063"
-            }
-          ]
-        },
-        "test": "cronet_tests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Windows-10-15063"
-            }
-          ]
-        },
-        "test": "cronet_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Windows-10-15063"
-            }
-          ]
-        },
-        "test": "crypto_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Windows-10-15063"
-            }
-          ]
-        },
-        "test": "device_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Windows-10-15063"
-            }
-          ]
-        },
-        "test": "display_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Windows-10-15063"
-            }
-          ]
-        },
-        "test": "elevation_service_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Windows-10-15063"
-            }
-          ]
-        },
-        "test": "events_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Windows-10-15063"
-            }
-          ]
-        },
-        "test": "extensions_browsertests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Windows-10-15063"
-            }
-          ]
-        },
-        "test": "extensions_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Windows-10-15063"
-            }
-          ]
-        },
-        "test": "filesystem_service_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Windows-10-15063"
-            }
-          ]
-        },
-        "test": "gcm_unit_tests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Windows-10-15063"
-            }
-          ]
-        },
-        "test": "gcp_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Windows-10-15063"
-            }
-          ]
-        },
-        "test": "gfx_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Windows-10-15063"
-            }
-          ]
-        },
-        "test": "gin_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Windows-10-15063"
-            }
-          ]
-        },
-        "test": "google_apis_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Windows-10-15063"
-            }
-          ]
-        },
-        "test": "gpu_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Windows-10-15063"
-            }
-          ]
-        },
-        "test": "headless_browsertests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Windows-10-15063"
-            }
-          ]
-        },
-        "test": "headless_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Windows-10-15063"
-            }
-          ]
-        },
-        "test": "install_static_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "integrity": "high",
-              "os": "Windows-10-15063"
-            }
-          ]
-        },
-        "test": "installer_util_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Windows-10-15063"
-            }
-          ],
-          "shards": 3
-        },
-        "test": "interactive_ui_tests"
-      },
-      {
-        "args": [
-          "--disable-features=WebUIPolymer2",
-          "--test-launcher-filter-file=../../testing/buildbot/filters/webui_polymer1_interactive_ui_tests.filter"
-        ],
-        "name": "webui_polymer1_interactive_ui_tests",
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Windows-10-15063"
-            }
-          ]
-        },
-        "test": "interactive_ui_tests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Windows-10-15063"
-            }
-          ]
-        },
-        "test": "ipc_tests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Windows-10-15063"
-            }
-          ]
-        },
-        "test": "jingle_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Windows-10-15063"
-            }
-          ]
-        },
-        "test": "latency_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Windows-10-15063"
-            }
-          ]
-        },
-        "test": "leveldb_service_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Windows-10-15063"
-            }
-          ]
-        },
-        "test": "libjingle_xmpp_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Windows-10-15063"
-            }
-          ]
-        },
-        "test": "media_blink_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Windows-10-15063"
-            }
-          ]
-        },
-        "test": "media_service_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Windows-10-15063"
-            }
-          ]
-        },
-        "test": "media_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Windows-10-15063"
-            }
-          ]
-        },
-        "test": "message_center_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Windows-10-15063"
-            }
-          ]
-        },
-        "test": "midi_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Windows-10-15063"
-            }
-          ]
-        },
-        "test": "mojo_core_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Windows-10-15063"
-            }
-          ]
-        },
-        "test": "mojo_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Windows-10-15063"
-            }
-          ]
-        },
-        "test": "nacl_loader_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Windows-10-15063"
-            }
-          ]
-        },
-        "test": "native_theme_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Windows-10-15063"
-            }
-          ]
-        },
-        "test": "net_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Windows-10-15063"
-            }
-          ]
-        },
-        "test": "notification_helper_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Windows-10-15063"
-            }
-          ]
-        },
-        "test": "pdf_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Windows-10-15063"
-            }
-          ]
-        },
-        "test": "ppapi_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Windows-10-15063"
-            }
-          ]
-        },
-        "test": "printing_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Windows-10-15063"
-            }
-          ]
-        },
-        "test": "remoting_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "integrity": "high",
-              "os": "Windows-10-15063"
-            }
-          ]
-        },
-        "test": "sbox_integration_tests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Windows-10-15063"
-            }
-          ]
-        },
-        "test": "sbox_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Windows-10-15063"
-            }
-          ]
-        },
-        "test": "sbox_validation_tests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Windows-10-15063"
-            }
-          ]
-        },
-        "test": "service_manager_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Windows-10-15063"
-            }
-          ]
-        },
-        "test": "services_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "integrity": "high",
-              "os": "Windows-10-15063"
-            }
-          ]
-        },
-        "test": "setup_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Windows-10-15063"
-            }
-          ]
-        },
-        "test": "shell_dialogs_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Windows-10-15063"
-            }
-          ]
-        },
-        "test": "skia_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Windows-10-15063"
-            }
-          ]
-        },
-        "test": "snapshot_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Windows-10-15063"
-            }
-          ]
-        },
-        "test": "sql_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Windows-10-15063"
-            }
-          ]
-        },
-        "test": "storage_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Windows-10-15063"
-            }
-          ]
-        },
-        "test": "sync_integration_tests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Windows-10-15063"
-            }
-          ]
-        },
-        "test": "traffic_annotation_auditor_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Windows-10-15063"
-            }
-          ]
-        },
-        "test": "ui_base_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Windows-10-15063"
-            }
-          ]
-        },
-        "test": "ui_touch_selection_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Windows-10-15063"
-            }
-          ]
-        },
-        "test": "unit_tests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Windows-10-15063"
-            }
-          ]
-        },
-        "test": "url_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Windows-10-15063"
-            }
-          ]
-        },
-        "test": "views_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Windows-10-15063"
-            }
-          ]
-        },
-        "test": "viz_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Windows-10-15063"
-            }
-          ]
-        },
-        "test": "vr_common_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Windows-10-15063"
-            }
-          ]
-        },
-        "test": "vr_pixeltests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Windows-10-15063"
-            }
-          ]
-        },
-        "test": "wm_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Windows-10-15063"
-            }
-          ]
-        },
-        "test": "wtf_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Windows-10-15063"
-            }
-          ]
-        },
-        "test": "zucchini_unittests"
-      }
-    ]
-  },
   "ToTAndroid": {
     "additional_compile_targets": [
       "all"
@@ -9564,12 +8479,6 @@
         },
         "test": "xr_browser_tests"
       }
-    ],
-    "scripts": [
-      {
-        "name": "check_gn_headers",
-        "script": "check_gn_headers.py"
-      }
     ]
   },
   "ToTLinux (dbg)": {
@@ -11484,12 +10393,6 @@
         },
         "test": "xr_browser_tests"
       }
-    ],
-    "scripts": [
-      {
-        "name": "check_gn_headers",
-        "script": "check_gn_headers.py"
-      }
     ]
   },
   "ToTLinuxTSan": {
diff --git a/testing/buildbot/chromium.fyi.json b/testing/buildbot/chromium.fyi.json
index 7e91a89..8b70df3 100644
--- a/testing/buildbot/chromium.fyi.json
+++ b/testing/buildbot/chromium.fyi.json
@@ -3033,6 +3033,11 @@
       "libANGLE"
     ]
   },
+  "Win cl.exe Goma Latest Client LocalOutputCache": {
+    "additional_compile_targets": [
+      "libANGLE"
+    ]
+  },
   "Win7 Builder (dbg) Goma Canary": {
     "additional_compile_targets": [
       "all"
@@ -3077,6 +3082,11 @@
       "libANGLE"
     ]
   },
+  "WinMSVC64 Goma Latest Client": {
+    "additional_compile_targets": [
+      "libANGLE"
+    ]
+  },
   "android-mojo-webview-rel": {
     "gtest_tests": [
       {
diff --git a/testing/buildbot/chromium.goma.json b/testing/buildbot/chromium.goma.json
index 6c7c994..c34fc2a 100644
--- a/testing/buildbot/chromium.goma.json
+++ b/testing/buildbot/chromium.goma.json
@@ -153,6 +153,25 @@
       }
     ]
   },
+  "Chromium Mac Goma RBE ToT": {
+    "additional_compile_targets": [
+      "chrome"
+    ],
+    "gtest_tests": [
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "base_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "content_unittests"
+      }
+    ]
+  },
   "Chromium Mac Goma Staging": {
     "additional_compile_targets": [
       "all"
diff --git a/testing/buildbot/chromium.perf.fyi.json b/testing/buildbot/chromium.perf.fyi.json
index bd82d46..b35b4e6 100644
--- a/testing/buildbot/chromium.perf.fyi.json
+++ b/testing/buildbot/chromium.perf.fyi.json
@@ -39,6 +39,7 @@
             "--multiple-dimension-script-verbose",
             "True"
           ],
+          "requires_simultaneous_shard_dispatch": true,
           "script": "//testing/trigger_scripts/perf_device_trigger.py"
         }
       },
@@ -80,6 +81,7 @@
             "--multiple-dimension-script-verbose",
             "True"
           ],
+          "requires_simultaneous_shard_dispatch": true,
           "script": "//testing/trigger_scripts/perf_device_trigger.py"
         }
       }
@@ -125,6 +127,7 @@
             "--multiple-dimension-script-verbose",
             "True"
           ],
+          "requires_simultaneous_shard_dispatch": true,
           "script": "//testing/trigger_scripts/perf_device_trigger.py"
         }
       }
@@ -170,6 +173,7 @@
             "--multiple-dimension-script-verbose",
             "True"
           ],
+          "requires_simultaneous_shard_dispatch": true,
           "script": "//testing/trigger_scripts/perf_device_trigger.py"
         }
       }
@@ -215,6 +219,7 @@
             "--multiple-dimension-script-verbose",
             "True"
           ],
+          "requires_simultaneous_shard_dispatch": true,
           "script": "//testing/trigger_scripts/perf_device_trigger.py"
         }
       }
@@ -260,6 +265,7 @@
             "--multiple-dimension-script-verbose",
             "True"
           ],
+          "requires_simultaneous_shard_dispatch": true,
           "script": "//testing/trigger_scripts/perf_device_trigger.py"
         }
       }
diff --git a/testing/buildbot/chromium.perf.json b/testing/buildbot/chromium.perf.json
index 0e5819e5..833472e 100644
--- a/testing/buildbot/chromium.perf.json
+++ b/testing/buildbot/chromium.perf.json
@@ -38,6 +38,7 @@
             "--multiple-dimension-script-verbose",
             "True"
           ],
+          "requires_simultaneous_shard_dispatch": true,
           "script": "//testing/trigger_scripts/perf_device_trigger.py"
         }
       },
@@ -76,6 +77,7 @@
             "--multiple-dimension-script-verbose",
             "True"
           ],
+          "requires_simultaneous_shard_dispatch": true,
           "script": "//testing/trigger_scripts/perf_device_trigger.py"
         }
       },
@@ -114,6 +116,7 @@
             "--multiple-dimension-script-verbose",
             "True"
           ],
+          "requires_simultaneous_shard_dispatch": true,
           "script": "//testing/trigger_scripts/perf_device_trigger.py"
         }
       },
@@ -156,6 +159,7 @@
             "--multiple-dimension-script-verbose",
             "True"
           ],
+          "requires_simultaneous_shard_dispatch": true,
           "script": "//testing/trigger_scripts/perf_device_trigger.py"
         }
       }
@@ -202,6 +206,7 @@
             "--multiple-dimension-script-verbose",
             "True"
           ],
+          "requires_simultaneous_shard_dispatch": true,
           "script": "//testing/trigger_scripts/perf_device_trigger.py"
         }
       }
@@ -248,6 +253,7 @@
             "--multiple-dimension-script-verbose",
             "True"
           ],
+          "requires_simultaneous_shard_dispatch": true,
           "script": "//testing/trigger_scripts/perf_device_trigger.py"
         }
       }
@@ -288,6 +294,7 @@
             "--multiple-dimension-script-verbose",
             "True"
           ],
+          "requires_simultaneous_shard_dispatch": true,
           "script": "//testing/trigger_scripts/perf_device_trigger.py"
         }
       },
@@ -324,6 +331,7 @@
             "--multiple-dimension-script-verbose",
             "True"
           ],
+          "requires_simultaneous_shard_dispatch": true,
           "script": "//testing/trigger_scripts/perf_device_trigger.py"
         }
       },
@@ -360,6 +368,7 @@
             "--multiple-dimension-script-verbose",
             "True"
           ],
+          "requires_simultaneous_shard_dispatch": true,
           "script": "//testing/trigger_scripts/perf_device_trigger.py"
         }
       },
@@ -398,6 +407,7 @@
             "--multiple-dimension-script-verbose",
             "True"
           ],
+          "requires_simultaneous_shard_dispatch": true,
           "script": "//testing/trigger_scripts/perf_device_trigger.py"
         }
       },
@@ -436,6 +446,7 @@
             "--multiple-dimension-script-verbose",
             "True"
           ],
+          "requires_simultaneous_shard_dispatch": true,
           "script": "//testing/trigger_scripts/perf_device_trigger.py"
         }
       },
@@ -476,6 +487,7 @@
             "--multiple-dimension-script-verbose",
             "True"
           ],
+          "requires_simultaneous_shard_dispatch": true,
           "script": "//testing/trigger_scripts/perf_device_trigger.py"
         }
       }
@@ -516,6 +528,7 @@
             "--multiple-dimension-script-verbose",
             "True"
           ],
+          "requires_simultaneous_shard_dispatch": true,
           "script": "//testing/trigger_scripts/perf_device_trigger.py"
         }
       },
@@ -552,6 +565,7 @@
             "--multiple-dimension-script-verbose",
             "True"
           ],
+          "requires_simultaneous_shard_dispatch": true,
           "script": "//testing/trigger_scripts/perf_device_trigger.py"
         }
       },
@@ -588,6 +602,7 @@
             "--multiple-dimension-script-verbose",
             "True"
           ],
+          "requires_simultaneous_shard_dispatch": true,
           "script": "//testing/trigger_scripts/perf_device_trigger.py"
         }
       },
@@ -627,6 +642,7 @@
             "--multiple-dimension-script-verbose",
             "True"
           ],
+          "requires_simultaneous_shard_dispatch": true,
           "script": "//testing/trigger_scripts/perf_device_trigger.py"
         }
       }
@@ -679,6 +695,7 @@
             "--multiple-dimension-script-verbose",
             "True"
           ],
+          "requires_simultaneous_shard_dispatch": true,
           "script": "//testing/trigger_scripts/perf_device_trigger.py"
         }
       }
@@ -724,6 +741,7 @@
             "--multiple-dimension-script-verbose",
             "True"
           ],
+          "requires_simultaneous_shard_dispatch": true,
           "script": "//testing/trigger_scripts/perf_device_trigger.py"
         }
       }
@@ -767,6 +785,7 @@
             "--multiple-dimension-script-verbose",
             "True"
           ],
+          "requires_simultaneous_shard_dispatch": true,
           "script": "//testing/trigger_scripts/perf_device_trigger.py"
         }
       },
@@ -805,6 +824,7 @@
             "--multiple-dimension-script-verbose",
             "True"
           ],
+          "requires_simultaneous_shard_dispatch": true,
           "script": "//testing/trigger_scripts/perf_device_trigger.py"
         }
       },
@@ -843,6 +863,7 @@
             "--multiple-dimension-script-verbose",
             "True"
           ],
+          "requires_simultaneous_shard_dispatch": true,
           "script": "//testing/trigger_scripts/perf_device_trigger.py"
         }
       },
@@ -881,6 +902,7 @@
             "--multiple-dimension-script-verbose",
             "True"
           ],
+          "requires_simultaneous_shard_dispatch": true,
           "script": "//testing/trigger_scripts/perf_device_trigger.py"
         }
       },
@@ -919,6 +941,7 @@
             "--multiple-dimension-script-verbose",
             "True"
           ],
+          "requires_simultaneous_shard_dispatch": true,
           "script": "//testing/trigger_scripts/perf_device_trigger.py"
         }
       },
@@ -957,6 +980,7 @@
             "--multiple-dimension-script-verbose",
             "True"
           ],
+          "requires_simultaneous_shard_dispatch": true,
           "script": "//testing/trigger_scripts/perf_device_trigger.py"
         }
       },
@@ -999,6 +1023,7 @@
             "--multiple-dimension-script-verbose",
             "True"
           ],
+          "requires_simultaneous_shard_dispatch": true,
           "script": "//testing/trigger_scripts/perf_device_trigger.py"
         }
       }
@@ -1051,6 +1076,7 @@
             "--multiple-dimension-script-verbose",
             "True"
           ],
+          "requires_simultaneous_shard_dispatch": true,
           "script": "//testing/trigger_scripts/perf_device_trigger.py"
         }
       },
@@ -1087,6 +1113,7 @@
             "--multiple-dimension-script-verbose",
             "True"
           ],
+          "requires_simultaneous_shard_dispatch": true,
           "script": "//testing/trigger_scripts/perf_device_trigger.py"
         }
       },
@@ -1123,6 +1150,7 @@
             "--multiple-dimension-script-verbose",
             "True"
           ],
+          "requires_simultaneous_shard_dispatch": true,
           "script": "//testing/trigger_scripts/perf_device_trigger.py"
         }
       },
@@ -1159,6 +1187,7 @@
             "--multiple-dimension-script-verbose",
             "True"
           ],
+          "requires_simultaneous_shard_dispatch": true,
           "script": "//testing/trigger_scripts/perf_device_trigger.py"
         }
       },
@@ -1195,6 +1224,7 @@
             "--multiple-dimension-script-verbose",
             "True"
           ],
+          "requires_simultaneous_shard_dispatch": true,
           "script": "//testing/trigger_scripts/perf_device_trigger.py"
         }
       },
@@ -1231,6 +1261,7 @@
             "--multiple-dimension-script-verbose",
             "True"
           ],
+          "requires_simultaneous_shard_dispatch": true,
           "script": "//testing/trigger_scripts/perf_device_trigger.py"
         }
       },
@@ -1271,6 +1302,7 @@
             "--multiple-dimension-script-verbose",
             "True"
           ],
+          "requires_simultaneous_shard_dispatch": true,
           "script": "//testing/trigger_scripts/perf_device_trigger.py"
         }
       }
@@ -1311,6 +1343,7 @@
             "--multiple-dimension-script-verbose",
             "True"
           ],
+          "requires_simultaneous_shard_dispatch": true,
           "script": "//testing/trigger_scripts/perf_device_trigger.py"
         }
       },
@@ -1347,6 +1380,7 @@
             "--multiple-dimension-script-verbose",
             "True"
           ],
+          "requires_simultaneous_shard_dispatch": true,
           "script": "//testing/trigger_scripts/perf_device_trigger.py"
         }
       },
@@ -1387,6 +1421,7 @@
             "--multiple-dimension-script-verbose",
             "True"
           ],
+          "requires_simultaneous_shard_dispatch": true,
           "script": "//testing/trigger_scripts/perf_device_trigger.py"
         }
       }
@@ -1427,6 +1462,7 @@
             "--multiple-dimension-script-verbose",
             "True"
           ],
+          "requires_simultaneous_shard_dispatch": true,
           "script": "//testing/trigger_scripts/perf_device_trigger.py"
         }
       },
@@ -1463,6 +1499,7 @@
             "--multiple-dimension-script-verbose",
             "True"
           ],
+          "requires_simultaneous_shard_dispatch": true,
           "script": "//testing/trigger_scripts/perf_device_trigger.py"
         }
       },
@@ -1499,6 +1536,7 @@
             "--multiple-dimension-script-verbose",
             "True"
           ],
+          "requires_simultaneous_shard_dispatch": true,
           "script": "//testing/trigger_scripts/perf_device_trigger.py"
         }
       },
@@ -1535,6 +1573,7 @@
             "--multiple-dimension-script-verbose",
             "True"
           ],
+          "requires_simultaneous_shard_dispatch": true,
           "script": "//testing/trigger_scripts/perf_device_trigger.py"
         }
       },
@@ -1571,6 +1610,7 @@
             "--multiple-dimension-script-verbose",
             "True"
           ],
+          "requires_simultaneous_shard_dispatch": true,
           "script": "//testing/trigger_scripts/perf_device_trigger.py"
         }
       },
@@ -1611,6 +1651,7 @@
             "--multiple-dimension-script-verbose",
             "True"
           ],
+          "requires_simultaneous_shard_dispatch": true,
           "script": "//testing/trigger_scripts/perf_device_trigger.py"
         }
       }
@@ -1657,6 +1698,7 @@
             "--multiple-dimension-script-verbose",
             "True"
           ],
+          "requires_simultaneous_shard_dispatch": true,
           "script": "//testing/trigger_scripts/perf_device_trigger.py"
         }
       },
@@ -1693,6 +1735,7 @@
             "--multiple-dimension-script-verbose",
             "True"
           ],
+          "requires_simultaneous_shard_dispatch": true,
           "script": "//testing/trigger_scripts/perf_device_trigger.py"
         }
       },
@@ -1729,6 +1772,7 @@
             "--multiple-dimension-script-verbose",
             "True"
           ],
+          "requires_simultaneous_shard_dispatch": true,
           "script": "//testing/trigger_scripts/perf_device_trigger.py"
         }
       },
@@ -1765,6 +1809,7 @@
             "--multiple-dimension-script-verbose",
             "True"
           ],
+          "requires_simultaneous_shard_dispatch": true,
           "script": "//testing/trigger_scripts/perf_device_trigger.py"
         }
       },
@@ -1801,6 +1846,7 @@
             "--multiple-dimension-script-verbose",
             "True"
           ],
+          "requires_simultaneous_shard_dispatch": true,
           "script": "//testing/trigger_scripts/perf_device_trigger.py"
         }
       },
@@ -1841,6 +1887,7 @@
             "--multiple-dimension-script-verbose",
             "True"
           ],
+          "requires_simultaneous_shard_dispatch": true,
           "script": "//testing/trigger_scripts/perf_device_trigger.py"
         }
       }
diff --git a/testing/buildbot/generate_buildbot_json.py b/testing/buildbot/generate_buildbot_json.py
index a2ec27ac..6600efde 100755
--- a/testing/buildbot/generate_buildbot_json.py
+++ b/testing/buildbot/generate_buildbot_json.py
@@ -933,6 +933,8 @@
       # chromium, due to https://crbug.com/878915
       'win-dbg',
       'win32-dbg',
+      # Defined in internal configs.
+      'chromeos-amd64-generic-google-rel',
     ]
 
   def check_input_file_consistency(self, verbose=False):
diff --git a/testing/buildbot/test_suite_exceptions.pyl b/testing/buildbot/test_suite_exceptions.pyl
index 740e67f..6db509e 100644
--- a/testing/buildbot/test_suite_exceptions.pyl
+++ b/testing/buildbot/test_suite_exceptions.pyl
@@ -77,7 +77,6 @@
     'remove_from': [
       # chromium.clang
       'CrWinAsan(dll)', # https://crbug.com/935598
-      'CrWinAsanCov',
       'linux-win_cross-rel',
       'ToTLinuxTSan',  # https://crbug.com/368525
       # chromium.memory
diff --git a/testing/buildbot/test_suites.pyl b/testing/buildbot/test_suites.pyl
index bdfd854..a5e8562 100644
--- a/testing/buildbot/test_suites.pyl
+++ b/testing/buildbot/test_suites.pyl
@@ -351,12 +351,6 @@
       'exo_unittests': {},
     },
 
-    'check_gn_headers_script': {
-      'check_gn_headers': {
-        'script': 'check_gn_headers.py',
-      }
-    },
-
     'check_network_annotations_script': {
       'check_network_annotations': {
         'script': 'check_network_annotations.py',
@@ -4079,6 +4073,51 @@
       'crashpad_tests': {},
     },
 
+    'non_network_service_gtests': {
+      'non_network_service_browser_tests': {
+        'args': [
+          '--disable-features=NetworkService',
+        ],
+        'swarming': {
+          'shards': 10,
+        },
+        'test': 'browser_tests',
+      },
+      'non_network_service_components_browsertests': {
+        'args': [
+          '--disable-features=NetworkService',
+        ],
+        'test': 'components_browsertests',
+      },
+      'non_network_service_content_browsertests': {
+        'args': [
+          '--disable-features=NetworkService',
+        ],
+        'test': 'content_browsertests',
+      },
+      'non_network_service_extensions_browsertests': {
+        'args': [
+          '--disable-features=NetworkService',
+        ],
+        'test': 'extensions_browsertests',
+      },
+      'non_network_service_interactive_ui_tests': {
+        'args': [
+          '--disable-features=NetworkService'
+        ],
+        'swarming': {
+          'shards': 3,
+        },
+        'test': 'interactive_ui_tests',
+      },
+      'non_network_service_sync_integration_tests': {
+        'args': [
+          '--disable-features=NetworkService'
+        ],
+        'test': 'sync_integration_tests',
+      },
+    },
+
     'non_network_service_isolated_scripts': {
       'non_network_service_webkit_layout_tests': {
         'args': [
@@ -4991,6 +5030,23 @@
       'viz_gtests',
     ],
 
+    'linux_chromeos_with_non_network_service_gtests': [
+      # This is:
+      #   linux_chromeos_gtests
+      #   + non_newtork_service_gtests
+      'aura_gtests',
+      'chromium_gtests',
+      'chromium_gtests_for_devices_with_graphical_output',
+      'chromium_gtests_for_linux_and_chromeos_only',
+      'chromium_gtests_for_win_and_linux_only',
+      'linux_chromeos_specific_gtests',
+      'linux_flavor_specific_chromium_gtests',
+      'non_android_chromium_gtests',
+      'non_network_service_gtests',
+      'viz_chromeos_gtests',
+      'viz_gtests',
+    ],
+
     'linux_viz_gtests': [
       'non_viz_fyi_chromium_gtests',
       'non_viz_non_android_fyi_chromium_gtests',
diff --git a/testing/buildbot/waterfalls.pyl b/testing/buildbot/waterfalls.pyl
index 5a6154bf..ec7b579 100644
--- a/testing/buildbot/waterfalls.pyl
+++ b/testing/buildbot/waterfalls.pyl
@@ -625,6 +625,11 @@
   {
     'name': 'chromium.chrome',
     'machines': {
+      'chromeos-amd64-generic-google-rel': {
+        'additional_compile_targets': [
+          'chrome',
+        ],
+      },
     },
   },
   {
@@ -680,7 +685,7 @@
       },
       'linux-chromeos-dbg': {
         'test_suites': {
-          'gtest_tests': 'linux_chromeos_gtests',
+          'gtest_tests': 'linux_chromeos_with_non_network_service_gtests',
         },
       },
       'linux-chromeos-rel': {
@@ -725,18 +730,6 @@
           ],
         },
       },
-      'CrWinAsanCov': {
-        'test_suites': {
-          'gtest_tests': 'chromium_win_gtests',
-        },
-        'swarming': {
-          'dimension_sets': [
-            {
-              'os': 'Windows-10-15063',
-            },
-          ],
-        },
-      },
       'ToTAndroid': {
         'additional_compile_targets': [
           'all',
@@ -806,7 +799,6 @@
       'ToTLinux': {
         'test_suites': {
           'gtest_tests': 'chromium_linux_gtests',
-          'scripts': 'check_gn_headers_script',
         },
       },
       'ToTLinux (dbg)': {
@@ -837,7 +829,6 @@
       'ToTLinuxOfficial': {
         'test_suites': {
           'gtest_tests': 'chromium_linux_gtests',
-          'scripts': 'check_gn_headers_script',
         },
       },
       'ToTLinuxTSan': {
@@ -1303,6 +1294,12 @@
           'libANGLE',
         ],
       },
+      'Win cl.exe Goma Latest Client LocalOutputCache': {
+        # Build only chromium's subproject using cl.exe.
+        'additional_compile_targets': [
+          'libANGLE',
+        ],
+      },
       'Win7 Builder (dbg) Goma Canary': {
         'additional_compile_targets': [
           'all',
@@ -1326,6 +1323,12 @@
           'libANGLE',
         ],
       },
+      'WinMSVC64 Goma Latest Client': {
+        # Build only chromium's subproject using cl.exe.
+        'additional_compile_targets': [
+          'libANGLE',
+        ],
+      },
       'android-mojo-webview-rel': {
         'swarming': {
           'dimension_sets': [
@@ -1689,10 +1692,10 @@
           'gtest_tests': 'goma_gtests',
         },
       },
+      # Due to disk shortage on Mac, we build 'chrome' instead of 'all'.
+      # See crbug.com/899425
       'Chromium Mac Goma RBE Staging': {
         'additional_compile_targets': [
-          # Due to disk shortage, we build 'chrome' instead of 'all'.
-          # See crbug.com/899425
           'chrome',
         ],
         'test_suites': {
@@ -1701,8 +1704,6 @@
       },
       'Chromium Mac Goma RBE Staging (clobber)': {
         'additional_compile_targets': [
-          # Due to disk shortage, we build 'chrome' instead of 'all'.
-          # See crbug.com/899425
           'chrome',
         ],
         'test_suites': {
@@ -1711,8 +1712,14 @@
       },
       'Chromium Mac Goma RBE Staging (dbg)': {
         'additional_compile_targets': [
-          # Due to disk shortage, we build 'chrome' instead of 'all'.
-          # See crbug.com/899425
+          'chrome',
+        ],
+        'test_suites': {
+          'gtest_tests': 'goma_gtests',
+        },
+      },
+      'Chromium Mac Goma RBE ToT': {
+        'additional_compile_targets': [
           'chrome',
         ],
         'test_suites': {
diff --git a/testing/trigger_scripts/chromeos_device_trigger.py b/testing/trigger_scripts/chromeos_device_trigger.py
index ce190317..71299e4 100755
--- a/testing/trigger_scripts/chromeos_device_trigger.py
+++ b/testing/trigger_scripts/chromeos_device_trigger.py
@@ -69,11 +69,16 @@
       help='How long to wait (in seconds) for an available bot in the primary '
            'task slice.')
   # BaseTestTriggerer's setup_parser_contract() takes care of adding needed
-  # swarming.py args if they're not already present.
-  base_test_triggerer.BaseTestTriggerer.setup_parser_contract(parser)
-  args, additional_args = parser.parse_known_args()
-  additional_args = triggerer.modify_args(
-      additional_args, 0, args.shard_index, args.shards, args.dump_json)
+  # swarming.py args if they're not already present. But only do this if
+  # '--shard-index' is passed in. (The exact usage of trigger scripts are
+  # currently changing. See crbug.com/926987 for more info.)
+  if '--shard-index' in sys.argv:
+    base_test_triggerer.BaseTestTriggerer.setup_parser_contract(parser)
+    args, additional_args = parser.parse_known_args()
+    additional_args = triggerer.modify_args(
+        additional_args, 0, args.shard_index, args.shards, args.dump_json)
+  else:
+    args, additional_args = parser.parse_known_args()
 
   if additional_args[0] != 'trigger':
     parser.error(
diff --git a/testing/variations/fieldtrial_testing_config.json b/testing/variations/fieldtrial_testing_config.json
index d7de8bb7..d6a573e 100644
--- a/testing/variations/fieldtrial_testing_config.json
+++ b/testing/variations/fieldtrial_testing_config.json
@@ -401,21 +401,20 @@
             ]
         }
     ],
-    "AndroidInProductHelpTranslateMenuButton": [
+    "AndroidInProductHelpManualTranslate": [
         {
             "platforms": [
                 "android"
             ],
             "experiments": [
                 {
-                    "name": "TranslateMenuButton",
+                    "name": "Enabled",
                     "params": {
                         "availability": "any",
-                        "event_trigger": "name:translate_menu_button_iph_triggered;comparator:<2;window:90;storage:360",
-                        "event_trigger_delay": "name:translate_menu_button_iph_triggered;comparator:<1;window:7;storage:360",
+                        "event_trigger": "name:translate_menu_button_iph_triggered;comparator:<2;window:30;storage:360",
+                        "event_trigger_delay": "name:translate_menu_button_iph_triggered;comparator:<1;window:1;storage:360",
                         "event_used": "name:translate_menu_button_clicked;comparator:==0;window:90;storage:360",
-                        "session_rate": "==0",
-                        "tracking_only": "true"
+                        "session_rate": "==0"
                     },
                     "enable_features": [
                         "IPH_TranslateMenuButton"
@@ -4350,25 +4349,6 @@
             ]
         }
     ],
-    "ScheduledScriptStreaming": [
-        {
-            "platforms": [
-                "android",
-                "chromeos",
-                "linux",
-                "mac",
-                "windows"
-            ],
-            "experiments": [
-                {
-                    "name": "ScheduledScriptStreaming",
-                    "enable_features": [
-                        "ScheduledScriptStreaming"
-                    ]
-                }
-            ]
-        }
-    ],
     "SearchEnginePromo": [
         {
             "platforms": [
diff --git a/third_party/binutils/download.py b/third_party/binutils/download.py
index f4ee5f9..78c8b4f1 100755
--- a/third_party/binutils/download.py
+++ b/third_party/binutils/download.py
@@ -9,6 +9,7 @@
 TODO(mithro): Replace with generic download_and_extract tool.
 """
 
+from __future__ import print_function
 import argparse
 import os
 import platform
@@ -48,7 +49,7 @@
 
   sha1file = tarball + '.sha1'
   if not os.path.exists(sha1file):
-    print "WARNING: No binutils found for your architecture (%s)!" % arch
+    print("WARNING: No binutils found for your architecture (%s)!" % arch)
     return 0
 
   checksum = ReadFile(sha1file)
@@ -62,7 +63,7 @@
     else:
       os.unlink(stampfile)
 
-  print "Downloading", tarball
+  print("Downloading", tarball)
   subprocess.check_call([
       'download_from_google_storage',
       '--no_resume',
@@ -77,7 +78,7 @@
   os.makedirs(outdir)
   assert os.path.exists(outdir)
 
-  print "Extracting", tarball
+  print("Extracting", tarball)
   subprocess.check_call(['tar', 'axf', tarball], cwd=outdir)
 
   for tool in BINUTILS_TOOLS:
@@ -111,7 +112,7 @@
     # Fetch the x64 toolchain as well for official bots with 64-bit kernels.
     return FetchAndExtract('x64')
 
-  print "Host architecture %s is not supported." % arch
+  print("Host architecture %s is not supported." % arch)
   return 1
 
 
diff --git a/third_party/blink/common/feature_policy/feature_policy.cc b/third_party/blink/common/feature_policy/feature_policy.cc
index 3b48bef6..eec0d5e 100644
--- a/third_party/blink/common/feature_policy/feature_policy.cc
+++ b/third_party/blink/common/feature_policy/feature_policy.cc
@@ -172,6 +172,7 @@
     const url::Origin& origin) const {
   DCHECK(base::ContainsKey(feature_list_, feature));
   DCHECK(base::ContainsKey(inherited_policies_, feature));
+
   auto inherited_value = inherited_policies_.at(feature);
   auto allowlist = allowlists_.find(feature);
   if (allowlist != allowlists_.end()) {
@@ -387,7 +388,7 @@
                             mojom::PolicyValueType::kBool)},
        {mojom::FeaturePolicyFeature::kOversizedImages,
         FeatureDefaultValue(FeaturePolicy::FeatureDefault::EnableForAll,
-                            mojom::PolicyValueType::kBool)},
+                            mojom::PolicyValueType::kDecDouble)},
        {mojom::FeaturePolicyFeature::kMicrophone,
         FeatureDefaultValue(FeaturePolicy::FeatureDefault::EnableForSelf,
                             mojom::PolicyValueType::kBool)},
diff --git a/third_party/blink/common/feature_policy/feature_policy_mojom_traits.cc b/third_party/blink/common/feature_policy/feature_policy_mojom_traits.cc
index 36548ec..14ed005 100644
--- a/third_party/blink/common/feature_policy/feature_policy_mojom_traits.cc
+++ b/third_party/blink/common/feature_policy/feature_policy_mojom_traits.cc
@@ -21,8 +21,13 @@
     Read(blink::mojom::PolicyValueDataDataView in, blink::PolicyValue* out) {
   switch (in.tag()) {
     case blink::mojom::PolicyValueDataDataView::Tag::BOOL_VALUE:
+      out->SetType(blink::mojom::PolicyValueType::kBool);
       out->SetBoolValue(in.bool_value());
       return true;
+    case blink::mojom::PolicyValueDataDataView::Tag::DEC_DOUBLE_VALUE:
+      out->SetType(blink::mojom::PolicyValueType::kDecDouble);
+      out->SetDoubleValue(in.dec_double_value());
+      return true;
     case blink::mojom::PolicyValueDataDataView::Tag::NULL_VALUE:
       break;
   }
diff --git a/third_party/blink/common/feature_policy/feature_policy_mojom_traits.h b/third_party/blink/common/feature_policy/feature_policy_mojom_traits.h
index b59ed32..9087316f 100644
--- a/third_party/blink/common/feature_policy/feature_policy_mojom_traits.h
+++ b/third_party/blink/common/feature_policy/feature_policy_mojom_traits.h
@@ -84,6 +84,8 @@
         return blink::mojom::PolicyValueDataDataView::Tag::NULL_VALUE;
       case blink::mojom::PolicyValueType::kBool:
         return blink::mojom::PolicyValueDataDataView::Tag::BOOL_VALUE;
+      case blink::mojom::PolicyValueType::kDecDouble:
+        return blink::mojom::PolicyValueDataDataView::Tag::DEC_DOUBLE_VALUE;
     }
 
     NOTREACHED();
@@ -93,6 +95,9 @@
   static bool bool_value(const blink::PolicyValue& value) {
     return value.BoolValue();
   }
+  static double dec_double_value(const blink::PolicyValue& value) {
+    return value.DoubleValue();
+  }
   static bool Read(blink::mojom::PolicyValueDataDataView in,
                    blink::PolicyValue* out);
 };
diff --git a/third_party/blink/common/feature_policy/feature_policy_unittest.cc b/third_party/blink/common/feature_policy/feature_policy_unittest.cc
index e578620..1def6f78 100644
--- a/third_party/blink/common/feature_policy/feature_policy_unittest.cc
+++ b/third_party/blink/common/feature_policy/feature_policy_unittest.cc
@@ -28,6 +28,10 @@
     static_cast<mojom::FeaturePolicyFeature>(
         static_cast<int>(mojom::FeaturePolicyFeature::kMaxValue) + 4);
 
+mojom::FeaturePolicyFeature kParameterizedDecDoubleFeature =
+    static_cast<mojom::FeaturePolicyFeature>(
+        static_cast<int>(mojom::FeaturePolicyFeature::kMaxValue) + 5);
+
 }  // namespace
 
 class FeaturePolicyTest : public testing::Test {
@@ -44,7 +48,14 @@
                        {kDefaultOffFeature,
                         FeaturePolicy::FeatureDefaultValue(
                             FeaturePolicy::FeatureDefault::DisableForAll,
-                            mojom::PolicyValueType::kBool)}}) {}
+                            mojom::PolicyValueType::kBool)},
+                       // Currently all parameterized features are by default
+                       // enabled for all, test for parameterized features with
+                       // different FeatureDefault if required.
+                       {kParameterizedDecDoubleFeature,
+                        FeaturePolicy::FeatureDefaultValue(
+                            FeaturePolicy::FeatureDefault::EnableForAll,
+                            mojom::PolicyValueType::kDecDouble)}}) {}
 
   ~FeaturePolicyTest() override = default;
 
@@ -76,6 +87,10 @@
 
   const PolicyValue min_value = PolicyValue(false);
   const PolicyValue max_value = PolicyValue(true);
+  const PolicyValue double_value =
+      PolicyValue(2.5, mojom::PolicyValueType::kDecDouble);
+  const PolicyValue min_double_value =
+      PolicyValue(2.0, mojom::PolicyValueType::kDecDouble);
 
  private:
   // Contains the list of controlled features, so that we are guaranteed to
@@ -94,6 +109,9 @@
       CreateFromParentPolicy(nullptr, origin_a_);
   EXPECT_TRUE(policy1->IsFeatureEnabled(kDefaultOnFeature));
   EXPECT_TRUE(policy1->IsFeatureEnabled(kDefaultSelfFeature));
+  EXPECT_TRUE(policy1->IsFeatureEnabled(kParameterizedDecDoubleFeature));
+  EXPECT_TRUE(
+      policy1->IsFeatureEnabled(kParameterizedDecDoubleFeature, double_value));
   EXPECT_FALSE(policy1->IsFeatureEnabled(kDefaultOffFeature));
 }
 
@@ -114,6 +132,9 @@
       CreateFromParentPolicy(policy1.get(), origin_a_);
   EXPECT_TRUE(policy2->IsFeatureEnabled(kDefaultOnFeature));
   EXPECT_TRUE(policy2->IsFeatureEnabled(kDefaultSelfFeature));
+  EXPECT_TRUE(policy2->IsFeatureEnabled(kParameterizedDecDoubleFeature));
+  EXPECT_TRUE(
+      policy2->IsFeatureEnabled(kParameterizedDecDoubleFeature, double_value));
   EXPECT_FALSE(policy2->IsFeatureEnabled(kDefaultOffFeature));
 }
 
@@ -133,6 +154,9 @@
   std::unique_ptr<FeaturePolicy> policy2 =
       CreateFromParentPolicy(policy1.get(), origin_b_);
   EXPECT_TRUE(policy2->IsFeatureEnabled(kDefaultOnFeature));
+  EXPECT_TRUE(policy2->IsFeatureEnabled(kParameterizedDecDoubleFeature));
+  EXPECT_TRUE(
+      policy2->IsFeatureEnabled(kParameterizedDecDoubleFeature, double_value));
   EXPECT_FALSE(policy2->IsFeatureEnabled(kDefaultSelfFeature));
   EXPECT_FALSE(policy2->IsFeatureEnabled(kDefaultOffFeature));
 }
@@ -292,14 +316,14 @@
 }
 
 TEST_F(FeaturePolicyTest, TestChildPolicyCanBlockSelf) {
-  // +--------------------------------------+
-  // |(1)Origin A                           |
-  // |No Policy                             |
-  // | +----------------------------------+ |
-  // | |(2)Origin B                       | |
-  // | |Feature-Policy: default-on 'none' | |
-  // | +----------------------------------+ |
-  // +--------------------------------------+
+  // +-------------------------------------------------------------+
+  // |(1)Origin A                                                  |
+  // |No Policy                                                    |
+  // | +---------------------------------------------------------+ |
+  // | |(2)Origin B                                              | |
+  // | |Feature-Policy: default-on 'none'; double-feature 'none' | |
+  // | +---------------------------------------------------------+ |
+  // +-------------------------------------------------------------+
   // Default-on feature should be disabled by cross-origin child frame.
   std::unique_ptr<FeaturePolicy> policy1 =
       CreateFromParentPolicy(nullptr, origin_a_);
@@ -307,23 +331,30 @@
       CreateFromParentPolicy(policy1.get(), origin_b_);
   policy2->SetHeaderPolicy(
       {{{kDefaultOnFeature, std::map<url::Origin, PolicyValue>{}, min_value,
-         min_value}}});
+         min_value},
+        {kParameterizedDecDoubleFeature, std::map<url::Origin, PolicyValue>{},
+         min_double_value, min_double_value}}});
   EXPECT_FALSE(policy2->IsFeatureEnabled(kDefaultOnFeature));
+  EXPECT_FALSE(policy2->IsFeatureEnabled(kParameterizedDecDoubleFeature));
+  EXPECT_FALSE(
+      policy2->IsFeatureEnabled(kParameterizedDecDoubleFeature, double_value));
+  EXPECT_TRUE(policy2->IsFeatureEnabled(kParameterizedDecDoubleFeature,
+                                        min_double_value));
 }
 
 TEST_F(FeaturePolicyTest, TestChildPolicyCanBlockChildren) {
-  // +--------------------------------------+
-  // |(1)Origin A                           |
-  // |No Policy                             |
-  // | +----------------------------------+ |
-  // | |(2)Origin B                       | |
-  // | |Feature-Policy: default-on 'self' | |
-  // | | +-------------+                  | |
-  // | | |(3)Origin C  |                  | |
-  // | | |No Policy    |                  | |
-  // | | +-------------+                  | |
-  // | +----------------------------------+ |
-  // +--------------------------------------+
+  // +------------------------------------------------------------------+
+  // |(1)Origin A                                                       |
+  // |No Policy                                                         |
+  // | +--------------------------------------------------------------+ |
+  // | |(2)Origin B                                                   | |
+  // | |Feature-Policy: default-on 'self'; double-feature 'self'(2.5) | |
+  // | | +-------------+                                              | |
+  // | | |(3)Origin C  |                                              | |
+  // | | |No Policy    |                                              | |
+  // | | +-------------+                                              | |
+  // | +--------------------------------------------------------------+ |
+  // +------------------------------------------------------------------+
   // Default-on feature should be enabled in frames 1 and 2; disabled in frame
   // 3 by child frame policy.
   std::unique_ptr<FeaturePolicy> policy1 =
@@ -333,11 +364,21 @@
   policy2->SetHeaderPolicy(
       {{{kDefaultOnFeature,
          std::map<url::Origin, PolicyValue>{{origin_b_, max_value}}, min_value,
-         min_value}}});
+         min_value},
+        {kParameterizedDecDoubleFeature,
+         std::map<url::Origin, PolicyValue>{{origin_b_, double_value}},
+         min_double_value, min_double_value}}});
   std::unique_ptr<FeaturePolicy> policy3 =
       CreateFromParentPolicy(policy2.get(), origin_c_);
   EXPECT_TRUE(policy2->IsFeatureEnabled(kDefaultOnFeature));
+  EXPECT_TRUE(
+      policy2->IsFeatureEnabled(kParameterizedDecDoubleFeature, double_value));
   EXPECT_FALSE(policy3->IsFeatureEnabled(kDefaultOnFeature));
+  EXPECT_FALSE(policy3->IsFeatureEnabled(kParameterizedDecDoubleFeature));
+  EXPECT_FALSE(
+      policy3->IsFeatureEnabled(kParameterizedDecDoubleFeature, double_value));
+  EXPECT_TRUE(policy3->IsFeatureEnabled(kParameterizedDecDoubleFeature,
+                                        min_double_value));
 }
 
 TEST_F(FeaturePolicyTest, TestParentPolicyBlocksCrossOriginChildPolicy) {
@@ -354,10 +395,17 @@
       CreateFromParentPolicy(nullptr, origin_a_);
   policy1->SetHeaderPolicy(
       {{{kDefaultOnFeature, std::map<url::Origin, PolicyValue>{}, min_value,
-         min_value}}});
+         min_value},
+        {kParameterizedDecDoubleFeature, std::map<url::Origin, PolicyValue>{},
+         min_double_value, min_double_value}}});
   std::unique_ptr<FeaturePolicy> policy2 =
       CreateFromParentPolicy(policy1.get(), origin_b_);
   EXPECT_FALSE(policy2->IsFeatureEnabled(kDefaultOnFeature));
+  EXPECT_FALSE(policy2->IsFeatureEnabled(kParameterizedDecDoubleFeature));
+  EXPECT_FALSE(
+      policy2->IsFeatureEnabled(kParameterizedDecDoubleFeature, double_value));
+  EXPECT_TRUE(policy2->IsFeatureEnabled(kParameterizedDecDoubleFeature,
+                                        min_double_value));
 }
 
 TEST_F(FeaturePolicyTest, TestEnableForAllOrigins) {
@@ -389,25 +437,28 @@
 }
 
 TEST_F(FeaturePolicyTest, TestDefaultOnEnablesForAllAncestors) {
-  // +---------------------------------------+
-  // |(1) Origin A                           |
-  // |Feature-Policy: default-on OriginB     |
-  // | +-----------------------------------+ |
-  // | |(2) Origin B                       | |
-  // | |No Policy                          | |
-  // | | +-------------+   +-------------+ | |
-  // | | |(3)Origin B  |   |(4)Origin C  | | |
-  // | | |No Policy    |   |No Policy    | | |
-  // | | +-------------+   +-------------+ | |
-  // | +-----------------------------------+ |
-  // +---------------------------------------+
+  // +--------------------------------------------------------------------+
+  // |(1) Origin A                                                        |
+  // |Feature-Policy: default-on OriginB; double-feature OriginB(2.5)     |
+  // | +-----------------------------------+                              |
+  // | |(2) Origin B                       |                              |
+  // | |No Policy                          |                              |
+  // | | +-------------+   +-------------+ |                              |
+  // | | |(3)Origin B  |   |(4)Origin C  | |                              |
+  // | | |No Policy    |   |No Policy    | |                              |
+  // | | +-------------+   +-------------+ |                              |
+  // | +-----------------------------------+                              |
+  // +--------------------------------------------------------------------+
   // Feature should be disabled in frame 1; enabled in frames 2, 3 and 4.
   std::unique_ptr<FeaturePolicy> policy1 =
       CreateFromParentPolicy(nullptr, origin_a_);
   policy1->SetHeaderPolicy(
       {{{kDefaultOnFeature,
          std::map<url::Origin, PolicyValue>{{origin_b_, max_value}}, min_value,
-         min_value}}});
+         min_value},
+        {kParameterizedDecDoubleFeature,
+         std::map<url::Origin, PolicyValue>{{origin_b_, double_value}},
+         min_double_value, min_double_value}}});
   std::unique_ptr<FeaturePolicy> policy2 =
       CreateFromParentPolicy(policy1.get(), origin_b_);
   std::unique_ptr<FeaturePolicy> policy3 =
@@ -415,9 +466,19 @@
   std::unique_ptr<FeaturePolicy> policy4 =
       CreateFromParentPolicy(policy2.get(), origin_c_);
   EXPECT_FALSE(policy1->IsFeatureEnabled(kDefaultOnFeature));
+  EXPECT_FALSE(
+      policy1->IsFeatureEnabled(kParameterizedDecDoubleFeature, double_value));
+  EXPECT_TRUE(policy1->IsFeatureEnabled(kParameterizedDecDoubleFeature,
+                                        min_double_value));
   EXPECT_TRUE(policy2->IsFeatureEnabled(kDefaultOnFeature));
+  EXPECT_TRUE(
+      policy2->IsFeatureEnabled(kParameterizedDecDoubleFeature, double_value));
   EXPECT_TRUE(policy3->IsFeatureEnabled(kDefaultOnFeature));
+  EXPECT_TRUE(
+      policy3->IsFeatureEnabled(kParameterizedDecDoubleFeature, double_value));
   EXPECT_TRUE(policy4->IsFeatureEnabled(kDefaultOnFeature));
+  EXPECT_TRUE(
+      policy4->IsFeatureEnabled(kParameterizedDecDoubleFeature, double_value));
 }
 
 TEST_F(FeaturePolicyTest, TestDefaultSelfRespectsSameOriginEmbedding) {
@@ -595,26 +656,32 @@
 }
 
 TEST_F(FeaturePolicyTest, TestEnabledFrameCanDelegateByDefault) {
-  // +-----------------------------------------------+
-  // |(1) Origin A                                   |
-  // |Feature-Policy: default-on 'self' OriginB      |
-  // | +--------------------+ +--------------------+ |
-  // | |(2) Origin B        | | (4) Origin C       | |
-  // | |No Policy           | | No Policy          | |
-  // | | +-------------+    | |                    | |
-  // | | |(3)Origin C  |    | |                    | |
-  // | | |No Policy    |    | |                    | |
-  // | | +-------------+    | |                    | |
-  // | +--------------------+ +--------------------+ |
-  // +-----------------------------------------------+
+  // +----------------------------------------------------------------------+
+  // |(1) Origin A                                                          |
+  // |Feature-Policy: default-on 'self' OriginB; double-feature 'self'(2.5) |
+  // |                OriginB(3)                                            |
+  // | +--------------------+ +--------------------+                        |
+  // | |(2) Origin B        | | (4) Origin C       |                        |
+  // | |No Policy           | | No Policy          |                        |
+  // | | +-------------+    | |                    |                        |
+  // | | |(3)Origin C  |    | |                    |                        |
+  // | | |No Policy    |    | |                    |                        |
+  // | | +-------------+    | |                    |                        |
+  // | +--------------------+ +--------------------+                        |
+  // +----------------------------------------------------------------------+
   // Feature should be enabled in frames 1, 2, and 3, and disabled in frame 4.
   std::unique_ptr<FeaturePolicy> policy1 =
       CreateFromParentPolicy(nullptr, origin_a_);
+  PolicyValue large_double_value(3.0, mojom::PolicyValueType::kDecDouble);
   policy1->SetHeaderPolicy(
       {{{kDefaultOnFeature,
          std::map<url::Origin, PolicyValue>{{origin_a_, max_value},
                                             {origin_b_, max_value}},
-         min_value, min_value}}});
+         min_value, min_value},
+        {kParameterizedDecDoubleFeature,
+         std::map<url::Origin, PolicyValue>{{origin_a_, double_value},
+                                            {origin_b_, large_double_value}},
+         min_double_value, min_double_value}}});
   std::unique_ptr<FeaturePolicy> policy2 =
       CreateFromParentPolicy(policy1.get(), origin_b_);
   std::unique_ptr<FeaturePolicy> policy3 =
@@ -622,9 +689,21 @@
   std::unique_ptr<FeaturePolicy> policy4 =
       CreateFromParentPolicy(policy1.get(), origin_c_);
   EXPECT_TRUE(policy1->IsFeatureEnabled(kDefaultOnFeature));
+  EXPECT_TRUE(
+      policy1->IsFeatureEnabled(kParameterizedDecDoubleFeature, double_value));
+  EXPECT_FALSE(policy1->IsFeatureEnabled(kParameterizedDecDoubleFeature,
+                                         large_double_value));
   EXPECT_TRUE(policy2->IsFeatureEnabled(kDefaultOnFeature));
+  EXPECT_TRUE(policy2->IsFeatureEnabled(kParameterizedDecDoubleFeature,
+                                        large_double_value));
   EXPECT_TRUE(policy3->IsFeatureEnabled(kDefaultOnFeature));
+  EXPECT_TRUE(policy3->IsFeatureEnabled(kParameterizedDecDoubleFeature,
+                                        large_double_value));
   EXPECT_FALSE(policy4->IsFeatureEnabled(kDefaultOnFeature));
+  EXPECT_FALSE(
+      policy4->IsFeatureEnabled(kParameterizedDecDoubleFeature, double_value));
+  EXPECT_TRUE(policy4->IsFeatureEnabled(kParameterizedDecDoubleFeature,
+                                        min_double_value));
 }
 
 TEST_F(FeaturePolicyTest, TestNonNestedFeaturesDontDelegateByDefault) {
@@ -803,59 +882,78 @@
 }
 
 TEST_F(FeaturePolicyTest, TestFramePolicyCanBeFurtherDelegated) {
-  // +------------------------------------------+
-  // |(1)Origin A                               |
-  // |No Policy                                 |
-  // |                                          |
-  // |<iframe allow="default-self OriginB">     |
-  // | +--------------------------------------+ |
-  // | |(2)Origin B                           | |
-  // | |No Policy                             | |
-  // | |                                      | |
-  // | |<iframe allow="default-self OriginC"> | |
-  // | | +-------------+                      | |
-  // | | |(3)Origin C  |                      | |
-  // | | |No Policy    |                      | |
-  // | | +-------------+                      | |
-  // | |                                      | |
-  // | |<iframe> (No frame policy)            | |
-  // | | +-------------+                      | |
-  // | | |(4)Origin C  |                      | |
-  // | | |No Policy    |                      | |
-  // | | +-------------+                      | |
-  // | +--------------------------------------+ |
-  // +------------------------------------------+
+  // +-----------------------------------------------------------------------+
+  // |(1)Origin A                                                            |
+  // |No Policy                                                              |
+  // |                                                                       |
+  // |<iframe allow="default-self OriginB; double-feature OriginB(2.5)">     |
+  // | +------------------------------------------------------------- -----+ |
+  // | |(2)Origin B                                                        | |
+  // | |No Policy                                                          | |
+  // | |                                                                   | |
+  // | |<iframe allow="default-self OriginC; double-feature OriginC(2.5)"> | |
+  // | | +-------------+                                                   | |
+  // | | |(3)Origin C  |                                                   | |
+  // | | |No Policy    |                                                   | |
+  // | | +-------------+                                                   | |
+  // | |                                                                   | |
+  // | |<iframe> (No frame policy)                                         | |
+  // | | +-------------+                                                   | |
+  // | | |(4)Origin C  |                                                   | |
+  // | | |No Policy    |                                                   | |
+  // | | +-------------+                                                   | |
+  // | +-------------------------------------------------------------------+ |
+  // +-----------------------------------------------------------------------+
   // Default-self feature should be enabled in cross-origin child frames 2 and
   // 3. Feature should be disabled in frame 4 because it was not further
   // delegated through frame policy.
+  // |double-feature| (default-all) should be enabled in any delegated subframe.
   std::unique_ptr<FeaturePolicy> policy1 =
       CreateFromParentPolicy(nullptr, origin_a_);
   ParsedFeaturePolicy frame_policy1 = {
       {{kDefaultSelfFeature,
         std::map<url::Origin, PolicyValue>{{origin_b_, max_value}}, min_value,
-        min_value}}};
+        min_value},
+       {kParameterizedDecDoubleFeature,
+        std::map<url::Origin, PolicyValue>{{origin_b_, double_value}},
+        min_double_value, min_double_value}}};
   std::unique_ptr<FeaturePolicy> policy2 =
       CreateFromParentWithFramePolicy(policy1.get(), frame_policy1, origin_b_);
   ParsedFeaturePolicy frame_policy2 = {
       {{kDefaultSelfFeature,
         std::map<url::Origin, PolicyValue>{{origin_c_, max_value}}, min_value,
-        min_value}}};
+        min_value},
+       {kParameterizedDecDoubleFeature,
+        std::map<url::Origin, PolicyValue>{{origin_c_, double_value}},
+        min_double_value, min_double_value}}};
   std::unique_ptr<FeaturePolicy> policy3 =
       CreateFromParentWithFramePolicy(policy2.get(), frame_policy2, origin_c_);
   std::unique_ptr<FeaturePolicy> policy4 =
       CreateFromParentPolicy(policy2.get(), origin_c_);
   EXPECT_FALSE(
       policy3->IsFeatureEnabledForOrigin(kDefaultSelfFeature, origin_a_));
+  EXPECT_TRUE(policy3->IsFeatureEnabledForOrigin(kParameterizedDecDoubleFeature,
+                                                 origin_a_, double_value));
   EXPECT_FALSE(
       policy3->IsFeatureEnabledForOrigin(kDefaultSelfFeature, origin_b_));
+  EXPECT_TRUE(policy3->IsFeatureEnabledForOrigin(kParameterizedDecDoubleFeature,
+                                                 origin_b_, double_value));
   EXPECT_TRUE(
       policy3->IsFeatureEnabledForOrigin(kDefaultSelfFeature, origin_c_));
+  EXPECT_TRUE(policy3->IsFeatureEnabledForOrigin(kParameterizedDecDoubleFeature,
+                                                 origin_c_, double_value));
   EXPECT_FALSE(
       policy4->IsFeatureEnabledForOrigin(kDefaultSelfFeature, origin_a_));
+  EXPECT_TRUE(policy4->IsFeatureEnabledForOrigin(kParameterizedDecDoubleFeature,
+                                                 origin_a_, double_value));
   EXPECT_FALSE(
       policy4->IsFeatureEnabledForOrigin(kDefaultSelfFeature, origin_b_));
+  EXPECT_TRUE(policy4->IsFeatureEnabledForOrigin(kParameterizedDecDoubleFeature,
+                                                 origin_b_, double_value));
   EXPECT_FALSE(
       policy4->IsFeatureEnabledForOrigin(kDefaultSelfFeature, origin_c_));
+  EXPECT_TRUE(policy4->IsFeatureEnabledForOrigin(kParameterizedDecDoubleFeature,
+                                                 origin_c_, double_value));
 }
 
 TEST_F(FeaturePolicyTest, TestDefaultOnCanBeDisabledByFramePolicy) {
@@ -881,29 +979,63 @@
       CreateFromParentPolicy(nullptr, origin_a_);
   ParsedFeaturePolicy frame_policy1 = {
       {{kDefaultOnFeature, std::map<url::Origin, PolicyValue>{}, min_value,
-        min_value}}};
+        min_value},
+       {kParameterizedDecDoubleFeature, std::map<url::Origin, PolicyValue>{},
+        min_double_value, min_double_value}}};
   std::unique_ptr<FeaturePolicy> policy2 =
       CreateFromParentWithFramePolicy(policy1.get(), frame_policy1, origin_a_);
   ParsedFeaturePolicy frame_policy2 = {
       {{kDefaultOnFeature, std::map<url::Origin, PolicyValue>{}, min_value,
-        min_value}}};
+        min_value},
+       {kParameterizedDecDoubleFeature, std::map<url::Origin, PolicyValue>{},
+        min_double_value, min_double_value}}};
   std::unique_ptr<FeaturePolicy> policy3 =
       CreateFromParentWithFramePolicy(policy1.get(), frame_policy2, origin_b_);
   EXPECT_TRUE(policy1->IsFeatureEnabledForOrigin(kDefaultOnFeature, origin_a_));
+  EXPECT_TRUE(policy1->IsFeatureEnabledForOrigin(kParameterizedDecDoubleFeature,
+                                                 origin_a_, double_value));
   EXPECT_TRUE(policy1->IsFeatureEnabledForOrigin(kDefaultOnFeature, origin_b_));
+  EXPECT_TRUE(policy1->IsFeatureEnabledForOrigin(kParameterizedDecDoubleFeature,
+                                                 origin_b_, double_value));
   EXPECT_TRUE(policy1->IsFeatureEnabledForOrigin(kDefaultOnFeature, origin_c_));
+  EXPECT_TRUE(policy1->IsFeatureEnabledForOrigin(kParameterizedDecDoubleFeature,
+                                                 origin_c_, double_value));
   EXPECT_FALSE(
       policy2->IsFeatureEnabledForOrigin(kDefaultOnFeature, origin_a_));
+  EXPECT_FALSE(policy2->IsFeatureEnabledForOrigin(
+      kParameterizedDecDoubleFeature, origin_a_, double_value));
+  EXPECT_TRUE(policy2->IsFeatureEnabledForOrigin(kParameterizedDecDoubleFeature,
+                                                 origin_a_, min_double_value));
   EXPECT_FALSE(
       policy2->IsFeatureEnabledForOrigin(kDefaultOnFeature, origin_b_));
+  EXPECT_FALSE(policy2->IsFeatureEnabledForOrigin(
+      kParameterizedDecDoubleFeature, origin_b_, double_value));
+  EXPECT_TRUE(policy2->IsFeatureEnabledForOrigin(kParameterizedDecDoubleFeature,
+                                                 origin_b_, min_double_value));
   EXPECT_FALSE(
       policy2->IsFeatureEnabledForOrigin(kDefaultOnFeature, origin_c_));
+  EXPECT_FALSE(policy2->IsFeatureEnabledForOrigin(
+      kParameterizedDecDoubleFeature, origin_c_, double_value));
+  EXPECT_TRUE(policy2->IsFeatureEnabledForOrigin(kParameterizedDecDoubleFeature,
+                                                 origin_c_, min_double_value));
   EXPECT_FALSE(
       policy3->IsFeatureEnabledForOrigin(kDefaultOnFeature, origin_a_));
+  EXPECT_FALSE(policy3->IsFeatureEnabledForOrigin(
+      kParameterizedDecDoubleFeature, origin_a_, double_value));
+  EXPECT_TRUE(policy3->IsFeatureEnabledForOrigin(kParameterizedDecDoubleFeature,
+                                                 origin_a_, min_double_value));
   EXPECT_FALSE(
       policy3->IsFeatureEnabledForOrigin(kDefaultOnFeature, origin_b_));
+  EXPECT_FALSE(policy3->IsFeatureEnabledForOrigin(
+      kParameterizedDecDoubleFeature, origin_b_, double_value));
+  EXPECT_TRUE(policy3->IsFeatureEnabledForOrigin(kParameterizedDecDoubleFeature,
+                                                 origin_b_, min_double_value));
   EXPECT_FALSE(
       policy3->IsFeatureEnabledForOrigin(kDefaultOnFeature, origin_c_));
+  EXPECT_FALSE(policy3->IsFeatureEnabledForOrigin(
+      kParameterizedDecDoubleFeature, origin_c_, double_value));
+  EXPECT_TRUE(policy3->IsFeatureEnabledForOrigin(kParameterizedDecDoubleFeature,
+                                                 origin_c_, min_double_value));
 }
 
 TEST_F(FeaturePolicyTest, TestDefaultOffMustBeEnabledByChildFrame) {
@@ -1030,22 +1162,24 @@
 }
 
 TEST_F(FeaturePolicyTest, TestFramePolicyModifiesHeaderPolicy) {
-  // +---------------------------------------------+
-  // |(1)Origin A                                  |
-  // |Feature-Policy: default-self 'self', OriginB |
-  // |                                             |
-  // |<iframe allow="default-self 'none'">         |
-  // | +-----------------------------------------+ |
-  // | |(2)Origin B                              | |
-  // | |No Policy                                | |
-  // | +-----------------------------------------+ |
-  // |                                             |
-  // |<iframe allow="default-self 'none'">         |
-  // | +-----------------------------------------+ |
-  // | |(3)Origin B                              | |
-  // | |Feature-Policy: default-self 'self'      | |
-  // | +-----------------------------------------+ |
-  // +---------------------------------------------+
+  // +--------------------------------------------------------------------+
+  // |(1)Origin A                                                         |
+  // |Feature-Policy: default-self 'self', OriginB                        |
+  // |                double-feature 'self'(2.5), OriginB(2.5)
+  // |                                                                    |
+  // |<iframe allow="default-self 'none'; double-feature 'none'">         |
+  // | +-----------------------------------------+                        |
+  // | |(2)Origin B                              |                        |
+  // | |No Policy                                |                        |
+  // | +-----------------------------------------+                        |
+  // |                                                                    |
+  // |<iframe allow="default-self 'none'; double-feature 'none'">         |
+  // | +-------------------------------------------+                      |
+  // | |(3)Origin B                                |                      |
+  // | |Feature-Policy: default-self 'self'        |                      |
+  // | |                double-feature 'self'(2.5) |                      |
+  // | +-------------------------------------------+                      |
+  // +--------------------------------------------------------------------+
   // Default-self feature should be disabled in both cross-origin child frames
   // by frame policy, even though the parent frame's header policy would
   // otherwise enable it. This is true regardless of the child frame's header
@@ -1056,50 +1190,68 @@
       {{{kDefaultSelfFeature,
          std::map<url::Origin, PolicyValue>{{origin_a_, max_value},
                                             {origin_b_, max_value}},
-         min_value, min_value}}});
+         min_value, min_value},
+        {kParameterizedDecDoubleFeature,
+         std::map<url::Origin, PolicyValue>{{origin_a_, double_value},
+                                            {origin_b_, double_value}},
+         min_double_value, min_double_value}}});
   ParsedFeaturePolicy frame_policy1 = {
       {{kDefaultSelfFeature, std::map<url::Origin, PolicyValue>{}, min_value,
-        min_value}}};
+        min_value},
+       {kParameterizedDecDoubleFeature, std::map<url::Origin, PolicyValue>{},
+        min_double_value, min_double_value}}};
   std::unique_ptr<FeaturePolicy> policy2 =
       CreateFromParentWithFramePolicy(policy1.get(), frame_policy1, origin_b_);
   ParsedFeaturePolicy frame_policy2 = {
       {{kDefaultSelfFeature, std::map<url::Origin, PolicyValue>{}, min_value,
-        min_value}}};
+        min_value},
+       {kParameterizedDecDoubleFeature, std::map<url::Origin, PolicyValue>{},
+        min_double_value, min_double_value}}};
   std::unique_ptr<FeaturePolicy> policy3 =
       CreateFromParentWithFramePolicy(policy1.get(), frame_policy2, origin_b_);
   policy3->SetHeaderPolicy(
       {{{kDefaultSelfFeature,
          std::map<url::Origin, PolicyValue>{{origin_b_, max_value}}, min_value,
-         min_value}}});
+         min_value},
+        {kParameterizedDecDoubleFeature,
+         std::map<url::Origin, PolicyValue>{{origin_b_, double_value}},
+         min_double_value, min_double_value}}});
   EXPECT_FALSE(
       policy2->IsFeatureEnabledForOrigin(kDefaultSelfFeature, origin_b_));
-  EXPECT_FALSE(
-      policy3->IsFeatureEnabledForOrigin(kDefaultSelfFeature, origin_b_));
+  EXPECT_FALSE(policy2->IsFeatureEnabledForOrigin(
+      kParameterizedDecDoubleFeature, origin_b_, double_value));
+  EXPECT_TRUE(policy2->IsFeatureEnabledForOrigin(kParameterizedDecDoubleFeature,
+                                                 origin_b_, min_double_value));
+  EXPECT_FALSE(policy3->IsFeatureEnabledForOrigin(
+      kParameterizedDecDoubleFeature, origin_b_, double_value));
+  EXPECT_TRUE(policy3->IsFeatureEnabledForOrigin(kParameterizedDecDoubleFeature,
+                                                 origin_b_, min_double_value));
 }
 
 TEST_F(FeaturePolicyTest, TestCombineFrameAndHeaderPolicies) {
-  // +-----------------------------------------+
-  // |(1)Origin A                              |
-  // |No Policy                                |
-  // |                                         |
-  // |<iframe allow="default-self OriginB">    |
-  // | +-------------------------------------+ |
-  // | |(2)Origin B                          | |
-  // | |Feature-Policy: default-self *       | |
-  // | |                                     | |
-  // | |<iframe allow="default-self 'none'"> | |
-  // | | +-------------+                     | |
-  // | | |(3)Origin C  |                     | |
-  // | | |No Policy    |                     | |
-  // | | +-------------+                     | |
-  // | |                                     | |
-  // | |<iframe> (No frame policy)           | |
-  // | | +-------------+                     | |
-  // | | |(4)Origin C  |                     | |
-  // | | |No Policy    |                     | |
-  // | | +-------------+                     | |
-  // | +-------------------------------------+ |
-  // +-----------------------------------------+
+  // +----------------------------------------------------------------------+
+  // |(1)Origin A                                                           |
+  // |No Policy                                                             |
+  // |                                                                      |
+  // |<iframe allow="default-self OriginB; double-feature OriginB(2.5)">    |
+  // | +------------------------------------------------------------------+ |
+  // | |(2)Origin B                                                       | |
+  // | |Feature-Policy: default-self *                                    | |
+  // | |                double-feature *(2.5)                             | |
+  // | |                                                                  | |
+  // | |<iframe allow="default-self 'none'; double-feature 'none'">       | |
+  // | | +-------------+                                                  | |
+  // | | |(3)Origin C  |                                                  | |
+  // | | |No Policy    |                                                  | |
+  // | | +-------------+                                                  | |
+  // | |                                                                  | |
+  // | |<iframe> (No frame policy)                                        | |
+  // | | +-------------+                                                  | |
+  // | | |(4)Origin C  |                                                  | |
+  // | | |No Policy    |                                                  | |
+  // | | +-------------+                                                  | |
+  // | +------------------------------------------------------------------+ |
+  // +----------------------------------------------------------------------+
   // Default-self feature should be enabled in cross-origin child frames 2 and
   // 4. Feature should be disabled in frame 3 by frame policy.
   std::unique_ptr<FeaturePolicy> policy1 =
@@ -1107,69 +1259,98 @@
   ParsedFeaturePolicy frame_policy1 = {
       {{kDefaultSelfFeature,
         std::map<url::Origin, PolicyValue>{{origin_b_, max_value}}, min_value,
-        min_value}}};
+        min_value},
+       {kParameterizedDecDoubleFeature,
+        std::map<url::Origin, PolicyValue>{{origin_b_, double_value}},
+        min_double_value, min_double_value}}};
   std::unique_ptr<FeaturePolicy> policy2 =
       CreateFromParentWithFramePolicy(policy1.get(), frame_policy1, origin_b_);
   policy2->SetHeaderPolicy(
       {{{kDefaultSelfFeature, std::map<url::Origin, PolicyValue>{}, max_value,
-         min_value}}});
+         min_value},
+        {kParameterizedDecDoubleFeature, std::map<url::Origin, PolicyValue>{},
+         double_value, min_double_value}}});
   ParsedFeaturePolicy frame_policy2 = {
       {{kDefaultSelfFeature, std::map<url::Origin, PolicyValue>{}, min_value,
-        min_value}}};
+        min_value},
+       {kParameterizedDecDoubleFeature, std::map<url::Origin, PolicyValue>{},
+        min_double_value, min_double_value}}};
   std::unique_ptr<FeaturePolicy> policy3 =
       CreateFromParentWithFramePolicy(policy2.get(), frame_policy2, origin_c_);
   std::unique_ptr<FeaturePolicy> policy4 =
       CreateFromParentPolicy(policy2.get(), origin_c_);
   EXPECT_TRUE(
       policy1->IsFeatureEnabledForOrigin(kDefaultSelfFeature, origin_a_));
+  EXPECT_TRUE(policy1->IsFeatureEnabledForOrigin(kParameterizedDecDoubleFeature,
+                                                 origin_a_, double_value));
   EXPECT_TRUE(
       policy2->IsFeatureEnabledForOrigin(kDefaultSelfFeature, origin_b_));
+  EXPECT_TRUE(policy2->IsFeatureEnabledForOrigin(kParameterizedDecDoubleFeature,
+                                                 origin_b_, double_value));
   EXPECT_FALSE(
       policy3->IsFeatureEnabledForOrigin(kDefaultSelfFeature, origin_c_));
-  EXPECT_TRUE(
-      policy4->IsFeatureEnabledForOrigin(kDefaultSelfFeature, origin_c_));
+  EXPECT_FALSE(policy3->IsFeatureEnabledForOrigin(
+      kParameterizedDecDoubleFeature, origin_c_, double_value));
+  EXPECT_TRUE(policy3->IsFeatureEnabledForOrigin(kParameterizedDecDoubleFeature,
+                                                 origin_c_, min_double_value));
+  EXPECT_TRUE(policy4->IsFeatureEnabledForOrigin(kParameterizedDecDoubleFeature,
+                                                 origin_c_, double_value));
 }
 
 TEST_F(FeaturePolicyTest, TestFeatureDeclinedAtTopLevel) {
-  // +-----------------------------------------+
-  // |(1)Origin A                              |
-  // |Feature-Policy: default-self 'none'      |
-  // |                                         |
-  // |<iframe allow="default-self OriginB">    |
-  // | +-------------------------------------+ |
-  // | |(2)Origin B                          | |
-  // | |No Policy                            | |
-  // | +-------------------------------------+ |
-  // |                                         |
-  // |<iframe allow="default-self *">          |
-  // | +-------------------------------------+ |
-  // | |(3)Origin A                          | |
-  // | |No Policy                            | |
-  // | +-------------------------------------+ |
-  // +-----------------------------------------+
+  // +--------------------------------------------------------------------+
+  // |(1)Origin A                                                         |
+  // |Feature-Policy: default-self 'none'                                 |
+  // |                double-feature 'none'                               |
+  // |                                                                    |
+  // |<iframe allow="default-self OriginB; double-feature OriginB(2.5)">  |
+  // | +-------------------------------------+                            |
+  // | |(2)Origin B                          |                            |
+  // | |No Policy                            |                            |
+  // | +-------------------------------------+                            |
+  // |                                                                    |
+  // |<iframe allow="default-self *; double-feature *(2.5)">              |
+  // | +-------------------------------------+                            |
+  // | |(3)Origin A                          |                            |
+  // | |No Policy                            |                            |
+  // | +-------------------------------------+                            |
+  // +--------------------------------------------------------------------+
   // Default-self feature should be disabled in all frames.
   std::unique_ptr<FeaturePolicy> policy1 =
       CreateFromParentPolicy(nullptr, origin_a_);
   policy1->SetHeaderPolicy(
       {{{kDefaultSelfFeature, std::map<url::Origin, PolicyValue>{}, min_value,
-         min_value}}});
+         min_value},
+        {kParameterizedDecDoubleFeature, std::map<url::Origin, PolicyValue>{},
+         min_double_value, min_double_value}}});
   ParsedFeaturePolicy frame_policy1 = {
       {{kDefaultSelfFeature,
         std::map<url::Origin, PolicyValue>{{origin_b_, max_value}}, min_value,
-        min_value}}};
+        min_value},
+       {kParameterizedDecDoubleFeature,
+        std::map<url::Origin, PolicyValue>{{origin_b_, double_value}},
+        min_double_value, min_double_value}}};
   std::unique_ptr<FeaturePolicy> policy2 =
       CreateFromParentWithFramePolicy(policy1.get(), frame_policy1, origin_b_);
   ParsedFeaturePolicy frame_policy2 = {
       {{kDefaultSelfFeature, std::map<url::Origin, PolicyValue>{}, max_value,
-        min_value}}};
+        min_value},
+       {kParameterizedDecDoubleFeature, std::map<url::Origin, PolicyValue>{},
+        double_value, min_double_value}}};
   std::unique_ptr<FeaturePolicy> policy3 =
       CreateFromParentWithFramePolicy(policy1.get(), frame_policy2, origin_a_);
   EXPECT_FALSE(
       policy1->IsFeatureEnabledForOrigin(kDefaultSelfFeature, origin_a_));
+  EXPECT_FALSE(policy1->IsFeatureEnabledForOrigin(
+      kParameterizedDecDoubleFeature, origin_a_, double_value));
   EXPECT_FALSE(
       policy2->IsFeatureEnabledForOrigin(kDefaultSelfFeature, origin_b_));
+  EXPECT_FALSE(policy2->IsFeatureEnabledForOrigin(
+      kParameterizedDecDoubleFeature, origin_b_, double_value));
   EXPECT_FALSE(
       policy3->IsFeatureEnabledForOrigin(kDefaultSelfFeature, origin_a_));
+  EXPECT_FALSE(policy3->IsFeatureEnabledForOrigin(
+      kParameterizedDecDoubleFeature, origin_a_, double_value));
 }
 
 TEST_F(FeaturePolicyTest, TestFeatureDelegatedAndAllowed) {
@@ -1263,6 +1444,10 @@
       policy2->IsFeatureEnabledForOrigin(kDefaultOffFeature, origin_a_));
   EXPECT_FALSE(
       policy2->IsFeatureEnabledForOrigin(kDefaultOffFeature, sandboxed_origin));
+  EXPECT_TRUE(policy2->IsFeatureEnabledForOrigin(kParameterizedDecDoubleFeature,
+                                                 origin_a_, double_value));
+  EXPECT_TRUE(policy2->IsFeatureEnabledForOrigin(
+      kParameterizedDecDoubleFeature, sandboxed_origin, double_value));
 }
 
 TEST_F(FeaturePolicyTest, TestSandboxedFramePolicyForAllOrigins) {
@@ -1481,4 +1666,209 @@
       PolicyContainsInheritedValue(policy2.get(), kUnavailableFeature));
 }
 
+TEST_F(FeaturePolicyTest, TestParameterizedFeatureWithDifferentThresholds) {
+  // +-------------------------------------------------------------+
+  // |(1) Origin A                                                 |
+  // |Feature-Policy: double-feature *(2.5) 'self'(3) OriginB(2)   |
+  // +-------------------------------------------------------------+
+  // +---------------------------+
+  // |(2) Origin A               |
+  // |Feature-Policy: 'self'(3)  |
+  // +---------------------------+
+  // Feature should have different threshold on different origin, if no
+  // particular value is specified, use fallback value.
+  std::unique_ptr<FeaturePolicy> policy1 =
+      CreateFromParentPolicy(nullptr, origin_a_);
+  std::unique_ptr<FeaturePolicy> policy2 =
+      CreateFromParentPolicy(nullptr, origin_a_);
+  PolicyValue large_double_value(3.0, mojom::PolicyValueType::kDecDouble);
+  PolicyValue small_double_value(2.2, mojom::PolicyValueType::kDecDouble);
+  policy1->SetHeaderPolicy(
+      {{{kParameterizedDecDoubleFeature,
+         std::map<url::Origin, PolicyValue>{{origin_a_, large_double_value},
+                                            {origin_b_, small_double_value}},
+         double_value, min_double_value}}});
+  policy2->SetHeaderPolicy(
+      {{{kParameterizedDecDoubleFeature,
+         std::map<url::Origin, PolicyValue>{{origin_a_, large_double_value}},
+         min_double_value, min_double_value}}});
+  EXPECT_TRUE(policy1->IsFeatureEnabled(kParameterizedDecDoubleFeature,
+                                        large_double_value));
+  EXPECT_TRUE(policy1->IsFeatureEnabledForOrigin(
+      kParameterizedDecDoubleFeature, origin_b_, small_double_value));
+  EXPECT_FALSE(policy1->IsFeatureEnabledForOrigin(
+      kParameterizedDecDoubleFeature, origin_b_, double_value));
+  EXPECT_TRUE(policy1->IsFeatureEnabledForOrigin(kParameterizedDecDoubleFeature,
+                                                 origin_c_, double_value));
+  EXPECT_FALSE(policy1->IsFeatureEnabledForOrigin(
+      kParameterizedDecDoubleFeature, origin_c_, large_double_value));
+  EXPECT_TRUE(policy2->IsFeatureEnabled(kParameterizedDecDoubleFeature,
+                                        large_double_value));
+  EXPECT_FALSE(policy2->IsFeatureEnabledForOrigin(
+      kParameterizedDecDoubleFeature, origin_b_, small_double_value));
+  EXPECT_TRUE(policy2->IsFeatureEnabledForOrigin(kParameterizedDecDoubleFeature,
+                                                 origin_b_, min_double_value));
+  EXPECT_FALSE(policy2->IsFeatureEnabledForOrigin(
+      kParameterizedDecDoubleFeature, origin_c_, small_double_value));
+  EXPECT_TRUE(policy2->IsFeatureEnabledForOrigin(kParameterizedDecDoubleFeature,
+                                                 origin_c_, min_double_value));
+}
+
+TEST_F(FeaturePolicyTest, TestParameterizedFeatureInheritence) {
+  // +-------------------------------------------------+
+  // |(1) Origin A                                    |
+  // | Feature-Policy: double-feature 'self'(2.5)     |
+  // | +-----------------+  +-----------------+       |
+  // | |(2) Origin A     |  |(4) Origin B     |       |
+  // | |No Policy        |  |No Policy        |       |
+  // | | +-------------+ |  | +-------------+ |       |
+  // | | |(3)Origin A  | |  | |(5)Origin B  | |       |
+  // | | |No Policy    | |  | |No Policy    | |       |
+  // | | +-------------+ |  | +-------------+ |       |
+  // | +-----------------+  +-----------------+       |
+  // +------------------------------------------------+
+  // Feature should be enabled at the top-level, and through the chain of
+  // same-origin frames 2 and 3. It should be disabled in frames 4 and 5, as
+  // they are at a different origin.
+  std::unique_ptr<FeaturePolicy> policy1 =
+      CreateFromParentPolicy(nullptr, origin_a_);
+  policy1->SetHeaderPolicy(
+      {{{kParameterizedDecDoubleFeature,
+         std::map<url::Origin, PolicyValue>{{origin_a_, double_value}},
+         min_double_value, min_double_value}}});
+  std::unique_ptr<FeaturePolicy> policy2 =
+      CreateFromParentPolicy(policy1.get(), origin_a_);
+  std::unique_ptr<FeaturePolicy> policy3 =
+      CreateFromParentPolicy(policy2.get(), origin_a_);
+  std::unique_ptr<FeaturePolicy> policy4 =
+      CreateFromParentPolicy(policy1.get(), origin_b_);
+  std::unique_ptr<FeaturePolicy> policy5 =
+      CreateFromParentPolicy(policy4.get(), origin_b_);
+  PolicyValue large_double_value(3.0, mojom::PolicyValueType::kDecDouble);
+  EXPECT_TRUE(
+      policy2->IsFeatureEnabled(kParameterizedDecDoubleFeature, double_value));
+  EXPECT_FALSE(policy2->IsFeatureEnabled(kParameterizedDecDoubleFeature,
+                                         large_double_value));
+  EXPECT_TRUE(
+      policy3->IsFeatureEnabled(kParameterizedDecDoubleFeature, double_value));
+  EXPECT_FALSE(policy3->IsFeatureEnabled(kParameterizedDecDoubleFeature,
+                                         large_double_value));
+  EXPECT_FALSE(
+      policy4->IsFeatureEnabled(kParameterizedDecDoubleFeature, double_value));
+  EXPECT_FALSE(policy4->IsFeatureEnabled(kParameterizedDecDoubleFeature,
+                                         large_double_value));
+  EXPECT_FALSE(
+      policy5->IsFeatureEnabled(kParameterizedDecDoubleFeature, double_value));
+  EXPECT_FALSE(policy5->IsFeatureEnabled(kParameterizedDecDoubleFeature,
+                                         large_double_value));
+  EXPECT_TRUE(policy5->IsFeatureEnabled(kParameterizedDecDoubleFeature,
+                                        min_double_value));
+}
+
+TEST_F(FeaturePolicyTest, TestParameterizedFeatureCombineThreshold) {
+  // +--------------------------------------+
+  // |(1) Origin A                          |
+  // |Feature-Policy: double-feature *(2.5) |
+  // | +----------------------------+       |
+  // | |(2) Origin A                |       |
+  // | | Feature-Policy:            |       |
+  // | |   double-feature 'self'(3) |       |
+  // | +----------------------------+       |
+  // | +----------------------------+       |
+  // | |(3)Origin A                 |       |
+  // | | Feature-Policy:            |       |
+  // | |   double-feature 'self'(2) |       |
+  // | +----------------------------+       |
+  // +--------------------------------------+
+  // Feature threshold should get stricter and stricter along inheritance.
+  std::unique_ptr<FeaturePolicy> policy1 =
+      CreateFromParentPolicy(nullptr, origin_a_);
+  policy1->SetHeaderPolicy(
+      {{{kParameterizedDecDoubleFeature,
+         std::map<url::Origin, PolicyValue>{{origin_a_, double_value}},
+         min_double_value, min_double_value}}});
+  PolicyValue large_double_value(3.0, mojom::PolicyValueType::kDecDouble);
+  std::unique_ptr<FeaturePolicy> policy2 =
+      CreateFromParentPolicy(policy1.get(), origin_a_);
+  policy2->SetHeaderPolicy(
+      {{{kParameterizedDecDoubleFeature,
+         std::map<url::Origin, PolicyValue>{{origin_a_, large_double_value}},
+         min_double_value, min_double_value}}});
+  PolicyValue small_double_value(2.2, mojom::PolicyValueType::kDecDouble);
+  std::unique_ptr<FeaturePolicy> policy3 =
+      CreateFromParentPolicy(policy1.get(), origin_a_);
+  policy3->SetHeaderPolicy(
+      {{{kParameterizedDecDoubleFeature,
+         std::map<url::Origin, PolicyValue>{{origin_a_, small_double_value}},
+         min_double_value, min_double_value}}});
+  EXPECT_TRUE(
+      policy2->IsFeatureEnabled(kParameterizedDecDoubleFeature, double_value));
+  EXPECT_FALSE(policy2->IsFeatureEnabled(kParameterizedDecDoubleFeature,
+                                         large_double_value));
+  EXPECT_TRUE(policy3->IsFeatureEnabled(kParameterizedDecDoubleFeature,
+                                        small_double_value));
+  EXPECT_FALSE(
+      policy3->IsFeatureEnabled(kParameterizedDecDoubleFeature, double_value));
+}
+
+TEST_F(FeaturePolicyTest, TestParameterizedSandboxPolicy) {
+  // +-----------------------------------------------------+
+  // |(1)Origin A                                          |
+  // |No Policy                                            |
+  // |                                                     |
+  // |<iframe sandbox>                                     |
+  // | +-------------+                                     |
+  // | |(2)Sandboxed |                                     |
+  // | |No Policy    |                                     |
+  // | +-------------+                                     |
+  // |<iframe sandbox allow="double-feature 'self'(2.5)">  |
+  // | +-----------------------------------------------+   |
+  // | |(3)Sandboxed                                   |   |
+  // | |                                               |   |
+  // | |<iframe sandbox allow="double-feature *(2.5)"> |   |
+  // | | +-------------+                               |   |
+  // | | |(4)Sandboxed |                               |   |
+  // | | |No Policy    |                               |   |
+  // | | +-------------+                               |   |
+  // | +-----------------------------------------------+   |
+  // +-----------------------------------------------------+
+  // |double-feature| (default-all) should be enabled in child frame with opaque
+  // origin and along inheritance chain.
+  std::unique_ptr<FeaturePolicy> policy1 =
+      CreateFromParentPolicy(nullptr, origin_a_);
+  url::Origin sandboxed_origin = url::Origin();
+  std::unique_ptr<FeaturePolicy> policy2 =
+      CreateFromParentPolicy(policy1.get(), sandboxed_origin);
+
+  ParsedFeaturePolicy frame_policy1 = {
+      {{kParameterizedDecDoubleFeature, std::map<url::Origin, PolicyValue>{},
+        min_double_value, double_value}}};
+  std::unique_ptr<FeaturePolicy> policy3 = CreateFromParentWithFramePolicy(
+      policy1.get(), frame_policy1, sandboxed_origin);
+  ParsedFeaturePolicy frame_policy2 = {
+      {{kParameterizedDecDoubleFeature, std::map<url::Origin, PolicyValue>{},
+        double_value, double_value}}};
+  std::unique_ptr<FeaturePolicy> policy4 = CreateFromParentWithFramePolicy(
+      policy3.get(), frame_policy2, sandboxed_origin);
+
+  EXPECT_TRUE(policy2->IsFeatureEnabledForOrigin(
+      kParameterizedDecDoubleFeature, sandboxed_origin, double_value));
+  EXPECT_TRUE(
+      policy2->IsFeatureEnabled(kParameterizedDecDoubleFeature, double_value));
+  EXPECT_TRUE(policy2->IsFeatureEnabledForOrigin(kParameterizedDecDoubleFeature,
+                                                 origin_a_, double_value));
+  EXPECT_TRUE(policy3->IsFeatureEnabledForOrigin(
+      kParameterizedDecDoubleFeature, sandboxed_origin, double_value));
+  EXPECT_TRUE(
+      policy3->IsFeatureEnabled(kParameterizedDecDoubleFeature, double_value));
+  EXPECT_TRUE(policy3->IsFeatureEnabledForOrigin(kParameterizedDecDoubleFeature,
+                                                 origin_a_, double_value));
+  EXPECT_TRUE(policy4->IsFeatureEnabledForOrigin(
+      kParameterizedDecDoubleFeature, sandboxed_origin, double_value));
+  EXPECT_TRUE(
+      policy4->IsFeatureEnabled(kParameterizedDecDoubleFeature, double_value));
+  EXPECT_TRUE(policy4->IsFeatureEnabledForOrigin(kParameterizedDecDoubleFeature,
+                                                 origin_a_, double_value));
+}
+
 }  // namespace blink
diff --git a/third_party/blink/common/feature_policy/policy_value.cc b/third_party/blink/common/feature_policy/policy_value.cc
index 92b346c5..f58edea6 100644
--- a/third_party/blink/common/feature_policy/policy_value.cc
+++ b/third_party/blink/common/feature_policy/policy_value.cc
@@ -17,6 +17,9 @@
 PolicyValue::PolicyValue(bool bool_value)
     : type_(mojom::PolicyValueType::kBool), bool_value_(bool_value) {}
 
+PolicyValue::PolicyValue(double double_value, mojom::PolicyValueType type)
+    : type_(type), double_value_(double_value) {}
+
 PolicyValue PolicyValue::CreateMaxPolicyValue(mojom::PolicyValueType type) {
   PolicyValue value;
   value.SetType(type);
@@ -36,32 +39,37 @@
   return bool_value_;
 }
 
+double PolicyValue::DoubleValue() const {
+  DCHECK_EQ(type_, mojom::PolicyValueType::kDecDouble);
+  return double_value_;
+}
+
 void PolicyValue::SetBoolValue(bool bool_value) {
-  type_ = mojom::PolicyValueType::kBool;
+  DCHECK_EQ(mojom::PolicyValueType::kBool, type_);
   bool_value_ = bool_value;
 }
 
+void PolicyValue::SetDoubleValue(double double_value,
+                                 mojom::PolicyValueType type) {
+  DCHECK_EQ(type, type_);
+  double_value_ = double_value;
+}
+
 PolicyValue& PolicyValue::operator=(const PolicyValue& rhs) {
   if (this != &rhs) {
     type_ = rhs.type_;
     bool_value_ = rhs.bool_value_;
+    double_value_ = rhs.double_value_;
   }
   return *this;
 }
 
+// static
 PolicyValue PolicyValue::Combine(const PolicyValue& lhs,
                                  const PolicyValue& rhs) {
-  DCHECK_EQ(lhs.Type(), rhs.Type());
-  PolicyValue final_value;
-  final_value.SetType(lhs.Type());
-  switch (lhs.Type()) {
-    case mojom::PolicyValueType::kBool:
-      final_value.SetBoolValue(lhs.BoolValue() && rhs.BoolValue());
-      break;
-    default:
-      NOTREACHED();
-  }
-  return final_value;
+  PolicyValue result = lhs;
+  result.Combine(rhs);
+  return result;
 }
 
 void PolicyValue::Combine(const PolicyValue& rhs) {
@@ -70,6 +78,9 @@
     case mojom::PolicyValueType::kBool:
       SetBoolValue(bool_value_ && rhs.BoolValue());
       break;
+    case mojom::PolicyValueType::kDecDouble:
+      SetDoubleValue(std::min(double_value_, rhs.DoubleValue()), type_);
+      break;
     default:
       NOTREACHED();
   }
@@ -81,6 +92,8 @@
   switch (lhs.Type()) {
     case mojom::PolicyValueType::kBool:
       return lhs.BoolValue() == rhs.BoolValue();
+    case mojom::PolicyValueType::kDecDouble:
+      return lhs.DoubleValue() == rhs.DoubleValue();
     case mojom::PolicyValueType::kNull:
       return true;
   }
@@ -97,6 +110,8 @@
   switch (lhs.Type()) {
     case mojom::PolicyValueType::kBool:
       return rhs.BoolValue();
+    case mojom::PolicyValueType::kDecDouble:
+      return lhs.DoubleValue() < rhs.DoubleValue();
     case mojom::PolicyValueType::kNull:
       NOTREACHED();
       break;
@@ -121,6 +136,9 @@
     case mojom::PolicyValueType::kBool:
       bool_value_ = true;
       break;
+    case mojom::PolicyValueType::kDecDouble:
+      double_value_ = std::numeric_limits<double>::infinity();
+      break;
     default:
       break;
   }
@@ -132,6 +150,9 @@
     case mojom::PolicyValueType::kBool:
       bool_value_ = false;
       break;
+    case mojom::PolicyValueType::kDecDouble:
+      double_value_ = 0.0;
+      break;
     default:
       NOTREACHED();
   }
diff --git a/third_party/blink/common/features.cc b/third_party/blink/common/features.cc
index f0ae48a..1de7942cc1 100644
--- a/third_party/blink/common/features.cc
+++ b/third_party/blink/common/features.cc
@@ -174,8 +174,8 @@
                                       base::FEATURE_DISABLED_BY_DEFAULT};
 
 // Writable files and native filesystem access. https://crbug.com/853326
-const base::Feature kWritableFilesAPI{"WritableFilesAPI",
-                                      base::FEATURE_DISABLED_BY_DEFAULT};
+const base::Feature kNativeFilesystemAPI{"NativeFilesystemAPI",
+                                         base::FEATURE_DISABLED_BY_DEFAULT};
 
 // Allows for synchronous XHR requests during page dismissal
 const base::Feature kForbidSyncXHRInPageDismissal{
diff --git a/third_party/blink/public/common/feature_policy/policy_value.h b/third_party/blink/public/common/feature_policy/policy_value.h
index a51972a..95f8e79 100644
--- a/third_party/blink/public/common/feature_policy/policy_value.h
+++ b/third_party/blink/public/common/feature_policy/policy_value.h
@@ -16,7 +16,7 @@
 // PolicyValue is a union of types (int / double / set<int> / bool ) that can be
 // used to specify the parameter of a policy.
 
-// TODO(loonybear): Add the following types: inc/dec int, inc/dec double, set.
+// TODO(loonybear): Add the following types: enum, inc/dec int, inc double, set.
 class BLINK_COMMON_EXPORT PolicyValue {
  public:
   PolicyValue();
@@ -25,6 +25,9 @@
   explicit PolicyValue(mojom::PolicyValueType);
 
   explicit PolicyValue(bool bool_value);
+  explicit PolicyValue(
+      double double_value,
+      mojom::PolicyValueType type = mojom::PolicyValueType::kDecDouble);
 
   // A 'max' PolicyValue is the most permissive value for the policy.
   static PolicyValue CreateMaxPolicyValue(mojom::PolicyValueType type);
@@ -37,9 +40,14 @@
   // PolicyValue getters.
   // Note the getters also DCHECKs that the type is correct.
   bool BoolValue() const;
+  double DoubleValue() const;
 
   // PolicyValue setters.
+  // Note the getters also DCHECKs that the type is correct.
   void SetBoolValue(bool bool_value);
+  void SetDoubleValue(
+      double double_value,
+      mojom::PolicyValueType type = mojom::PolicyValueType::kDecDouble);
 
   // Operater overrides
   PolicyValue& operator=(const PolicyValue& rhs);
@@ -54,6 +62,7 @@
  private:
   mojom::PolicyValueType type_;
   bool bool_value_;
+  double double_value_;
 };
 
 bool BLINK_COMMON_EXPORT operator==(const PolicyValue& lhs,
diff --git a/third_party/blink/public/common/features.h b/third_party/blink/public/common/features.h
index b7b827a..ef4b5d2 100644
--- a/third_party/blink/public/common/features.h
+++ b/third_party/blink/public/common/features.h
@@ -52,7 +52,7 @@
 BLINK_COMMON_EXPORT extern const base::Feature kStopNonTimersInBackground;
 BLINK_COMMON_EXPORT extern const base::Feature kTextFragmentAnchor;
 BLINK_COMMON_EXPORT extern const base::Feature kWasmCodeCache;
-BLINK_COMMON_EXPORT extern const base::Feature kWritableFilesAPI;
+BLINK_COMMON_EXPORT extern const base::Feature kNativeFilesystemAPI;
 BLINK_COMMON_EXPORT extern const base::Feature kForbidSyncXHRInPageDismissal;
 BLINK_COMMON_EXPORT extern const base::Feature
     kRestrictDeviceSensorEventsToSecureContexts;
diff --git a/third_party/blink/public/mojom/feature_policy/feature_policy.mojom b/third_party/blink/public/mojom/feature_policy/feature_policy.mojom
index 1364aa0f..f61f126 100644
--- a/third_party/blink/public/mojom/feature_policy/feature_policy.mojom
+++ b/third_party/blink/public/mojom/feature_policy/feature_policy.mojom
@@ -134,18 +134,20 @@
 };
 
 // This enum defines the types of parameters used to specify a feature policy.
-// TODO(loonybear): Add the following types: inc/dec int, inc/dec double, set.
+// TODO(loonybear): Add the following types: inc/dec int, inc double, enum, set.
 enum PolicyValueType {
   kNull,
   kBool,
+  kDecDouble,
 };
 
 // This union includes all the types that can be used to specify a policy's
 // parameter.
-// TODO(loonybear): Add the following types: inc/dec int, inc/dec double, set.
+// TODO(loonybear): Add the following types: inc/dec int, inc double, enum, set.
 union PolicyValueData {
   bool null_value;
   bool bool_value;
+  double dec_double_value;
 };
 
 // Defined as a structure so that it can be typemapped with StructTraits.
diff --git a/third_party/blink/public/mojom/frame/document_interface_broker.mojom b/third_party/blink/public/mojom/frame/document_interface_broker.mojom
index 5afe5cef..658925e 100644
--- a/third_party/blink/public/mojom/frame/document_interface_broker.mojom
+++ b/third_party/blink/public/mojom/frame/document_interface_broker.mojom
@@ -5,9 +5,11 @@
 module blink.mojom;
 
 import "third_party/blink/public/mojom/frame/frame_host_test_interface.mojom";
+import "third_party/blink/public/mojom/webaudio/audio_context_manager.mojom";
 
 // An interface through which the renderer may request document-scoped
 // interfaces from the browser.
 interface DocumentInterfaceBroker {
   GetFrameHostTestInterface(blink.mojom.FrameHostTestInterface& request);
+  GetAudioContextManager(blink.mojom.AudioContextManager& request);
 };
diff --git a/third_party/blink/public/mojom/service_worker/embedded_worker.mojom b/third_party/blink/public/mojom/service_worker/embedded_worker.mojom
index 0cdcf79..1974d759 100644
--- a/third_party/blink/public/mojom/service_worker/embedded_worker.mojom
+++ b/third_party/blink/public/mojom/service_worker/embedded_worker.mojom
@@ -171,7 +171,8 @@
                     int32 column_number, url.mojom.Url source_url);
 
   // Reports that a console message was emitted to the worker's console.
-  OnReportConsoleMessage(int32 source_identifier, int32 message_level,
+  OnReportConsoleMessage(int32 source_identifier,
+                         ConsoleMessageLevel message_level,
                          mojo_base.mojom.String16 message, int32 line_number,
                          url.mojom.Url source_url);
   // Indicates that the worker has stopped.
diff --git a/third_party/blink/public/mojom/web_feature/web_feature.mojom b/third_party/blink/public/mojom/web_feature/web_feature.mojom
index 3254769..f7d014b 100644
--- a/third_party/blink/public/mojom/web_feature/web_feature.mojom
+++ b/third_party/blink/public/mojom/web_feature/web_feature.mojom
@@ -2251,6 +2251,10 @@
   kCSSValueAppearanceProgressBarForOthersRendered = 2817,
   kCSSValueAppearancePushButton = 2818,
   kCSSValueAppearanceSquareButton = 2819,
+  kCSSValueAppearanceSearchCancel = 2820,
+  kCSSValueAppearanceTextarea = 2821,
+  kCSSValueAppearanceTextFieldForOthersRendered = 2822,
+  kCSSValueAppearanceTextFieldForTemporalRendered = 2823,
 
   // Add new features immediately above this line. Don't change assigned
   // numbers of any item, and don't reuse removed slots.
diff --git a/third_party/blink/public/platform/web_font_description.h b/third_party/blink/public/platform/web_font_description.h
index 753cf79..cd493dfe 100644
--- a/third_party/blink/public/platform/web_font_description.h
+++ b/third_party/blink/public/platform/web_font_description.h
@@ -87,8 +87,8 @@
   Weight weight;
   Smoothing smoothing;
 
-  short letter_spacing;
-  short word_spacing;
+  int16_t letter_spacing;
+  int16_t word_spacing;
 
 #if INSIDE_BLINK
   BLINK_PLATFORM_EXPORT WebFontDescription(const FontDescription&);
diff --git a/third_party/blink/public/platform/web_layer_tree_view.h b/third_party/blink/public/platform/web_layer_tree_view.h
index ae8f20a0..931b0b52 100644
--- a/third_party/blink/public/platform/web_layer_tree_view.h
+++ b/third_party/blink/public/platform/web_layer_tree_view.h
@@ -135,9 +135,13 @@
     return nullptr;
   }
 
-  // Start and Stop defering commits to the compositor. Defering commits
-  // allows document lifecycle updates but does not commit the layer tree.
-  virtual void StartDeferringCommits() {}
+  // Start defering commits to the compositor, allowing document lifecycle
+  // updates without committing the layer tree. Commits are deferred
+  // until at most the given |timeout| has passed. If multiple calls are made
+  // when deferal is active then the initial timeout applies.
+  virtual void StartDeferringCommits(base::TimeDelta timeout) {}
+
+  // Immediately stop deferring commits.
   virtual void StopDeferringCommits() {}
 
   // Mutations are plumbed back to the layer tree via the mutator client.
diff --git a/third_party/blink/public/platform/web_runtime_features.h b/third_party/blink/public/platform/web_runtime_features.h
index 6a78e96..d51c3c5 100644
--- a/third_party/blink/public/platform/web_runtime_features.h
+++ b/third_party/blink/public/platform/web_runtime_features.h
@@ -111,7 +111,9 @@
   BLINK_PLATFORM_EXPORT static void EnableImplicitRootScroller(bool);
   BLINK_PLATFORM_EXPORT static void EnableInputMultipleFieldsUI(bool);
   BLINK_PLATFORM_EXPORT static void EnableJankTrackingSweepLine(bool);
-  BLINK_PLATFORM_EXPORT static void EnableLayeredAPI(bool);
+  BLINK_PLATFORM_EXPORT static void EnableBuiltInModuleAll(bool);
+  BLINK_PLATFORM_EXPORT static void EnableBuiltInModuleInfra(bool);
+  BLINK_PLATFORM_EXPORT static void EnableBuiltInModuleKvStorage(bool);
   BLINK_PLATFORM_EXPORT static void EnableLayoutNG(bool);
   BLINK_PLATFORM_EXPORT static void EnableLazyFrameLoading(bool);
   BLINK_PLATFORM_EXPORT static void EnableLazyFrameVisibleLoadTimeMetrics(bool);
@@ -217,7 +219,6 @@
   BLINK_PLATFORM_EXPORT static void EnableMediaEngagementBypassAutoplayPolicies(
       bool);
   BLINK_PLATFORM_EXPORT static void EnableAutomationControlled(bool);
-  BLINK_PLATFORM_EXPORT static void EnableScheduledScriptStreaming(bool);
   BLINK_PLATFORM_EXPORT static void EnableScriptStreamingOnPreload(bool);
   BLINK_PLATFORM_EXPORT static void EnableExperimentalProductivityFeatures(
       bool);
diff --git a/third_party/blink/public/platform/web_thread_type.h b/third_party/blink/public/platform/web_thread_type.h
index 7f0cf64..38ace6b 100644
--- a/third_party/blink/public/platform/web_thread_type.h
+++ b/third_party/blink/public/platform/web_thread_type.h
@@ -21,7 +21,7 @@
   kFileThread = 8,
   kDatabaseThread = 9,
   kWebAudioThread = 10,
-  kScriptStreamerThread = 11,
+  // 11 was kScriptStreamerThread, which was deleted
   kOfflineAudioRenderThread = 12,
   kReverbConvolutionBackgroundThread = 13,
   kHRTFDatabaseLoaderThread = 14,
diff --git a/third_party/blink/public/platform/web_url_response.h b/third_party/blink/public/platform/web_url_response.h
index af6fbef..cd6279a 100644
--- a/third_party/blink/public/platform/web_url_response.h
+++ b/third_party/blink/public/platform/web_url_response.h
@@ -189,7 +189,7 @@
   BLINK_PLATFORM_EXPORT void SetRequestId(int);
 
   BLINK_PLATFORM_EXPORT int HttpStatusCode() const;
-  BLINK_PLATFORM_EXPORT void SetHTTPStatusCode(int);
+  BLINK_PLATFORM_EXPORT void SetHttpStatusCode(int);
 
   BLINK_PLATFORM_EXPORT WebString HttpStatusText() const;
   BLINK_PLATFORM_EXPORT void SetHTTPStatusText(const WebString&);
diff --git a/third_party/blink/public/web/modules/service_worker/web_service_worker_context_client.h b/third_party/blink/public/web/modules/service_worker/web_service_worker_context_client.h
index 0709f2f..5e4e337 100644
--- a/third_party/blink/public/web/modules/service_worker/web_service_worker_context_client.h
+++ b/third_party/blink/public/web/modules/service_worker/web_service_worker_context_client.h
@@ -35,6 +35,7 @@
 
 #include "base/memory/scoped_refptr.h"
 #include "base/time/time.h"
+#include "third_party/blink/public/mojom/devtools/console_message.mojom-shared.h"
 #include "third_party/blink/public/mojom/service_worker/service_worker_event_status.mojom-shared.h"
 #include "third_party/blink/public/mojom/web_feature/web_feature.mojom-shared.h"
 #include "third_party/blink/public/platform/modules/service_worker/web_service_worker_stream_handle.h"
@@ -163,7 +164,7 @@
 
   // Called when a console message was written.
   virtual void ReportConsoleMessage(int source,
-                                    int level,
+                                    blink::mojom::ConsoleMessageLevel level,
                                     const WebString& message,
                                     int line_number,
                                     const WebString& source_url) {}
diff --git a/third_party/blink/public/web/web_view_client.h b/third_party/blink/public/web/web_view_client.h
index bfbc272..e8b86d6 100644
--- a/third_party/blink/public/web/web_view_client.h
+++ b/third_party/blink/public/web/web_view_client.h
@@ -203,6 +203,10 @@
   // Gestures -------------------------------------------------------------
 
   virtual bool CanHandleGestureEvent() { return false; }
+
+  // Policies -------------------------------------------------------------
+
+  virtual bool AllowPopupsDuringPageUnload() { return false; }
 };
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/bindings/bindings.gni b/third_party/blink/renderer/bindings/bindings.gni
index 7dff426..ee2e9a5 100644
--- a/third_party/blink/renderer/bindings/bindings.gni
+++ b/third_party/blink/renderer/bindings/bindings.gni
@@ -100,8 +100,6 @@
                     "core/v8/script_source_location_type.h",
                     "core/v8/script_streamer.cc",
                     "core/v8/script_streamer.h",
-                    "core/v8/script_streamer_thread.cc",
-                    "core/v8/script_streamer_thread.h",
                     "core/v8/script_value.cc",
                     "core/v8/script_value.h",
                     "core/v8/serialization/post_message_helper.cc",
diff --git a/third_party/blink/renderer/bindings/core/v8/custom/v8_readable_stream_custom.cc b/third_party/blink/renderer/bindings/core/v8/custom/v8_readable_stream_custom.cc
index 492091a5..58974b4 100644
--- a/third_party/blink/renderer/bindings/core/v8/custom/v8_readable_stream_custom.cc
+++ b/third_party/blink/renderer/bindings/core/v8/custom/v8_readable_stream_custom.cc
@@ -5,7 +5,7 @@
 #include "third_party/blink/renderer/bindings/core/v8/v8_readable_stream.h"
 
 #include "third_party/blink/renderer/bindings/core/v8/script_value.h"
-#include "third_party/blink/renderer/core/streams/readable_stream.h"
+#include "third_party/blink/renderer/core/streams/readable_stream_wrapper.h"
 #include "third_party/blink/renderer/platform/bindings/exception_state.h"
 #include "third_party/blink/renderer/platform/bindings/script_state.h"
 #include "third_party/blink/renderer/platform/bindings/v8_binding.h"
@@ -30,7 +30,9 @@
   ScriptValue strategy = ScriptValue(ScriptState::Current(info.GetIsolate()),
                                      v8::Undefined(info.GetIsolate()));
   int num_args = info.Length();
-  auto* impl = MakeGarbageCollected<ReadableStream>();
+  // TODO(ricea): Switch implementation based on StreamsNative feature flag
+  // here.
+  auto* impl = MakeGarbageCollected<ReadableStreamWrapper>();
   v8::Local<v8::Object> wrapper = info.Holder();
   wrapper = impl->AssociateWithWrapper(
       info.GetIsolate(), V8ReadableStream::GetWrapperTypeInfo(), wrapper);
diff --git a/third_party/blink/renderer/bindings/core/v8/dictionary_helper_for_core.cc b/third_party/blink/renderer/bindings/core/v8/dictionary_helper_for_core.cc
index 56f444e9..37fa366 100644
--- a/third_party/blink/renderer/bindings/core/v8/dictionary_helper_for_core.cc
+++ b/third_party/blink/renderer/bindings/core/v8/dictionary_helper_for_core.cc
@@ -130,8 +130,8 @@
 template <>
 bool DictionaryHelper::Get(const Dictionary& dictionary,
                            const StringView& key,
-                           short& value) {
-  return GetNumericType<short>(dictionary, key, value);
+                           int16_t& value) {
+  return GetNumericType<int16_t>(dictionary, key, value);
 }
 
 template <>
diff --git a/third_party/blink/renderer/bindings/core/v8/script_streamer.cc b/third_party/blink/renderer/bindings/core/v8/script_streamer.cc
index e58b16d..6836cf1 100644
--- a/third_party/blink/renderer/bindings/core/v8/script_streamer.cc
+++ b/third_party/blink/renderer/bindings/core/v8/script_streamer.cc
@@ -8,7 +8,6 @@
 
 #include "base/macros.h"
 #include "base/memory/ptr_util.h"
-#include "third_party/blink/renderer/bindings/core/v8/script_streamer_thread.h"
 #include "third_party/blink/renderer/bindings/core/v8/v8_code_cache.h"
 #include "third_party/blink/renderer/core/dom/document.h"
 #include "third_party/blink/renderer/core/dom/element.h"
@@ -352,21 +351,6 @@
   streamer->StreamingCompleteOnBackgroundThread();
 }
 
-void RunBlockingScriptStreamingTask(
-    std::unique_ptr<v8::ScriptCompiler::ScriptStreamingTask> task,
-    ScriptStreamer* streamer,
-    std::atomic_flag* blocking_task_started_or_cancelled) {
-  if (blocking_task_started_or_cancelled->test_and_set())
-    return;
-  RunScriptStreamingTask(std::move(task), streamer);
-}
-
-void RunNonBlockingScriptStreamingTask(
-    std::unique_ptr<v8::ScriptCompiler::ScriptStreamingTask> task,
-    ScriptStreamer* streamer) {
-  RunScriptStreamingTask(std::move(task), streamer);
-}
-
 }  // namespace
 
 bool ScriptStreamer::HasEnoughDataForStreaming(size_t resource_buffer_size) {
@@ -415,16 +399,6 @@
       }
     }
 
-    if (!RuntimeEnabledFeatures::ScheduledScriptStreamingEnabled() &&
-        ScriptStreamerThread::Shared()->IsRunningTask()) {
-      // If scheduled script streaming is disabled, we only have one thread for
-      // running the tasks. A new task shouldn't be queued before the running
-      // task completes, because the running task can block and wait for data
-      // from the network.
-      SuppressStreaming(kThreadBusy);
-      return;
-    }
-
     DCHECK(!stream_);
     DCHECK(!source_);
     stream_ = new SourceStream;
@@ -451,31 +425,16 @@
         inspector_parse_script_event::Data(this->ScriptResourceIdentifier(),
                                            this->ScriptURLString()));
 
-    if (RuntimeEnabledFeatures::ScheduledScriptStreamingEnabled()) {
-      // Script streaming tasks are high priority, as they can block the parser,
-      // and they can (and probably will) block during their own execution as
-      // they wait for more input.
-      //
-      // Pass through the atomic cancellation token which is set to true by the
-      // task when it is started, or set to true by the streamer if it wants to
-      // cancel the task.
-      //
-      // TODO(leszeks): Decrease the priority of these tasks where possible.
-      worker_pool::PostTaskWithTraits(
-          FROM_HERE, {base::TaskPriority::USER_BLOCKING, base::MayBlock()},
-          CrossThreadBind(RunBlockingScriptStreamingTask,
-                          WTF::Passed(std::move(script_streaming_task)),
-                          WrapCrossThreadPersistent(this),
-                          WTF::CrossThreadUnretained(
-                              &blocking_task_started_or_cancelled_)));
-    } else {
-      blocking_task_started_or_cancelled_.test_and_set();
-      ScriptStreamerThread::Shared()->PostTask(
-          CrossThreadBind(&ScriptStreamerThread::RunScriptStreamingTask,
-                          WTF::Passed(std::move(script_streaming_task)),
-                          WrapCrossThreadPersistent(this)));
-    }
-
+    // Script streaming tasks are high priority, as they can block the parser,
+    // and they can (and probably will) block during their own execution as
+    // they wait for more input.
+    //
+    // TODO(leszeks): Decrease the priority of these tasks where possible.
+    worker_pool::PostTaskWithTraits(
+        FROM_HERE, {base::TaskPriority::USER_BLOCKING, base::MayBlock()},
+        CrossThreadBind(RunScriptStreamingTask,
+                        WTF::Passed(std::move(script_streaming_task)),
+                        WrapCrossThreadPersistent(this)));
   }
   if (stream_)
     stream_->DidReceiveData(script_resource_, this);
@@ -492,34 +451,7 @@
   }
 
   if (stream_) {
-    // Mark the stream as finished loading before potentially re-posting the
-    // task to avoid a race between this finish and the task's first read.
     stream_->DidFinishLoading();
-
-    // If the corresponding blocking task hasn't started yet, cancel it and post
-    // a non-blocking task, since we know now that all the data is received and
-    // we will no longer block.
-    //
-    // TODO(874080): Remove this once blocking and non-blocking pools are
-    // merged.
-    if (RuntimeEnabledFeatures::ScheduledScriptStreamingEnabled() &&
-        !RuntimeEnabledFeatures::MergeBlockingNonBlockingPoolsEnabled() &&
-        !blocking_task_started_or_cancelled_.test_and_set()) {
-      std::unique_ptr<v8::ScriptCompiler::ScriptStreamingTask>
-          script_streaming_task(
-              base::WrapUnique(v8::ScriptCompiler::StartStreamingScript(
-                  V8PerIsolateData::MainThreadIsolate(), source_.get(),
-                  compile_options_)));
-
-      // The task creation shouldn't fail, since it didn't fail before during
-      // NotifyAppendData.
-      CHECK(script_streaming_task);
-      worker_pool::PostTaskWithTraits(
-          FROM_HERE, {base::TaskPriority::USER_BLOCKING},
-          CrossThreadBind(RunNonBlockingScriptStreamingTask,
-                          WTF::Passed(std::move(script_streaming_task)),
-                          WrapCrossThreadPersistent(this)));
-    }
   }
   loading_finished_ = true;
 
diff --git a/third_party/blink/renderer/bindings/core/v8/script_streamer.h b/third_party/blink/renderer/bindings/core/v8/script_streamer.h
index 32f97c0..a162366 100644
--- a/third_party/blink/renderer/bindings/core/v8/script_streamer.h
+++ b/third_party/blink/renderer/bindings/core/v8/script_streamer.h
@@ -5,7 +5,6 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_BINDINGS_CORE_V8_SCRIPT_STREAMER_H_
 #define THIRD_PARTY_BLINK_RENDERER_BINDINGS_CORE_V8_SCRIPT_STREAMER_H_
 
-#include <atomic>
 #include <memory>
 
 #include "base/macros.h"
@@ -41,8 +40,7 @@
     kRevalidate,
     kContextNotValid,  // DEPRECATED
     kEncodingNotSupported,
-    // TODO(leszeks): Deprecate once scheduled streaming is on by default
-    kThreadBusy,
+    kThreadBusy,  // DEPRECATED
     kV8CannotStream,
     kScriptTooSmall,
     kNoResourceBuffer,
@@ -155,11 +153,6 @@
   // Whether we have received enough data to start the streaming.
   bool have_enough_data_for_streaming_;
 
-  // Flag used to allow atomic cancelling and reposting of the streaming task
-  // when the load completes without the task yet starting.
-  // TODO(874080): Remove this once blocking and non-blocking pools are merged.
-  std::atomic_flag blocking_task_started_or_cancelled_ = ATOMIC_FLAG_INIT;
-
   // Whether the script source code should be retrieved from the Resource
   // instead of the ScriptStreamer.
   bool streaming_suppressed_;
diff --git a/third_party/blink/renderer/bindings/core/v8/script_streamer_test.cc b/third_party/blink/renderer/bindings/core/v8/script_streamer_test.cc
index 0516132..4b9ec64 100644
--- a/third_party/blink/renderer/bindings/core/v8/script_streamer_test.cc
+++ b/third_party/blink/renderer/bindings/core/v8/script_streamer_test.cc
@@ -13,7 +13,6 @@
 #include "third_party/blink/public/platform/web_url_loader_mock_factory.h"
 #include "third_party/blink/renderer/bindings/core/v8/referrer_script_info.h"
 #include "third_party/blink/renderer/bindings/core/v8/script_source_code.h"
-#include "third_party/blink/renderer/bindings/core/v8/script_streamer_thread.h"
 #include "third_party/blink/renderer/bindings/core/v8/v8_binding_for_core.h"
 #include "third_party/blink/renderer/bindings/core/v8/v8_binding_for_testing.h"
 #include "third_party/blink/renderer/bindings/core/v8/v8_code_cache.h"
@@ -64,7 +63,7 @@
     Platform::Current()->GetURLLoaderMockFactory()->UnregisterURL(url);
 
     ResourceResponse response(url);
-    response.SetHTTPStatusCode(200);
+    response.SetHttpStatusCode(200);
     GetResource()->SetResponse(response);
   }
 
@@ -123,13 +122,6 @@
   }
 
   void ProcessTasksUntilStreamingComplete() {
-    if (!RuntimeEnabledFeatures::ScheduledScriptStreamingEnabled()) {
-      while (ScriptStreamerThread::Shared()->IsRunningTask()) {
-        test::RunPendingTasks();
-      }
-    }
-    // Once more, because the "streaming complete" notification might only
-    // now be in the task queue.
     test::RunPendingTasks();
   }
 
diff --git a/third_party/blink/renderer/bindings/core/v8/script_streamer_thread.cc b/third_party/blink/renderer/bindings/core/v8/script_streamer_thread.cc
deleted file mode 100644
index e1b3c34..0000000
--- a/third_party/blink/renderer/bindings/core/v8/script_streamer_thread.cc
+++ /dev/null
@@ -1,90 +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 "third_party/blink/renderer/bindings/core/v8/script_streamer_thread.h"
-
-#include <memory>
-#include "base/location.h"
-#include "third_party/blink/public/platform/platform.h"
-#include "third_party/blink/renderer/bindings/core/v8/script_streamer.h"
-#include "third_party/blink/renderer/core/inspector/inspector_trace_events.h"
-#include "third_party/blink/renderer/platform/instrumentation/tracing/trace_event.h"
-#include "third_party/blink/renderer/platform/runtime_enabled_features.h"
-#include "third_party/blink/renderer/platform/scheduler/public/post_cross_thread_task.h"
-#include "third_party/blink/renderer/platform/scheduler/public/thread.h"
-
-namespace blink {
-
-static ScriptStreamerThread* g_shared_thread = nullptr;
-// Guards s_sharedThread. s_sharedThread is initialized and deleted in the main
-// thread, but also used by the streamer thread. Races can occur during
-// shutdown.
-static Mutex* g_mutex = nullptr;
-
-void ScriptStreamerThread::Init() {
-  // Only enabled when ScheduledScriptStreaming is disabled.
-  if (!RuntimeEnabledFeatures::ScheduledScriptStreamingEnabled()) {
-    DCHECK(!g_shared_thread);
-    DCHECK(IsMainThread());
-    // This is called in the main thread before any tasks are created, so no
-    // locking is needed.
-    g_mutex = new Mutex();
-    g_shared_thread = new ScriptStreamerThread();
-  }
-}
-
-ScriptStreamerThread* ScriptStreamerThread::Shared() {
-  DCHECK(!RuntimeEnabledFeatures::ScheduledScriptStreamingEnabled());
-  return g_shared_thread;
-}
-
-void ScriptStreamerThread::PostTask(CrossThreadClosure task) {
-  DCHECK(!RuntimeEnabledFeatures::ScheduledScriptStreamingEnabled());
-  DCHECK(IsMainThread());
-  MutexLocker locker(mutex_);
-  DCHECK(!running_task_);
-  running_task_ = true;
-  PostCrossThreadTask(*PlatformThread().GetTaskRunner(), FROM_HERE,
-                      std::move(task));
-}
-
-void ScriptStreamerThread::TaskDone() {
-  MutexLocker locker(mutex_);
-  DCHECK(running_task_);
-  running_task_ = false;
-}
-
-Thread& ScriptStreamerThread::PlatformThread() {
-  if (!IsRunning()) {
-    thread_ = Platform::Current()->CreateThread(
-        ThreadCreationParams(WebThreadType::kScriptStreamerThread));
-  }
-  return *thread_;
-}
-
-void ScriptStreamerThread::RunScriptStreamingTask(
-    std::unique_ptr<v8::ScriptCompiler::ScriptStreamingTask> task,
-    ScriptStreamer* streamer) {
-  DCHECK(!RuntimeEnabledFeatures::ScheduledScriptStreamingEnabled());
-  TRACE_EVENT1(
-      "v8,devtools.timeline", "v8.parseOnBackground", "data",
-      inspector_parse_script_event::Data(streamer->ScriptResourceIdentifier(),
-                                         streamer->ScriptURLString()));
-  TRACE_EVENT_WITH_FLOW1(
-      TRACE_DISABLED_BY_DEFAULT("v8.compile"), "v8.streamingCompile.run",
-      streamer, TRACE_EVENT_FLAG_FLOW_IN | TRACE_EVENT_FLAG_FLOW_OUT, "data",
-      inspector_parse_script_event::Data(streamer->ScriptResourceIdentifier(),
-                                         streamer->ScriptURLString()));
-  // Running the task can and will block: SourceStream::GetSomeData will get
-  // called and it will block and wait for data from the network.
-  task->Run();
-  streamer->StreamingCompleteOnBackgroundThread();
-  MutexLocker locker(*g_mutex);
-  ScriptStreamerThread* thread = Shared();
-  if (thread)
-    thread->TaskDone();
-  // If thread is 0, we're shutting down.
-}
-
-}  // namespace blink
diff --git a/third_party/blink/renderer/bindings/core/v8/script_streamer_thread.h b/third_party/blink/renderer/bindings/core/v8/script_streamer_thread.h
deleted file mode 100644
index 664df34e..0000000
--- a/third_party/blink/renderer/bindings/core/v8/script_streamer_thread.h
+++ /dev/null
@@ -1,60 +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 THIRD_PARTY_BLINK_RENDERER_BINDINGS_CORE_V8_SCRIPT_STREAMER_THREAD_H_
-#define THIRD_PARTY_BLINK_RENDERER_BINDINGS_CORE_V8_SCRIPT_STREAMER_THREAD_H_
-
-#include <memory>
-
-#include "base/macros.h"
-#include "third_party/blink/renderer/core/core_export.h"
-#include "third_party/blink/renderer/platform/scheduler/public/thread.h"
-#include "third_party/blink/renderer/platform/wtf/functional.h"
-#include "third_party/blink/renderer/platform/wtf/threading_primitives.h"
-#include "v8/include/v8.h"
-
-namespace blink {
-
-class ScriptStreamer;
-
-// A singleton thread for running background tasks for script streaming.
-class CORE_EXPORT ScriptStreamerThread {
-  USING_FAST_MALLOC(ScriptStreamerThread);
-
- public:
-  static void Init();
-  static ScriptStreamerThread* Shared();
-
-  void PostTask(CrossThreadClosure);
-
-  bool IsRunningTask() const {
-    MutexLocker locker(mutex_);
-    return running_task_;
-  }
-
-  void TaskDone();
-
-  static void RunScriptStreamingTask(
-      std::unique_ptr<v8::ScriptCompiler::ScriptStreamingTask>,
-      ScriptStreamer*);
-
- private:
-  ScriptStreamerThread() : running_task_(false) {}
-
-  bool IsRunning() const { return !!thread_; }
-
-  Thread& PlatformThread();
-
-  // At the moment, we only use one thread, so we can only stream one script
-  // at a time. FIXME: Use a thread pool and stream multiple scripts.
-  std::unique_ptr<Thread> thread_;
-  bool running_task_ GUARDED_BY(mutex_);
-  mutable Mutex mutex_;
-
-  DISALLOW_COPY_AND_ASSIGN(ScriptStreamerThread);
-};
-
-}  // namespace blink
-
-#endif  // THIRD_PARTY_BLINK_RENDERER_BINDINGS_CORE_V8_SCRIPT_STREAMER_THREAD_H_
diff --git a/third_party/blink/renderer/bindings/core/v8/v8_intersection_observer_delegate.h b/third_party/blink/renderer/bindings/core/v8/v8_intersection_observer_delegate.h
index 52460fb8..b9028887 100644
--- a/third_party/blink/renderer/bindings/core/v8/v8_intersection_observer_delegate.h
+++ b/third_party/blink/renderer/bindings/core/v8/v8_intersection_observer_delegate.h
@@ -31,6 +31,10 @@
 
   void Trace(blink::Visitor*) override;
 
+  IntersectionObserver::DeliveryBehavior GetDeliveryBehavior() const override {
+    return IntersectionObserver::kPostTaskToDeliver;
+  }
+
   void Deliver(const HeapVector<Member<IntersectionObserverEntry>>&,
                IntersectionObserver&) override;
 
diff --git a/third_party/blink/renderer/bindings/core/v8/v8_script_runner_test.cc b/third_party/blink/renderer/bindings/core/v8/v8_script_runner_test.cc
index 9e1c5b39..3d09d3c 100644
--- a/third_party/blink/renderer/bindings/core/v8/v8_script_runner_test.cc
+++ b/third_party/blink/renderer/bindings/core/v8/v8_script_runner_test.cc
@@ -102,7 +102,7 @@
     resource->SetClientIsWaitingForFinished();
     String code = Code();
     ResourceResponse response(Url());
-    response.SetHTTPStatusCode(200);
+    response.SetHttpStatusCode(200);
     resource->SetResponse(response);
     resource->AppendData(code.Utf8().data(), code.Utf8().length());
     resource->FinishForTest();
diff --git a/third_party/blink/renderer/build/scripts/make_instrumenting_probes.py b/third_party/blink/renderer/build/scripts/make_instrumenting_probes.py
index 514a0dc..da92ee24 100644
--- a/third_party/blink/renderer/build/scripts/make_instrumenting_probes.py
+++ b/third_party/blink/renderer/build/scripts/make_instrumenting_probes.py
@@ -165,7 +165,7 @@
 
 
 def build_param_name(param_type):
-    return "param" + re.match(r"(const |scoped_refptr<)?(\w*)", param_type).group(2)
+    return "param_" + NameStyleConverter(re.match(r"(const |scoped_refptr<)?(\w*)", param_type).group(2)).to_snake_case()
 
 
 def load_config(file_name):
diff --git a/third_party/blink/renderer/core/BUILD.gn b/third_party/blink/renderer/core/BUILD.gn
index 920e8ec..6ae258fa 100644
--- a/third_party/blink/renderer/core/BUILD.gn
+++ b/third_party/blink/renderer/core/BUILD.gn
@@ -1818,7 +1818,6 @@
     "dom/dom_implementation_test.cc",
     "dom/dom_node_ids_test.cc",
     "dom/element_test.cc",
-    "dom/element_visibility_observer_test.cc",
     "dom/events/event_path_test.cc",
     "dom/events/event_target_test.cc",
     "dom/events/listener_leak_test.cc",
@@ -1944,6 +1943,7 @@
     "html/forms/html_output_element_test.cc",
     "html/forms/html_select_element_test.cc",
     "html/forms/html_text_area_element_test.cc",
+    "html/forms/internal_popup_menu_test.cc",
     "html/forms/option_list_test.cc",
     "html/forms/password_input_type_test.cc",
     "html/forms/step_range_test.cc",
diff --git a/third_party/blink/renderer/core/content_capture/BUILD.gn b/third_party/blink/renderer/core/content_capture/BUILD.gn
index 3d3b6b9..1282f76 100644
--- a/third_party/blink/renderer/core/content_capture/BUILD.gn
+++ b/third_party/blink/renderer/core/content_capture/BUILD.gn
@@ -10,6 +10,8 @@
     "content_capture_manager.h",
     "content_capture_task.cc",
     "content_capture_task.h",
+    "content_capture_task_histogram_reporter.cc",
+    "content_capture_task_histogram_reporter.h",
     "content_holder.cc",
     "content_holder.h",
     "sent_nodes.cc",
diff --git a/third_party/blink/renderer/core/content_capture/content_capture_task.cc b/third_party/blink/renderer/core/content_capture/content_capture_task.cc
index d0fca45..bf2637d 100644
--- a/third_party/blink/renderer/core/content_capture/content_capture_task.cc
+++ b/third_party/blink/renderer/core/content_capture/content_capture_task.cc
@@ -25,6 +25,16 @@
   local_frame_root.Client()
       ->GetWebContentCaptureClient()
       ->GetTaskTimingParameters(task_short_delay_, task_long_delay_);
+  // The histogram is all about time, just disable it if high resolution isn't
+  // supported.
+  if (TimeTicks::IsHighResolution()) {
+    histogram_reporter_ =
+        base::MakeRefCounted<ContentCaptureTaskHistogramReporter>();
+    task_session_->SetSentNodeCountCallback(
+        WTF::BindRepeating(&ContentCaptureTaskHistogramReporter::
+                               RecordsSentContentCountPerDocument,
+                           histogram_reporter_));
+  }
 }
 
 ContentCaptureTask::~ContentCaptureTask() {}
@@ -53,7 +63,11 @@
 bool ContentCaptureTask::CaptureContent() {
   DCHECK(task_session_);
   std::vector<cc::NodeHolder> buffer;
+  if (histogram_reporter_)
+    histogram_reporter_->OnCaptureContentStarted();
   bool result = CaptureContent(buffer);
+  if (histogram_reporter_)
+    histogram_reporter_->OnCaptureContentEnded(buffer.size());
   if (!buffer.empty())
     task_session_->SetCapturedContent(buffer);
   return result;
@@ -63,6 +77,8 @@
     TaskSession::DocumentSession& doc_session) {
   auto* document = doc_session.GetDocument();
   DCHECK(document);
+  if (histogram_reporter_)
+    histogram_reporter_->OnSendContentStarted();
   std::vector<scoped_refptr<WebContentHolder>> content_batch;
   content_batch.reserve(kBatchSize);
   while (content_batch.size() < kBatchSize) {
@@ -79,6 +95,8 @@
         content_batch, !doc_session.FirstDataHasSent());
     doc_session.SetFirstDataHasSent();
   }
+  if (histogram_reporter_)
+    histogram_reporter_->OnSendContentEnded(content_batch.size());
 }
 
 WebContentCaptureClient* ContentCaptureTask::GetWebContentCaptureClient(
@@ -200,6 +218,8 @@
 void ContentCaptureTask::Schedule(ScheduleReason reason) {
   DCHECK(local_frame_root_);
   has_content_change_ = true;
+  if (histogram_reporter_)
+    histogram_reporter_->OnContentChanged();
   ScheduleInternal(reason);
 }
 
@@ -210,4 +230,8 @@
   return ThreadScheduler::Current()->ShouldYieldForHighPriorityWork();
 }
 
+void ContentCaptureTask::ClearDocumentSessionsForTesting() {
+  task_session_->ClearDocumentSessionsForTesting();
+}
+
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/content_capture/content_capture_task.h b/third_party/blink/renderer/core/content_capture/content_capture_task.h
index bb30774..01941c8 100644
--- a/third_party/blink/renderer/core/content_capture/content_capture_task.h
+++ b/third_party/blink/renderer/core/content_capture/content_capture_task.h
@@ -9,6 +9,7 @@
 #include <vector>
 
 #include "cc/paint/node_holder.h"
+#include "third_party/blink/renderer/core/content_capture/content_capture_task_histogram_reporter.h"
 #include "third_party/blink/renderer/core/content_capture/task_session.h"
 #include "third_party/blink/renderer/core/core_export.h"
 #include "third_party/blink/renderer/platform/timer.h"
@@ -62,6 +63,8 @@
     captured_content_for_testing_ = captured_content;
   }
 
+  void ClearDocumentSessionsForTesting();
+
  protected:
   // All protected data and methods are for testing purpose.
   // Return true if the task should pause.
@@ -107,6 +110,7 @@
   // kRetryTask, with long delay for kContentChange.
   base::TimeDelta task_short_delay_;
   base::TimeDelta task_long_delay_;
+  scoped_refptr<ContentCaptureTaskHistogramReporter> histogram_reporter_;
   base::Optional<TaskState> task_stop_for_testing_;
   base::Optional<std::vector<cc::NodeHolder>> captured_content_for_testing_;
 };
diff --git a/third_party/blink/renderer/core/content_capture/content_capture_task_histogram_reporter.cc b/third_party/blink/renderer/core/content_capture/content_capture_task_histogram_reporter.cc
new file mode 100644
index 0000000..7c3834b
--- /dev/null
+++ b/third_party/blink/renderer/core/content_capture/content_capture_task_histogram_reporter.cc
@@ -0,0 +1,82 @@
+// 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 <utility>
+
+#include "third_party/blink/renderer/core/content_capture/content_capture_task_histogram_reporter.h"
+
+namespace blink {
+
+// static
+constexpr char ContentCaptureTaskHistogramReporter::kCaptureContentTime[];
+constexpr char ContentCaptureTaskHistogramReporter::kCaptureOneContentTime[];
+constexpr char ContentCaptureTaskHistogramReporter::kCaptureContentDelayTime[];
+constexpr char ContentCaptureTaskHistogramReporter::kSendContentTime[];
+constexpr char ContentCaptureTaskHistogramReporter::kSentContentCount[];
+
+ContentCaptureTaskHistogramReporter::ContentCaptureTaskHistogramReporter()
+    : capture_content_delay_time_histogram_(kCaptureContentDelayTime,
+                                            500,
+                                            30000,
+                                            50),
+      capture_content_time_histogram_(kCaptureContentTime, 0, 50000, 50),
+      capture_one_content_time_histogram_(kCaptureOneContentTime, 0, 50000, 50),
+      send_content_time_histogram_(kSendContentTime, 0, 50000, 50),
+      sent_content_count_histogram_(kSentContentCount, 0, 10000, 50) {}
+
+ContentCaptureTaskHistogramReporter::~ContentCaptureTaskHistogramReporter() =
+    default;
+
+void ContentCaptureTaskHistogramReporter::OnContentChanged() {
+  if (content_change_time_)
+    return;
+  content_change_time_ = WTF::CurrentTimeTicks();
+}
+
+void ContentCaptureTaskHistogramReporter::OnCaptureContentStarted() {
+  capture_content_start_time_ = WTF::CurrentTimeTicks();
+}
+
+void ContentCaptureTaskHistogramReporter::OnCaptureContentEnded(
+    size_t captured_content_count) {
+  if (!captured_content_count) {
+    // We captured nothing for the recorded content change, reset the time to
+    // start again.
+    content_change_time_.reset();
+    return;
+  }
+  // Gives content_change_time_ to the change occurred while sending the
+  // content.
+  captured_content_change_time_ = std::move(content_change_time_);
+  base::TimeDelta delta = WTF::CurrentTimeTicks() - capture_content_start_time_;
+  capture_content_time_histogram_.CountMicroseconds(delta);
+  capture_one_content_time_histogram_.CountMicroseconds(delta /
+                                                        captured_content_count);
+}
+
+void ContentCaptureTaskHistogramReporter::OnSendContentStarted() {
+  send_content_start_time_ = WTF::CurrentTimeTicks();
+}
+
+void ContentCaptureTaskHistogramReporter::OnSendContentEnded(
+    size_t sent_content_count) {
+  TimeTicks now = WTF::CurrentTimeTicks();
+  if (captured_content_change_time_) {
+    TimeTicks content_change_time = captured_content_change_time_.value();
+    captured_content_change_time_.reset();
+    capture_content_delay_time_histogram_.CountMilliseconds(
+        now - content_change_time);
+  }
+  if (!sent_content_count)
+    return;
+  send_content_time_histogram_.CountMicroseconds(now -
+                                                 send_content_start_time_);
+}
+
+void ContentCaptureTaskHistogramReporter::RecordsSentContentCountPerDocument(
+    size_t sent_content_count) {
+  sent_content_count_histogram_.Count(sent_content_count);
+}
+
+}  // namespace blink
diff --git a/third_party/blink/renderer/core/content_capture/content_capture_task_histogram_reporter.h b/third_party/blink/renderer/core/content_capture/content_capture_task_histogram_reporter.h
new file mode 100644
index 0000000..49d5389
--- /dev/null
+++ b/third_party/blink/renderer/core/content_capture/content_capture_task_histogram_reporter.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 THIRD_PARTY_BLINK_RENDERER_CORE_CONTENT_CAPTURE_CONTENT_CAPTURE_TASK_HISTOGRAM_REPORTER_H_
+#define THIRD_PARTY_BLINK_RENDERER_CORE_CONTENT_CAPTURE_CONTENT_CAPTURE_TASK_HISTOGRAM_REPORTER_H_
+
+#include "base/optional.h"
+#include "third_party/blink/renderer/core/core_export.h"
+#include "third_party/blink/renderer/platform/histogram.h"
+#include "third_party/blink/renderer/platform/wtf/ref_counted.h"
+#include "third_party/blink/renderer/platform/wtf/time.h"
+
+namespace blink {
+
+// This class collects and reports metric data for the ContentCaptureTask.
+class CORE_EXPORT ContentCaptureTaskHistogramReporter
+    : public RefCounted<ContentCaptureTaskHistogramReporter> {
+ public:
+  // Visible for testing.
+  static constexpr char kCaptureContentDelayTime[] =
+      "ContentCapture.CaptureContentDelayTime";
+  static constexpr char kCaptureContentTime[] =
+      "ContentCapture.CaptureContentTime";
+  static constexpr char kCaptureOneContentTime[] =
+      "ContentCapture.CaptureOneContentTime";
+  static constexpr char kSendContentTime[] = "ContentCapture.SendContentTime";
+  static constexpr char kSentContentCount[] = "ContentCapture.SentContentCount";
+
+  ContentCaptureTaskHistogramReporter();
+  ~ContentCaptureTaskHistogramReporter();
+
+  void OnContentChanged();
+  void OnCaptureContentStarted();
+  void OnCaptureContentEnded(size_t captured_content_count);
+  void OnSendContentStarted();
+  void OnSendContentEnded(size_t sent_content_count);
+  void RecordsSentContentCountPerDocument(size_t sent_content_count);
+
+ private:
+  // The time of first content change since the last content captured.
+  base::Optional<WTF::TimeTicks> content_change_time_;
+  // The copy of |content_change_time| after the content has been captured; we
+  // need to record the time the content has been sent, |content_change_time_|
+  // shall be released for the next content change.
+  base::Optional<WTF::TimeTicks> captured_content_change_time_;
+  // The time to start capturing content.
+  WTF::TimeTicks capture_content_start_time_;
+  // The time to start sending content.
+  WTF::TimeTicks send_content_start_time_;
+
+  // Records time from first content change to content that has been sent, its
+  // range is 500ms from to 30s.
+  CustomCountHistogram capture_content_delay_time_histogram_;
+  // Records time to capture the content, its range is from 0 to 50,000
+  // microseconds.
+  CustomCountHistogram capture_content_time_histogram_;
+  // Records time to capture one content, this is the average of all captured
+  // content for a specific ContentCapture, its range is from 0 to 50,000
+  // microseconds.
+  CustomCountHistogram capture_one_content_time_histogram_;
+  // Records time to send the content, its range is from 0 to 50,000
+  // microseconds.
+  CustomCountHistogram send_content_time_histogram_;
+  // Records total count has been sent, its range is from 0 to 10,000.
+  LinearHistogram sent_content_count_histogram_;
+};
+
+}  // namespace blink
+
+#endif  // THIRD_PARTY_BLINK_RENDERER_CORE_CONTENT_CAPTURE_CONTENT_CAPTURE_TASK_HISTOGRAM_REPORTER_H_
diff --git a/third_party/blink/renderer/core/content_capture/content_capture_test.cc b/third_party/blink/renderer/core/content_capture/content_capture_test.cc
index dea49bb..513a76d 100644
--- a/third_party/blink/renderer/core/content_capture/content_capture_test.cc
+++ b/third_party/blink/renderer/core/content_capture/content_capture_test.cc
@@ -4,8 +4,10 @@
 
 #include "third_party/blink/renderer/core/content_capture/content_capture_manager.h"
 
+#include "base/test/metrics/histogram_tester.h"
 #include "third_party/blink/public/web/web_content_capture_client.h"
 #include "third_party/blink/public/web/web_content_holder.h"
+#include "third_party/blink/renderer/bindings/core/v8/v8_gc_controller.h"
 #include "third_party/blink/renderer/core/dom/element.h"
 #include "third_party/blink/renderer/core/dom/node.h"
 #include "third_party/blink/renderer/core/frame/web_local_frame_impl.h"
@@ -164,7 +166,8 @@
         "<p id='p5'>5</p>"
         "<p id='p6'>6</p>"
         "<p id='p7'>7</p>"
-        "<p id='p8'>8</p>");
+        "<p id='p8'>8</p>"
+        "<div id='d1'></div>");
     platform()->SetAutoAdvanceNowToPendingTasks(false);
     // TODO(michaelbai): ContentCaptureManager should be get from LocalFrame.
     content_capture_manager_ =
@@ -178,6 +181,20 @@
         ->SetCapturedContentForTesting(node_holders_);
   }
 
+  void CreateTextNodeAndNotifyManager() {
+    Document& doc = GetDocument();
+    Node* node = doc.createTextNode("New Text");
+    Element* element = Element::Create(html_names::kPTag, &doc);
+    element->appendChild(node);
+    Element* div_element = GetElementById("d1");
+    div_element->appendChild(element);
+    UpdateAllLifecyclePhasesForTest();
+    created_node_holder_ = GetContentCaptureManager()->GetNodeHolder(*node);
+    std::vector<NodeHolder> captured_content{created_node_holder_};
+    content_capture_manager_->GetContentCaptureTask()
+        ->SetCapturedContentForTesting(captured_content);
+  }
+
   ContentCaptureManagerTestHelper* GetContentCaptureManager() const {
     return content_capture_manager_;
   }
@@ -240,6 +257,7 @@
   std::unique_ptr<WebContentCaptureClientTestHelper> content_capture_client_;
   Persistent<ContentCaptureManagerTestHelper> content_capture_manager_;
   Persistent<ContentCaptureLocalFrameClientHelper> local_frame_client_;
+  NodeHolder created_node_holder_;
 };
 
 INSTANTIATE_TEST_SUITE_P(,
@@ -394,6 +412,113 @@
   EXPECT_EQ(1u, GetWebContentCaptureClient()->RemovedData().size());
 }
 
+TEST_P(ContentCaptureTest, TaskHistogramReporter) {
+  // This performs gc for all DocumentSession, flushes the existing
+  // SentContentCount and give a clean baseline for histograms.
+  // We are not sure if it always work, maybe still be the source of flaky.
+  V8GCController::CollectAllGarbageForTesting(v8::Isolate::GetCurrent());
+  base::HistogramTester histograms;
+
+  // The task stops before captures content.
+  GetContentCaptureTask()->SetTaskStopState(
+      ContentCaptureTask::TaskState::kCaptureContent);
+  RunContentCaptureTask();
+  // Verify no histogram reported yet.
+  histograms.ExpectTotalCount(
+      ContentCaptureTaskHistogramReporter::kCaptureContentTime, 0u);
+  histograms.ExpectTotalCount(
+      ContentCaptureTaskHistogramReporter::kCaptureOneContentTime, 0u);
+  histograms.ExpectTotalCount(
+      ContentCaptureTaskHistogramReporter::kSendContentTime, 0u);
+  histograms.ExpectTotalCount(
+      ContentCaptureTaskHistogramReporter::kCaptureContentDelayTime, 0u);
+  histograms.ExpectTotalCount(
+      ContentCaptureTaskHistogramReporter::kSentContentCount, 0u);
+
+  // The task stops before sends the captured content out.
+  GetContentCaptureTask()->SetTaskStopState(
+      ContentCaptureTask::TaskState::kProcessCurrentSession);
+  RunContentCaptureTask();
+  // Verify has one CaptureContentTime record.
+  histograms.ExpectTotalCount(
+      ContentCaptureTaskHistogramReporter::kCaptureContentTime, 1u);
+  histograms.ExpectTotalCount(
+      ContentCaptureTaskHistogramReporter::kCaptureOneContentTime, 1u);
+  histograms.ExpectTotalCount(
+      ContentCaptureTaskHistogramReporter::kSendContentTime, 0u);
+  histograms.ExpectTotalCount(
+      ContentCaptureTaskHistogramReporter::kCaptureContentDelayTime, 0u);
+  histograms.ExpectTotalCount(
+      ContentCaptureTaskHistogramReporter::kSentContentCount, 0u);
+
+  // The task stops at kProcessRetryTask because the captured content
+  // needs to be sent with 2 batch.
+  GetContentCaptureTask()->SetTaskStopState(
+      ContentCaptureTask::TaskState::kProcessRetryTask);
+  RunContentCaptureTask();
+  // Verify has one CaptureContentTime, one SendContentTime and one
+  // CaptureContentDelayTime record.
+  histograms.ExpectTotalCount(
+      ContentCaptureTaskHistogramReporter::kCaptureContentTime, 1u);
+  histograms.ExpectTotalCount(
+      ContentCaptureTaskHistogramReporter::kCaptureOneContentTime, 1u);
+  histograms.ExpectTotalCount(
+      ContentCaptureTaskHistogramReporter::kSendContentTime, 1u);
+  histograms.ExpectTotalCount(
+      ContentCaptureTaskHistogramReporter::kCaptureContentDelayTime, 1u);
+  histograms.ExpectTotalCount(
+      ContentCaptureTaskHistogramReporter::kSentContentCount, 0u);
+
+  // Run task until it stops, task will not capture content, because there is no
+  // content change.
+  GetContentCaptureTask()->SetTaskStopState(
+      ContentCaptureTask::TaskState::kStop);
+  RunContentCaptureTask();
+  // Verify has two SendContentTime records.
+  histograms.ExpectTotalCount(
+      ContentCaptureTaskHistogramReporter::kCaptureContentTime, 1u);
+  histograms.ExpectTotalCount(
+      ContentCaptureTaskHistogramReporter::kCaptureOneContentTime, 1u);
+  histograms.ExpectTotalCount(
+      ContentCaptureTaskHistogramReporter::kSendContentTime, 2u);
+  histograms.ExpectTotalCount(
+      ContentCaptureTaskHistogramReporter::kCaptureContentDelayTime, 1u);
+  histograms.ExpectTotalCount(
+      ContentCaptureTaskHistogramReporter::kSentContentCount, 0u);
+
+  // Create a node and run task until it stops.
+  CreateTextNodeAndNotifyManager();
+  GetContentCaptureTask()->SetTaskStopState(
+      ContentCaptureTask::TaskState::kStop);
+  RunLongDelayContentCaptureTask();
+  histograms.ExpectTotalCount(
+      ContentCaptureTaskHistogramReporter::kCaptureContentTime, 2u);
+  histograms.ExpectTotalCount(
+      ContentCaptureTaskHistogramReporter::kCaptureOneContentTime, 2u);
+  histograms.ExpectTotalCount(
+      ContentCaptureTaskHistogramReporter::kSendContentTime, 3u);
+  histograms.ExpectTotalCount(
+      ContentCaptureTaskHistogramReporter::kCaptureContentDelayTime, 2u);
+  histograms.ExpectTotalCount(
+      ContentCaptureTaskHistogramReporter::kSentContentCount, 0u);
+
+  GetContentCaptureTask()->ClearDocumentSessionsForTesting();
+  V8GCController::CollectAllGarbageForTesting(v8::Isolate::GetCurrent());
+  histograms.ExpectTotalCount(
+      ContentCaptureTaskHistogramReporter::kCaptureContentTime, 2u);
+  histograms.ExpectTotalCount(
+      ContentCaptureTaskHistogramReporter::kCaptureOneContentTime, 2u);
+  histograms.ExpectTotalCount(
+      ContentCaptureTaskHistogramReporter::kSendContentTime, 3u);
+  histograms.ExpectTotalCount(
+      ContentCaptureTaskHistogramReporter::kCaptureContentDelayTime, 2u);
+  histograms.ExpectTotalCount(
+      ContentCaptureTaskHistogramReporter::kSentContentCount, 1u);
+  // Verify total content has been sent.
+  histograms.ExpectBucketCount(
+      ContentCaptureTaskHistogramReporter::kSentContentCount, 9u, 1u);
+}
+
 // TODO(michaelbai): use RenderingTest instead of PageTestBase for multiple
 // frame test.
 class ContentCaptureSimTest
diff --git a/third_party/blink/renderer/core/content_capture/task_session.cc b/third_party/blink/renderer/core/content_capture/task_session.cc
index b1499ed..fa22a40 100644
--- a/third_party/blink/renderer/core/content_capture/task_session.cc
+++ b/third_party/blink/renderer/core/content_capture/task_session.cc
@@ -14,10 +14,14 @@
 namespace blink {
 
 TaskSession::DocumentSession::DocumentSession(const Document& document,
-                                              SentNodes& sent_nodes)
-    : document_(&document), sent_nodes_(&sent_nodes) {}
+                                              SentNodes& sent_nodes,
+                                              SentNodeCountCallback& callback)
+    : document_(&document), sent_nodes_(&sent_nodes), callback_(callback) {}
 
-TaskSession::DocumentSession::~DocumentSession() = default;
+TaskSession::DocumentSession::~DocumentSession() {
+  if (callback_.has_value())
+    callback_.value().Run(total_sent_nodes_);
+}
 
 void TaskSession::DocumentSession::AddNodeHolder(cc::NodeHolder node_holder) {
   captured_content_.push_back(node_holder);
@@ -129,7 +133,8 @@
     const Document& doc) {
   DocumentSession* doc_session = GetDocumentSession(doc);
   if (!doc_session) {
-    doc_session = MakeGarbageCollected<DocumentSession>(doc, *sent_nodes_);
+    doc_session =
+        MakeGarbageCollected<DocumentSession>(doc, *sent_nodes_, callback_);
     to_document_session_.insert(&doc, doc_session);
   }
   return *doc_session;
@@ -148,4 +153,8 @@
   visitor->Trace(to_document_session_);
 }
 
+void TaskSession::ClearDocumentSessionsForTesting() {
+  to_document_session_.clear();
+}
+
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/content_capture/task_session.h b/third_party/blink/renderer/core/content_capture/task_session.h
index 1361c52..a148d7f 100644
--- a/third_party/blink/renderer/core/content_capture/task_session.h
+++ b/third_party/blink/renderer/core/content_capture/task_session.h
@@ -5,8 +5,10 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_CORE_CONTENT_CAPTURE_TASK_SESSION_H_
 #define THIRD_PARTY_BLINK_RENDERER_CORE_CONTENT_CAPTURE_TASK_SESSION_H_
 
+#include <utility>
 #include <vector>
 
+#include "base/callback.h"
 #include "base/memory/scoped_refptr.h"
 #include "cc/paint/node_holder.h"
 #include "third_party/blink/renderer/platform/heap/garbage_collected.h"
@@ -43,7 +45,12 @@
   // document is GC-ed, see TaskSession::to_document_session_.
   class DocumentSession : public GarbageCollectedFinalized<DocumentSession> {
    public:
-    DocumentSession(const Document& document, SentNodes& sent_nodes);
+    // The callback for total_sent_nodes_ metrics.
+    using SentNodeCountCallback = base::RepeatingCallback<void(size_t)>;
+
+    DocumentSession(const Document& document,
+                    SentNodes& sent_nodes,
+                    SentNodeCountCallback& call_back);
     ~DocumentSession();
     void AddNodeHolder(cc::NodeHolder node_holder);
     void AddDetachedNode(int64_t id);
@@ -79,6 +86,9 @@
     bool first_data_has_sent_ = false;
     // This is for the metrics to record the total node that has been sent.
     size_t total_sent_nodes_ = 0;
+    // Histogram could be disabed in low time resolution OS, see
+    // TimeTicks::IsHighResolution and ContentCaptureTask.
+    base::Optional<SentNodeCountCallback> callback_;
   };
 
   TaskSession(SentNodes& sent_nodes);
@@ -94,8 +104,15 @@
 
   bool HasUnsentData() const { return has_unsent_data_; }
 
+  void SetSentNodeCountCallback(
+      DocumentSession::SentNodeCountCallback call_back) {
+    callback_ = std::move(call_back);
+  }
+
   void Trace(blink::Visitor*);
 
+  void ClearDocumentSessionsForTesting();
+
  private:
   void GroupCapturedContentByDocument(
       const std::vector<cc::NodeHolder>& captured_content);
@@ -113,6 +130,7 @@
   // DocumentSession, this is used to avoid to iterate all document sessions
   // to find out if there is any of them.
   bool has_unsent_data_ = false;
+  DocumentSession::SentNodeCountCallback callback_;
 };
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/core_initializer.cc b/third_party/blink/renderer/core/core_initializer.cc
index 4b2cc24..45a820c 100644
--- a/third_party/blink/renderer/core/core_initializer.cc
+++ b/third_party/blink/renderer/core/core_initializer.cc
@@ -32,7 +32,6 @@
 
 #include "third_party/blink/public/platform/platform.h"
 #include "third_party/blink/renderer/bindings/core/v8/binding_security.h"
-#include "third_party/blink/renderer/bindings/core/v8/script_streamer_thread.h"
 #include "third_party/blink/renderer/bindings/core/v8/v8_throw_dom_exception.h"
 #include "third_party/blink/renderer/core/css/media_feature_names.h"
 #include "third_party/blink/renderer/core/css/media_query_evaluator.h"
@@ -147,7 +146,6 @@
   V8ThrowDOMException::Init();
 
   BindingSecurity::Init();
-  ScriptStreamerThread::Init();
 }
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/css/parser/css_property_parser_helpers.cc b/third_party/blink/renderer/core/css/parser/css_property_parser_helpers.cc
index d8ce37f6..fa62584 100644
--- a/third_party/blink/renderer/core/css/parser/css_property_parser_helpers.cc
+++ b/third_party/blink/renderer/core/css/parser/css_property_parser_helpers.cc
@@ -1817,10 +1817,14 @@
           feature = WebFeature::kCSSValueAppearancePushButton;
         else if (value_id == CSSValueRadio)
           feature = WebFeature::kCSSValueAppearanceRadio;
+        else if (value_id == CSSValueSearchfieldCancelButton)
+          feature = WebFeature::kCSSValueAppearanceSearchCancel;
         else if (value_id == CSSValueSquareButton)
           feature = WebFeature::kCSSValueAppearanceSquareButton;
         else if (value_id == CSSValueSearchfield)
           feature = WebFeature::kCSSValueAppearanceSearchField;
+        else if (value_id == CSSValueTextarea)
+          feature = WebFeature::kCSSValueAppearanceTextarea;
         else if (value_id == CSSValueTextfield)
           feature = WebFeature::kCSSValueAppearanceTextField;
         else
diff --git a/third_party/blink/renderer/core/dom/BUILD.gn b/third_party/blink/renderer/core/dom/BUILD.gn
index b82181e..a680be6 100644
--- a/third_party/blink/renderer/core/dom/BUILD.gn
+++ b/third_party/blink/renderer/core/dom/BUILD.gn
@@ -95,8 +95,6 @@
     "element_rare_data.cc",
     "element_rare_data.h",
     "element_traversal.h",
-    "element_visibility_observer.cc",
-    "element_visibility_observer.h",
     "empty_node_list.cc",
     "empty_node_list.h",
     "events/add_event_listener_options_defaults.h",
diff --git a/third_party/blink/renderer/core/dom/element_visibility_observer.cc b/third_party/blink/renderer/core/dom/element_visibility_observer.cc
deleted file mode 100644
index 2ce69d3..0000000
--- a/third_party/blink/renderer/core/dom/element_visibility_observer.cc
+++ /dev/null
@@ -1,60 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "third_party/blink/renderer/core/dom/element_visibility_observer.h"
-
-#include "third_party/blink/renderer/core/dom/document.h"
-#include "third_party/blink/renderer/core/dom/element.h"
-#include "third_party/blink/renderer/core/frame/local_frame.h"
-#include "third_party/blink/renderer/core/intersection_observer/intersection_observer_entry.h"
-#include "third_party/blink/renderer/platform/wtf/functional.h"
-
-namespace blink {
-
-ElementVisibilityObserver::ElementVisibilityObserver(
-    Element* element,
-    VisibilityCallback callback)
-    : element_(element), callback_(std::move(callback)) {}
-
-ElementVisibilityObserver::~ElementVisibilityObserver() = default;
-
-void ElementVisibilityObserver::Start(float threshold) {
-  DCHECK(!intersection_observer_);
-
-  ExecutionContext* context = element_->GetExecutionContext();
-  Document& document = To<Document>(*context);
-
-  intersection_observer_ = IntersectionObserver::Create(
-      {} /* root_margin */, {threshold}, &document,
-      WTF::BindRepeating(&ElementVisibilityObserver::OnVisibilityChanged,
-                         WrapWeakPersistent(this)));
-  DCHECK(intersection_observer_);
-
-  intersection_observer_->observe(element_.Release());
-}
-
-void ElementVisibilityObserver::Stop() {
-  DCHECK(intersection_observer_);
-
-  intersection_observer_->disconnect();
-  intersection_observer_ = nullptr;
-}
-
-void ElementVisibilityObserver::DeliverObservationsForTesting() {
-  intersection_observer_->Deliver();
-}
-
-void ElementVisibilityObserver::Trace(Visitor* visitor) {
-  visitor->Trace(element_);
-  visitor->Trace(intersection_observer_);
-}
-
-void ElementVisibilityObserver::OnVisibilityChanged(
-    const HeapVector<Member<IntersectionObserverEntry>>& entries) {
-  bool is_visible = entries.back()->intersectionRatio() >=
-                    intersection_observer_->thresholds()[0];
-  callback_.Run(is_visible);
-}
-
-}  // namespace blink
diff --git a/third_party/blink/renderer/core/dom/element_visibility_observer.h b/third_party/blink/renderer/core/dom/element_visibility_observer.h
deleted file mode 100644
index c095713..0000000
--- a/third_party/blink/renderer/core/dom/element_visibility_observer.h
+++ /dev/null
@@ -1,62 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef THIRD_PARTY_BLINK_RENDERER_CORE_DOM_ELEMENT_VISIBILITY_OBSERVER_H_
-#define THIRD_PARTY_BLINK_RENDERER_CORE_DOM_ELEMENT_VISIBILITY_OBSERVER_H_
-
-#include <limits>
-
-#include "base/callback.h"
-#include "base/macros.h"
-#include "third_party/blink/renderer/core/core_export.h"
-#include "third_party/blink/renderer/core/intersection_observer/intersection_observer.h"
-#include "third_party/blink/renderer/platform/heap/heap.h"
-#include "third_party/blink/renderer/platform/heap/member.h"
-
-namespace blink {
-
-class Element;
-
-// ElementVisibilityObserver is a helper class to be used to track the
-// visibility of an Element in the viewport. Creating an
-// ElementVisibilityObserver is a no-op with regards to CPU cycle. The observing
-// has be started by calling |start()| and can be stopped with |stop()|.
-// When creating an instance, the caller will have to pass a callback taking
-// a boolean as an argument. The boolean will be the new visibility state.
-// The ElementVisibilityObserver is implemented on top of IntersectionObserver.
-// It is a layer meant to simplify the usage for C++ Blink code checking for the
-// visibility of an element.
-class CORE_EXPORT ElementVisibilityObserver final
-    : public GarbageCollectedFinalized<ElementVisibilityObserver> {
- public:
-  using VisibilityCallback = base::RepeatingCallback<void(bool)>;
-
-  ElementVisibilityObserver(Element*, VisibilityCallback);
-  virtual ~ElementVisibilityObserver();
-
-  // The |threshold| is the minimum fraction that needs to be visible.
-  // See https://github.com/WICG/IntersectionObserver/issues/164 for why this
-  // defaults to std::numeric_limits<float>::min() rather than zero.
-  void Start(float threshold = std::numeric_limits<float>::min());
-  void Stop();
-
-  void DeliverObservationsForTesting();
-
-  virtual void Trace(Visitor*);
-
- private:
-  class ElementVisibilityCallback;
-
-  void OnVisibilityChanged(
-      const HeapVector<Member<IntersectionObserverEntry>>&);
-
-  Member<Element> element_;
-  Member<IntersectionObserver> intersection_observer_;
-  VisibilityCallback callback_;
-  DISALLOW_COPY_AND_ASSIGN(ElementVisibilityObserver);
-};
-
-}  // namespace blink
-
-#endif  // THIRD_PARTY_BLINK_RENDERER_CORE_DOM_ELEMENT_VISIBILITY_OBSERVER_H_
diff --git a/third_party/blink/renderer/core/dom/element_visibility_observer_test.cc b/third_party/blink/renderer/core/dom/element_visibility_observer_test.cc
deleted file mode 100644
index f629fe86..0000000
--- a/third_party/blink/renderer/core/dom/element_visibility_observer_test.cc
+++ /dev/null
@@ -1,58 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "third_party/blink/renderer/core/dom/element_visibility_observer.h"
-
-#include "testing/gtest/include/gtest/gtest.h"
-#include "third_party/blink/renderer/core/dom/document.h"
-#include "third_party/blink/renderer/core/dom/dom_implementation.h"
-#include "third_party/blink/renderer/core/exported/web_remote_frame_impl.h"
-#include "third_party/blink/renderer/core/frame/frame_test_helpers.h"
-#include "third_party/blink/renderer/core/frame/web_local_frame_impl.h"
-#include "third_party/blink/renderer/core/html/html_div_element.h"
-#include "third_party/blink/renderer/core/html/html_document.h"
-#include "third_party/blink/renderer/core/loader/empty_clients.h"
-
-namespace blink {
-
-namespace {
-
-class ElementVisibilityObserverTest : public ::testing::Test {
- protected:
-  frame_test_helpers::WebViewHelper helper_;
-};
-
-TEST_F(ElementVisibilityObserverTest, ObserveElementWithoutDocumentFrame) {
-  helper_.Initialize();
-  Document& document = *helper_.LocalMainFrame()->GetFrame()->GetDocument();
-  HTMLElement* element = HTMLDivElement::Create(
-      *DOMImplementation::Create(document)->createHTMLDocument("test"));
-  ElementVisibilityObserver* observer =
-      MakeGarbageCollected<ElementVisibilityObserver>(
-          element, ElementVisibilityObserver::VisibilityCallback());
-  observer->Start();
-  observer->Stop();
-  // It should not crash.
-}
-
-TEST_F(ElementVisibilityObserverTest, ObserveElementWithRemoteFrameParent) {
-  helper_.InitializeRemote();
-
-  WebLocalFrameImpl* child_frame =
-      frame_test_helpers::CreateLocalChild(*helper_.RemoteMainFrame());
-  Document& document = *child_frame->GetFrame()->GetDocument();
-
-  Persistent<HTMLElement> element = HTMLDivElement::Create(document);
-  ElementVisibilityObserver* observer =
-      MakeGarbageCollected<ElementVisibilityObserver>(
-          element, WTF::BindRepeating([](bool) {}));
-  observer->Start();
-  observer->DeliverObservationsForTesting();
-  observer->Stop();
-  // It should not crash.
-}
-
-}  // anonymous namespace
-
-}  // blink namespace
diff --git a/third_party/blink/renderer/core/editing/finder/find_buffer.cc b/third_party/blink/renderer/core/editing/finder/find_buffer.cc
index 306582cb..c150176 100644
--- a/third_party/blink/renderer/core/editing/finder/find_buffer.cc
+++ b/third_party/blink/renderer/core/editing/finder/find_buffer.cc
@@ -96,15 +96,23 @@
 }
 
 void FindBuffer::InvisibleLayoutScope::EnsureRecalc(Node& block_root) {
+  if (!RuntimeEnabledFeatures::InvisibleDOMEnabled())
+    return;
   if (did_recalc_)
     return;
   did_recalc_ = true;
   DCHECK(block_root.GetDocument().Lifecycle().GetState() >=
          DocumentLifecycle::kStyleClean);
+  // If we're in an invisible subtree, we should recalc style from the invisible
+  // root/the highest ancestor of |block_root| with the invisible attribute,
+  // otherwise we should recalc from |block_root|.
+  // InvisibleRoot is always non-null when IsInsideInvisibleSubtree is true.
   if (InvisibleDOM::IsInsideInvisibleSubtree(block_root))
     invisible_root_ = InvisibleDOM::InvisibleRoot(block_root);
   else
     invisible_root_ = &ToElement(block_root);
+
+  DCHECK(invisible_root_);
   invisible_root_->GetDocument().SetFindInPageRoot(invisible_root_);
   invisible_root_->SetNeedsStyleRecalc(
       kSubtreeStyleChange,
@@ -117,6 +125,8 @@
 }
 
 FindBuffer::InvisibleLayoutScope::~InvisibleLayoutScope() {
+  if (!RuntimeEnabledFeatures::InvisibleDOMEnabled())
+    return;
   if (!did_recalc_)
     return;
   invisible_root_->GetDocument().SetFindInPageRoot(nullptr);
@@ -319,7 +329,8 @@
 
   // Calculate layout tree and style for invisible nodes inside the whole
   // subtree of |block_ancestor|.
-  if (node && InvisibleDOM::IsInsideInvisibleSubtree(*node))
+  if (RuntimeEnabledFeatures::InvisibleDOMEnabled() && node &&
+      InvisibleDOM::IsInsideInvisibleSubtree(*node))
     invisible_layout_scope_.EnsureRecalc(block_ancestor);
 
   // Collect all text under |block_ancestor| to |buffer_|,
@@ -352,7 +363,8 @@
       node = FlatTreeTraversal::NextSkippingChildren(*node);
       continue;
     }
-    if (node->IsElementNode() && ToElement(node)->HasInvisibleAttribute() &&
+    if (RuntimeEnabledFeatures::InvisibleDOMEnabled() &&
+        node->IsElementNode() && ToElement(node)->HasInvisibleAttribute() &&
         !invisible_layout_scope_.DidRecalc()) {
       // We found and invisible node. Calculate the layout & style for the whole
       // block at once, and we need to recalculate the NGOffsetMapping and start
diff --git a/third_party/blink/renderer/core/editing/finder/text_finder.cc b/third_party/blink/renderer/core/editing/finder/text_finder.cc
index c993b07..a5a26c5 100644
--- a/third_party/blink/renderer/core/editing/finder/text_finder.cc
+++ b/third_party/blink/renderer/core/editing/finder/text_finder.cc
@@ -74,7 +74,8 @@
 
 static void ScrollToVisible(Range* match) {
   const Node& first_node = *match->FirstNode();
-  if (InvisibleDOM::ActivateRangeIfNeeded(EphemeralRangeInFlatTree(match)))
+  if (RuntimeEnabledFeatures::InvisibleDOMEnabled() &&
+      InvisibleDOM::ActivateRangeIfNeeded(EphemeralRangeInFlatTree(match)))
     first_node.GetDocument().UpdateStyleAndLayoutIgnorePendingStylesheets();
   Settings* settings = first_node.GetDocument().GetSettings();
   bool smooth_find_enabled =
diff --git a/third_party/blink/renderer/core/editing/inline_box_position.cc b/third_party/blink/renderer/core/editing/inline_box_position.cc
index 73d81dd4..4380bf7 100644
--- a/third_party/blink/renderer/core/editing/inline_box_position.cc
+++ b/third_party/blink/renderer/core/editing/inline_box_position.cc
@@ -45,7 +45,7 @@
 
 namespace {
 
-const int kBlockFlowAdjustmentMaxRecursionDepth = 1024;
+const int kBlockFlowAdjustmentMaxRecursionDepth = 256;
 
 bool IsNonTextLeafChild(LayoutObject* object) {
   if (object->SlowFirstChild())
diff --git a/third_party/blink/renderer/core/editing/inline_box_traversal.cc b/third_party/blink/renderer/core/editing/inline_box_traversal.cc
index 8e7d6bd..4f31146 100644
--- a/third_party/blink/renderer/core/editing/inline_box_traversal.cc
+++ b/third_party/blink/renderer/core/editing/inline_box_traversal.cc
@@ -602,20 +602,17 @@
 
  public:
   static SelectionInFlatTree AdjustFor(
-      const VisiblePositionInFlatTree& visible_base,
-      const VisiblePositionInFlatTree& visible_extent) {
-    DCHECK(visible_base.IsValid());
-    DCHECK(visible_extent.IsValid());
-
+      const PositionInFlatTreeWithAffinity& visible_base,
+      const PositionInFlatTreeWithAffinity& visible_extent) {
     const SelectionInFlatTree& unchanged_selection =
         SelectionInFlatTree::Builder()
-            .SetBaseAndExtent(visible_base.DeepEquivalent(),
-                              visible_extent.DeepEquivalent())
+            .SetBaseAndExtent(visible_base.GetPosition(),
+                              visible_extent.GetPosition())
             .Build();
 
     if (RuntimeEnabledFeatures::BidiCaretAffinityEnabled()) {
-      if (NGInlineFormattingContextOf(visible_base.DeepEquivalent()) ||
-          NGInlineFormattingContextOf(visible_extent.DeepEquivalent()))
+      if (NGInlineFormattingContextOf(visible_base.GetPosition()) ||
+          NGInlineFormattingContextOf(visible_extent.GetPosition()))
         return unchanged_selection;
     }
 
@@ -632,7 +629,7 @@
         const PositionInFlatTree adjusted_base =
             CreateVisiblePosition(base.GetPosition()).DeepEquivalent();
         return SelectionInFlatTree::Builder()
-            .SetBaseAndExtent(adjusted_base, visible_extent.DeepEquivalent())
+            .SetBaseAndExtent(adjusted_base, visible_extent.GetPosition())
             .Build();
       }
       return unchanged_selection;
@@ -642,7 +639,7 @@
       const PositionInFlatTree adjusted_extent =
           CreateVisiblePosition(extent.GetPosition()).DeepEquivalent();
       return SelectionInFlatTree::Builder()
-          .SetBaseAndExtent(visible_base.DeepEquivalent(), adjusted_extent)
+          .SetBaseAndExtent(visible_base.GetPosition(), adjusted_extent)
           .Build();
     }
 
@@ -655,7 +652,7 @@
 
    public:
     RenderedPosition() = default;
-    static RenderedPosition Create(const VisiblePositionInFlatTree&);
+    static RenderedPosition Create(const PositionInFlatTreeWithAffinity&);
 
     bool IsNull() const { return box_.IsNull(); }
     bool operator==(const RenderedPosition& other) const {
@@ -736,12 +733,11 @@
 
     // Helper function for Create().
     static RenderedPosition CreateUncanonicalized(
-        const VisiblePositionInFlatTree& position) {
-      if (position.IsNull() ||
-          !position.DeepEquivalent().AnchorNode()->GetLayoutObject())
+        const PositionInFlatTreeWithAffinity& position) {
+      if (position.IsNull() || !position.AnchorNode()->GetLayoutObject())
         return RenderedPosition();
       const PositionInFlatTreeWithAffinity adjusted =
-          ComputeInlineAdjustedPosition(position.ToPositionWithAffinity());
+          ComputeInlineAdjustedPosition(position);
       if (adjusted.IsNull())
         return RenderedPosition();
 
@@ -782,7 +778,7 @@
 
 RangeSelectionAdjuster::RenderedPosition
 RangeSelectionAdjuster::RenderedPosition::Create(
-    const VisiblePositionInFlatTree& position) {
+    const PositionInFlatTreeWithAffinity& position) {
   const RenderedPosition uncanonicalized = CreateUncanonicalized(position);
   const BidiBoundaryType potential_type = uncanonicalized.bidi_boundary_type_;
   if (potential_type == BidiBoundaryType::kNotBoundary)
@@ -904,8 +900,8 @@
 }
 
 SelectionInFlatTree BidiAdjustment::AdjustForRangeSelection(
-    const VisiblePositionInFlatTree& base,
-    const VisiblePositionInFlatTree& extent) {
+    const PositionInFlatTreeWithAffinity& base,
+    const PositionInFlatTreeWithAffinity& extent) {
   return RangeSelectionAdjuster::AdjustFor(base, extent);
 }
 
diff --git a/third_party/blink/renderer/core/editing/inline_box_traversal.h b/third_party/blink/renderer/core/editing/inline_box_traversal.h
index 092bcf78..3e433017 100644
--- a/third_party/blink/renderer/core/editing/inline_box_traversal.h
+++ b/third_party/blink/renderer/core/editing/inline_box_traversal.h
@@ -37,8 +37,8 @@
   // even with bidi adjustment.
   // TODO(editing-dev): Eliminate |VisiblePosition| from this function.
   static SelectionInFlatTree AdjustForRangeSelection(
-      const VisiblePositionInFlatTree&,
-      const VisiblePositionInFlatTree&);
+      const PositionInFlatTreeWithAffinity&,
+      const PositionInFlatTreeWithAffinity&);
 };
 
 // This class provides common traveral functions on list of |InlineBox|.
diff --git a/third_party/blink/renderer/core/editing/selection_controller.cc b/third_party/blink/renderer/core/editing/selection_controller.cc
index 94049eb..3f2bcc1 100644
--- a/third_party/blink/renderer/core/editing/selection_controller.cc
+++ b/third_party/blink/renderer/core/editing/selection_controller.cc
@@ -322,14 +322,16 @@
   // link or image.
   bool extend_selection = IsExtendingSelection(event);
 
-  const VisiblePositionInFlatTree& visible_hit_position = CreateVisiblePosition(
-      PositionWithAffinityOfHitTestResult(event.GetHitTestResult()));
+  const PositionInFlatTreeWithAffinity visible_hit_position =
+      CreateVisiblePosition(
+          PositionWithAffinityOfHitTestResult(event.GetHitTestResult()))
+          .ToPositionWithAffinity();
   const PositionInFlatTreeWithAffinity& position_to_use =
       visible_hit_position.IsNull()
           ? CreateVisiblePosition(
                 PositionInFlatTree::FirstPositionInOrBeforeNode(*inner_node))
                 .ToPositionWithAffinity()
-          : visible_hit_position.ToPositionWithAffinity();
+          : visible_hit_position;
   const VisibleSelectionInFlatTree& selection =
       this->Selection().ComputeVisibleSelectionInFlatTree();
 
@@ -619,16 +621,17 @@
     adjusted_hit_test_result.SetNodeAndPosition(result.InnerNode(),
                                                 LayoutPoint(0, 0));
 
-  const VisiblePositionInFlatTree& pos = CreateVisiblePosition(
-      PositionWithAffinityOfHitTestResult(adjusted_hit_test_result));
+  const PositionInFlatTreeWithAffinity pos =
+      CreateVisiblePosition(
+          PositionWithAffinityOfHitTestResult(adjusted_hit_test_result))
+          .ToPositionWithAffinity();
   const SelectionInFlatTree new_selection =
-      pos.IsNotNull() ? CreateVisibleSelectionWithGranularity(
-                            SelectionInFlatTree::Builder()
-                                .Collapse(pos.ToPositionWithAffinity())
-                                .Build(),
-                            TextGranularity::kWord)
-                            .AsSelection()
-                      : SelectionInFlatTree();
+      pos.IsNotNull()
+          ? CreateVisibleSelectionWithGranularity(
+                SelectionInFlatTree::Builder().Collapse(pos).Build(),
+                TextGranularity::kWord)
+                .AsSelection()
+          : SelectionInFlatTree();
 
   // TODO(editing-dev): Fix CreateVisibleSelectionWithGranularity() to not
   // return invalid ranges. Until we do that, we need this check here to avoid a
@@ -646,7 +649,7 @@
 
     Element* const editable =
         RootEditableElementOf(new_selection.ComputeStartPosition());
-    if (editable && pos.DeepEquivalent() ==
+    if (editable && pos.GetPosition() ==
                         VisiblePositionInFlatTree::LastPositionInNode(*editable)
                             .DeepEquivalent())
       return false;
@@ -675,8 +678,9 @@
   if (!inner_node || !inner_node->GetLayoutObject())
     return;
 
-  const VisiblePositionInFlatTree& pos =
-      CreateVisiblePosition(PositionWithAffinityOfHitTestResult(result));
+  const PositionInFlatTreeWithAffinity pos =
+      CreateVisiblePosition(PositionWithAffinityOfHitTestResult(result))
+          .ToPositionWithAffinity();
   if (pos.IsNull()) {
     UpdateSelectionForMouseDownDispatchingSelectStart(
         inner_node, SelectionInFlatTree(),
@@ -687,7 +691,7 @@
   }
 
   const PositionInFlatTree& marker_position =
-      pos.DeepEquivalent().ParentAnchoredEquivalent();
+      pos.GetPosition().ParentAnchoredEquivalent();
   const DocumentMarker* const marker = SpellCheckMarkerAtPosition(
       inner_node->GetDocument().Markers(), marker_position);
   if (!marker) {
@@ -764,11 +768,12 @@
     return;
 
   Element* url_element = result.GetHitTestResult().URLElement();
-  const VisiblePositionInFlatTree& pos = CreateVisiblePosition(
-      PositionWithAffinityOfHitTestResult(result.GetHitTestResult()));
+  const PositionInFlatTreeWithAffinity pos =
+      CreateVisiblePosition(
+          PositionWithAffinityOfHitTestResult(result.GetHitTestResult()))
+          .ToPositionWithAffinity();
   const SelectionInFlatTree& new_selection =
-      pos.IsNotNull() &&
-              pos.DeepEquivalent().AnchorNode()->IsDescendantOf(url_element)
+      pos.IsNotNull() && pos.AnchorNode()->IsDescendantOf(url_element)
           ? SelectionInFlatTree::Builder()
                 .SelectAllChildren(*url_element)
                 .Build()
@@ -800,26 +805,27 @@
   // to |CreateVisiblePosition()| for |original_base|.
   const PositionInFlatTree& base_position =
       original_base_in_flat_tree_.GetPosition();
-  const VisiblePositionInFlatTree& original_base =
-      base_position.IsConnected() ? CreateVisiblePosition(base_position)
-                                  : VisiblePositionInFlatTree();
-  const VisiblePositionInFlatTree& base =
+  const PositionInFlatTreeWithAffinity original_base =
+      base_position.IsConnected()
+          ? CreateVisiblePosition(base_position).ToPositionWithAffinity()
+          : PositionInFlatTreeWithAffinity();
+  const PositionInFlatTreeWithAffinity base =
       original_base.IsNotNull() ? original_base
-                                : CreateVisiblePosition(new_selection.Base());
-  const VisiblePositionInFlatTree& extent =
-      CreateVisiblePosition(new_selection.Extent());
+                                : CreateVisiblePosition(new_selection.Base())
+                                      .ToPositionWithAffinity();
+  const PositionInFlatTreeWithAffinity extent =
+      CreateVisiblePosition(new_selection.Extent()).ToPositionWithAffinity();
   const SelectionInFlatTree& adjusted_selection =
       endpoints_adjustment_mode == kAdjustEndpointsAtBidiBoundary
           ? BidiAdjustment::AdjustForRangeSelection(base, extent)
           : SelectionInFlatTree::Builder()
-                .SetBaseAndExtent(base.DeepEquivalent(),
-                                  extent.DeepEquivalent())
+                .SetBaseAndExtent(base.GetPosition(), extent.GetPosition())
                 .Build();
 
   SelectionInFlatTree::Builder builder(new_selection);
-  if (adjusted_selection.Base() != base.DeepEquivalent() ||
-      adjusted_selection.Extent() != extent.DeepEquivalent()) {
-    original_base_in_flat_tree_ = base.ToPositionWithAffinity();
+  if (adjusted_selection.Base() != base.GetPosition() ||
+      adjusted_selection.Extent() != extent.GetPosition()) {
+    original_base_in_flat_tree_ = base;
     SetContext(&GetDocument());
     builder.SetBaseAndExtent(adjusted_selection.Base(),
                              adjusted_selection.Extent());
@@ -828,7 +834,7 @@
             Selection().ComputeVisibleSelectionInFlatTree().Base())
             .DeepEquivalent() ==
         CreateVisiblePosition(new_selection.Base()).DeepEquivalent()) {
-      builder.SetBaseAndExtent(original_base.DeepEquivalent(),
+      builder.SetBaseAndExtent(original_base.GetPosition(),
                                new_selection.Extent());
     }
     original_base_in_flat_tree_ = PositionInFlatTreeWithAffinity();
@@ -863,12 +869,15 @@
     const HitTestResult& hit_test_result) {
   Node* inner_node = hit_test_result.InnerNode();
   DCHECK(inner_node);
-  const VisiblePositionInFlatTree& visible_hit_pos = CreateVisiblePosition(
-      PositionWithAffinityOfHitTestResult(hit_test_result));
-  const VisiblePositionInFlatTree& visible_pos =
+  const PositionInFlatTreeWithAffinity visible_hit_pos =
+      CreateVisiblePosition(
+          PositionWithAffinityOfHitTestResult(hit_test_result))
+          .ToPositionWithAffinity();
+  const PositionInFlatTreeWithAffinity visible_pos =
       visible_hit_pos.IsNull()
           ? CreateVisiblePosition(
                 PositionInFlatTree::FirstPositionInOrBeforeNode(*inner_node))
+                .ToPositionWithAffinity()
           : visible_hit_pos;
 
   if (visible_pos.IsNull()) {
@@ -880,9 +889,8 @@
   UpdateSelectionForMouseDownDispatchingSelectStart(
       inner_node,
       ExpandSelectionToRespectUserSelectAll(
-          inner_node, SelectionInFlatTree::Builder()
-                          .Collapse(visible_pos.ToPositionWithAffinity())
-                          .Build()),
+          inner_node,
+          SelectionInFlatTree::Builder().Collapse(visible_pos).Build()),
       SetSelectionOptions::Builder().SetShouldShowHandle(true).Build());
 }
 
@@ -939,16 +947,17 @@
         mouse_down_may_start_select_))
     return false;
 
-  const VisiblePositionInFlatTree& pos = CreateVisiblePosition(
-      PositionWithAffinityOfHitTestResult(event.GetHitTestResult()));
+  const PositionInFlatTreeWithAffinity pos =
+      CreateVisiblePosition(
+          PositionWithAffinityOfHitTestResult(event.GetHitTestResult()))
+          .ToPositionWithAffinity();
   const SelectionInFlatTree new_selection =
-      pos.IsNotNull() ? CreateVisibleSelectionWithGranularity(
-                            SelectionInFlatTree::Builder()
-                                .Collapse(pos.ToPositionWithAffinity())
-                                .Build(),
-                            TextGranularity::kParagraph)
-                            .AsSelection()
-                      : SelectionInFlatTree();
+      pos.IsNotNull()
+          ? CreateVisibleSelectionWithGranularity(
+                SelectionInFlatTree::Builder().Collapse(pos).Build(),
+                TextGranularity::kParagraph)
+                .AsSelection()
+          : SelectionInFlatTree();
 
   const bool is_handle_visible =
       event.Event().FromTouch() && new_selection.IsRange();
@@ -1067,10 +1076,12 @@
     SelectionInFlatTree::Builder builder;
     Node* node = event.InnerNode();
     if (node && node->GetLayoutObject() && HasEditableStyle(*node)) {
-      const VisiblePositionInFlatTree& pos = CreateVisiblePosition(
-          PositionWithAffinityOfHitTestResult(event.GetHitTestResult()));
+      const PositionInFlatTreeWithAffinity pos =
+          CreateVisiblePosition(
+              PositionWithAffinityOfHitTestResult(event.GetHitTestResult()))
+              .ToPositionWithAffinity();
       if (pos.IsNotNull())
-        builder.Collapse(pos.ToPositionWithAffinity());
+        builder.Collapse(pos);
     }
 
     const SelectionInFlatTree new_selection = builder.Build();
@@ -1234,16 +1245,16 @@
   if (!Selection().Contains(p))
     return;
 
-  const VisiblePositionInFlatTree& visible_pos = CreateVisiblePosition(
-      PositionWithAffinityOfHitTestResult(mev.GetHitTestResult()));
+  const PositionInFlatTreeWithAffinity visible_pos =
+      CreateVisiblePosition(
+          PositionWithAffinityOfHitTestResult(mev.GetHitTestResult()))
+          .ToPositionWithAffinity();
   if (visible_pos.IsNull()) {
     Selection().SetSelectionAndEndTyping(SelectionInDOMTree());
     return;
   }
   Selection().SetSelectionAndEndTyping(ConvertToSelectionInDOMTree(
-      SelectionInFlatTree::Builder()
-          .Collapse(visible_pos.ToPositionWithAffinity())
-          .Build()));
+      SelectionInFlatTree::Builder().Collapse(visible_pos).Build()));
 }
 
 void SelectionController::InitializeSelectionState() {
diff --git a/third_party/blink/renderer/core/exported/web_associated_url_loader_impl_test.cc b/third_party/blink/renderer/core/exported/web_associated_url_loader_impl_test.cc
index 78b1bfd6..d13d9c5 100644
--- a/third_party/blink/renderer/core/exported/web_associated_url_loader_impl_test.cc
+++ b/third_party/blink/renderer/core/exported/web_associated_url_loader_impl_test.cc
@@ -227,7 +227,7 @@
     WebString header_name_string(WebString::FromUTF8(header_name));
     expected_response_ = WebURLResponse();
     expected_response_.SetMIMEType("text/html");
-    expected_response_.SetHTTPStatusCode(200);
+    expected_response_.SetHttpStatusCode(200);
     expected_response_.AddHTTPHeaderField("Access-Control-Allow-Origin", "*");
     if (exposed) {
       expected_response_.AddHTTPHeaderField("access-control-expose-headers",
@@ -281,7 +281,7 @@
 
   expected_response_ = WebURLResponse();
   expected_response_.SetMIMEType("text/html");
-  expected_response_.SetHTTPStatusCode(200);
+  expected_response_.SetHttpStatusCode(200);
   Platform::Current()->GetURLLoaderMockFactory()->RegisterURL(
       url, expected_response_, frame_file_path_);
 
@@ -316,7 +316,7 @@
 
   expected_response_ = WebURLResponse();
   expected_response_.SetMIMEType("text/html");
-  expected_response_.SetHTTPStatusCode(200);
+  expected_response_.SetHttpStatusCode(200);
   Platform::Current()->GetURLLoaderMockFactory()->RegisterURL(
       url, expected_response_, frame_file_path_);
 
@@ -341,7 +341,7 @@
 
   expected_response_ = WebURLResponse();
   expected_response_.SetMIMEType("text/html");
-  expected_response_.SetHTTPStatusCode(200);
+  expected_response_.SetHttpStatusCode(200);
   expected_response_.AddHTTPHeaderField("access-control-allow-origin", "*");
   Platform::Current()->GetURLLoaderMockFactory()->RegisterURL(
       url, expected_response_, frame_file_path_);
@@ -369,7 +369,7 @@
 
   expected_response_ = WebURLResponse();
   expected_response_.SetMIMEType("text/html");
-  expected_response_.SetHTTPStatusCode(200);
+  expected_response_.SetHttpStatusCode(200);
   expected_response_.AddHTTPHeaderField("access-control-allow-origin", "*");
   Platform::Current()->GetURLLoaderMockFactory()->RegisterURL(
       url, expected_response_, frame_file_path_);
@@ -399,7 +399,7 @@
 
   expected_response_ = WebURLResponse();
   expected_response_.SetMIMEType("text/html");
-  expected_response_.SetHTTPStatusCode(0);
+  expected_response_.SetHttpStatusCode(0);
   expected_response_.AddHTTPHeaderField("access-control-allow-origin", "*");
   Platform::Current()->GetURLLoaderMockFactory()->RegisterURL(
       url, expected_response_, frame_file_path_);
@@ -429,7 +429,7 @@
 
   expected_redirect_response_ = WebURLResponse();
   expected_redirect_response_.SetMIMEType("text/html");
-  expected_redirect_response_.SetHTTPStatusCode(301);
+  expected_redirect_response_.SetHttpStatusCode(301);
   expected_redirect_response_.SetHTTPHeaderField("Location", redirect);
   Platform::Current()->GetURLLoaderMockFactory()->RegisterURL(
       url, expected_redirect_response_, frame_file_path_);
@@ -438,7 +438,7 @@
 
   expected_response_ = WebURLResponse();
   expected_response_.SetMIMEType("text/html");
-  expected_response_.SetHTTPStatusCode(200);
+  expected_response_.SetHttpStatusCode(200);
   Platform::Current()->GetURLLoaderMockFactory()->RegisterURL(
       redirect_url, expected_response_, frame_file_path_);
 
@@ -465,7 +465,7 @@
 
   expected_redirect_response_ = WebURLResponse();
   expected_redirect_response_.SetMIMEType("text/html");
-  expected_redirect_response_.SetHTTPStatusCode(301);
+  expected_redirect_response_.SetHttpStatusCode(301);
   expected_redirect_response_.SetHTTPHeaderField("Location", redirect);
   Platform::Current()->GetURLLoaderMockFactory()->RegisterURL(
       url, expected_redirect_response_, frame_file_path_);
@@ -474,7 +474,7 @@
 
   expected_response_ = WebURLResponse();
   expected_response_.SetMIMEType("text/html");
-  expected_response_.SetHTTPStatusCode(200);
+  expected_response_.SetHttpStatusCode(200);
   Platform::Current()->GetURLLoaderMockFactory()->RegisterURL(
       redirect_url, expected_response_, frame_file_path_);
 
@@ -505,7 +505,7 @@
 
   expected_redirect_response_ = WebURLResponse();
   expected_redirect_response_.SetMIMEType("text/html");
-  expected_redirect_response_.SetHTTPStatusCode(301);
+  expected_redirect_response_.SetHttpStatusCode(301);
   expected_redirect_response_.SetHTTPHeaderField("Location", redirect);
   Platform::Current()->GetURLLoaderMockFactory()->RegisterURL(
       url, expected_redirect_response_, frame_file_path_);
@@ -514,7 +514,7 @@
 
   expected_response_ = WebURLResponse();
   expected_response_.SetMIMEType("text/html");
-  expected_response_.SetHTTPStatusCode(200);
+  expected_response_.SetHttpStatusCode(200);
   Platform::Current()->GetURLLoaderMockFactory()->RegisterURL(
       redirect_url, expected_response_, frame_file_path_);
 
@@ -552,7 +552,7 @@
   // control checks.
   expected_redirect_response_ = WebURLResponse();
   expected_redirect_response_.SetMIMEType("text/html");
-  expected_redirect_response_.SetHTTPStatusCode(301);
+  expected_redirect_response_.SetHttpStatusCode(301);
   expected_redirect_response_.SetHTTPHeaderField("Location", redirect);
   expected_redirect_response_.AddHTTPHeaderField("access-control-allow-origin",
                                                  "*");
@@ -563,7 +563,7 @@
 
   expected_response_ = WebURLResponse();
   expected_response_.SetMIMEType("text/html");
-  expected_response_.SetHTTPStatusCode(200);
+  expected_response_.SetHttpStatusCode(200);
   expected_response_.AddHTTPHeaderField("access-control-allow-origin", "*");
   Platform::Current()->GetURLLoaderMockFactory()->RegisterURL(
       redirect_url, expected_response_, frame_file_path_);
@@ -670,7 +670,7 @@
   WebString header_name_string(WebString::FromUTF8("non-whitelisted"));
   expected_response_ = WebURLResponse();
   expected_response_.SetMIMEType("text/html");
-  expected_response_.SetHTTPStatusCode(200);
+  expected_response_.SetHttpStatusCode(200);
   expected_response_.AddHTTPHeaderField("Access-Control-Allow-Origin", "*");
   expected_response_.AddHTTPHeaderField(header_name_string, "foo");
   Platform::Current()->GetURLLoaderMockFactory()->RegisterURL(
@@ -700,7 +700,7 @@
 
   expected_response_ = WebURLResponse();
   expected_response_.SetMIMEType("text/plain");
-  expected_response_.SetHTTPStatusCode(200);
+  expected_response_.SetHttpStatusCode(200);
   Platform::Current()->GetURLLoaderMockFactory()->RegisterURL(
       url, expected_response_, frame_file_path_);
 
@@ -727,7 +727,7 @@
 
   expected_response_ = WebURLResponse();
   expected_response_.SetMIMEType("text/plain");
-  expected_response_.SetHTTPStatusCode(200);
+  expected_response_.SetHttpStatusCode(200);
   Platform::Current()->GetURLLoaderMockFactory()->RegisterURL(
       url, expected_response_, frame_file_path_);
 
diff --git a/third_party/blink/renderer/core/exported/web_console_message.cc b/third_party/blink/renderer/core/exported/web_console_message.cc
index c981006..bfc6f4e 100644
--- a/third_party/blink/renderer/core/exported/web_console_message.cc
+++ b/third_party/blink/renderer/core/exported/web_console_message.cc
@@ -4,59 +4,20 @@
 
 #include "third_party/blink/public/web/web_console_message.h"
 
-#include "third_party/blink/renderer/bindings/core/v8/source_location.h"
 #include "third_party/blink/renderer/bindings/core/v8/v8_binding_for_core.h"
-#include "third_party/blink/renderer/core/dom/document.h"
-#include "third_party/blink/renderer/core/dom/dom_node_ids.h"
 #include "third_party/blink/renderer/core/execution_context/execution_context.h"
 #include "third_party/blink/renderer/core/inspector/console_message.h"
-#include "third_party/blink/renderer/platform/wtf/casting.h"
-#include "third_party/blink/renderer/platform/wtf/vector.h"
-#include "v8/include/v8.h"
 
 namespace blink {
 
 void WebConsoleMessage::LogWebConsoleMessage(v8::Local<v8::Context> context,
                                              const WebConsoleMessage& message) {
-  MessageLevel web_core_message_level = kInfoMessageLevel;
-  switch (message.level) {
-    case mojom::ConsoleMessageLevel::kVerbose:
-      web_core_message_level = kVerboseMessageLevel;
-      break;
-    case mojom::ConsoleMessageLevel::kInfo:
-      web_core_message_level = kInfoMessageLevel;
-      break;
-    case mojom::ConsoleMessageLevel::kWarning:
-      web_core_message_level = kWarningMessageLevel;
-      break;
-    case mojom::ConsoleMessageLevel::kError:
-      web_core_message_level = kErrorMessageLevel;
-      break;
-  }
-
-  MessageSource message_source = message.nodes.empty()
-                                     ? kOtherMessageSource
-                                     : kRecommendationMessageSource;
-
   auto* execution_context = ToExecutionContext(context);
   if (!execution_context)  // Can happen in unittests.
     return;
 
-  ConsoleMessage* console_message = ConsoleMessage::Create(
-      message_source, web_core_message_level, message.text,
-      SourceLocation::Create(message.url, message.line_number,
-                             message.column_number, nullptr));
-
-  if (auto* document = DynamicTo<Document>(execution_context)) {
-    if (auto* frame = document->GetFrame()) {
-      Vector<DOMNodeId> nodes;
-      for (const WebNode& web_node : message.nodes)
-        nodes.push_back(DOMNodeIds::IdForNode(&(*web_node)));
-      console_message->SetNodes(frame, std::move(nodes));
-    }
-  }
-
-  execution_context->AddConsoleMessage(console_message);
+  execution_context->AddConsoleMessage(
+      ConsoleMessage::CreateFromWebConsoleMessage(message, nullptr));
 }
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/exported/web_frame_test.cc b/third_party/blink/renderer/core/exported/web_frame_test.cc
index 3ded69f..f5fc44b 100644
--- a/third_party/blink/renderer/core/exported/web_frame_test.cc
+++ b/third_party/blink/renderer/core/exported/web_frame_test.cc
@@ -7284,7 +7284,7 @@
   WebURL redirect_url(ToKURL(redirect));
   WebURLResponse redirect_response;
   redirect_response.SetMIMEType("text/html");
-  redirect_response.SetHTTPStatusCode(302);
+  redirect_response.SetHttpStatusCode(302);
   redirect_response.SetHTTPHeaderField("Location", redirect);
   Platform::Current()->GetURLLoaderMockFactory()->RegisterURL(
       test_url, redirect_response, file_path);
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 159b845..bcfc925 100644
--- a/third_party/blink/renderer/core/exported/web_view_impl.cc
+++ b/third_party/blink/renderer/core/exported/web_view_impl.cc
@@ -2183,7 +2183,8 @@
   // perform the conversion here.  TODO(bokan): Convert this function to take
   // coordinates in absolute/root-frame coordinates to make this more
   // consistent. https://crbug.com/931447.
-  if (root_scroller != MainFrameImpl()->GetFrame()->GetDocument()) {
+  if (root_scroller != MainFrameImpl()->GetFrame()->GetDocument() &&
+      controller.RootScrollerArea()) {
     ScrollOffset offset = controller.RootScrollerArea()->GetScrollOffset();
     element_bounds_in_content.Move(FlooredIntSize(offset));
     caret_bounds_in_content.Move(FlooredIntSize(offset));
@@ -3472,9 +3473,9 @@
   scoped_defer_main_frame_update_ = layer_tree_view_->DeferMainFrameUpdate();
 }
 
-void WebViewImpl::StartDeferringCommits() {
+void WebViewImpl::StartDeferringCommits(base::TimeDelta timeout) {
   if (layer_tree_view_)
-    layer_tree_view_->StartDeferringCommits();
+    layer_tree_view_->StartDeferringCommits(timeout);
 }
 
 void WebViewImpl::StopDeferringCommits() {
diff --git a/third_party/blink/renderer/core/exported/web_view_impl.h b/third_party/blink/renderer/core/exported/web_view_impl.h
index 5ebbe21..52b62bac 100644
--- a/third_party/blink/renderer/core/exported/web_view_impl.h
+++ b/third_party/blink/renderer/core/exported/web_view_impl.h
@@ -405,7 +405,7 @@
 
   void DeferMainFrameUpdateForTesting();
 
-  void StartDeferringCommits();
+  void StartDeferringCommits(base::TimeDelta timeout);
   void StopDeferringCommits();
 
  private:
diff --git a/third_party/blink/renderer/core/feature_policy/feature_policy.cc b/third_party/blink/renderer/core/feature_policy/feature_policy.cc
index 107e3f2c..4b596e2 100644
--- a/third_party/blink/renderer/core/feature_policy/feature_policy.cc
+++ b/third_party/blink/renderer/core/feature_policy/feature_policy.cc
@@ -46,6 +46,15 @@
   return !origin_trial_enabled(execution_context);
 }
 
+// TODO(loonybear): once the new syntax is implemented, use this method to
+// parse the policy value for each parameterized feature, and for non
+// parameterized feature (i.e. boolean-type policy value).
+PolicyValue GetFallbackValueForFeature(mojom::FeaturePolicyFeature feature) {
+  if (feature == mojom::FeaturePolicyFeature::kOversizedImages)
+    return PolicyValue(2.0, mojom::PolicyValueType::kDecDouble);
+  return PolicyValue(false);
+}
+
 }  // namespace
 
 ParsedFeaturePolicy ParseFeaturePolicyHeader(
@@ -137,6 +146,9 @@
       }
 
       ParsedFeaturePolicyDeclaration allowlist(feature, feature_type);
+      // TODO(loonybear): fallback value should be parsed from the new syntax.
+      allowlist.fallback_value = GetFallbackValueForFeature(feature);
+      allowlist.opaque_value = GetFallbackValueForFeature(feature);
       features_specified.QuickSet(static_cast<int>(feature));
       std::map<url::Origin, PolicyValue> values;
       PolicyValue value = PolicyValue::CreateMaxPolicyValue(feature_type);
@@ -158,6 +170,8 @@
       }
 
       for (wtf_size_t i = 1; i < tokens.size(); i++) {
+        // TODO(loonybear): for each token, parse the policy value from the
+        // new syntax.
         if (!tokens[i].ContainsOnlyASCIIOrEmpty()) {
           messages->push_back("Non-ASCII characters in origin.");
           continue;
@@ -181,8 +195,8 @@
         } else if (EqualIgnoringASCIICase(tokens[i], "'none'")) {
           continue;
         } else if (tokens[i] == "*") {
-          allowlist.fallback_value.SetToMax();
-          allowlist.opaque_value.SetToMax();
+          allowlist.fallback_value = value;
+          allowlist.opaque_value = value;
           break;
         } else {
           scoped_refptr<SecurityOrigin> target_origin =
diff --git a/third_party/blink/renderer/core/feature_policy/feature_policy_test.cc b/third_party/blink/renderer/core/feature_policy/feature_policy_test.cc
index df86cd9..b045cb51 100644
--- a/third_party/blink/renderer/core/feature_policy/feature_policy_test.cc
+++ b/third_party/blink/renderer/core/feature_policy/feature_policy_test.cc
@@ -80,14 +80,19 @@
   url::Origin expected_url_origin_b_ = url::Origin::Create(GURL(ORIGIN_B));
   url::Origin expected_url_origin_c_ = url::Origin::Create(GURL(ORIGIN_C));
 
-  // TODO(loonybear): Add a case for non-bool type feature.
   const FeatureNameMap test_feature_name_map = {
       {"fullscreen", blink::mojom::FeaturePolicyFeature::kFullscreen},
       {"payment", blink::mojom::FeaturePolicyFeature::kPayment},
-      {"geolocation", blink::mojom::FeaturePolicyFeature::kGeolocation}};
+      {"geolocation", blink::mojom::FeaturePolicyFeature::kGeolocation},
+      {"oversized-images",
+       blink::mojom::FeaturePolicyFeature::kOversizedImages}};
 
   const PolicyValue min_value = PolicyValue(false);
   const PolicyValue max_value = PolicyValue(true);
+  const PolicyValue min_double_value =
+      PolicyValue(2.0, mojom::PolicyValueType::kDecDouble);
+  const PolicyValue max_double_value =
+      PolicyValue::CreateMaxPolicyValue(mojom::PolicyValueType::kDecDouble);
 };
 
 TEST_F(FeaturePolicyParserTest, ParseValidPolicy) {
@@ -388,6 +393,37 @@
       static_cast<int>(blink::mojom::FeaturePolicyFeature::kGeolocation), 1);
 }
 
+TEST_F(FeaturePolicyParserTest, ParseParameterizedFeatures) {
+  Vector<String> messages;
+
+  scoped_refptr<SecurityOrigin> opaque_origin =
+      SecurityOrigin::CreateUniqueOpaque();
+
+  // Simple policy with *.
+  ParsedFeaturePolicy parsed_policy =
+      ParseFeaturePolicy("oversized-images *", origin_a_.get(),
+                         opaque_origin.get(), &messages, test_feature_name_map);
+  EXPECT_EQ(1UL, parsed_policy.size());
+  EXPECT_EQ(mojom::FeaturePolicyFeature::kOversizedImages,
+            parsed_policy[0].feature);
+  EXPECT_EQ(max_double_value, parsed_policy[0].fallback_value);
+  EXPECT_EQ(max_double_value, parsed_policy[0].opaque_value);
+  EXPECT_EQ(0UL, parsed_policy[0].values.size());
+
+  // Policy with explicit origins
+  parsed_policy = ParseFeaturePolicy(
+      "oversized-images https://example.net 'src'", origin_a_.get(),
+      opaque_origin.get(), &messages, test_feature_name_map);
+  EXPECT_EQ(1UL, parsed_policy.size());
+
+  EXPECT_EQ(mojom::FeaturePolicyFeature::kOversizedImages,
+            parsed_policy[0].feature);
+  EXPECT_GE(min_double_value, parsed_policy[0].fallback_value);
+  EXPECT_LE(max_double_value, parsed_policy[0].opaque_value);
+  EXPECT_EQ(1UL, parsed_policy[0].values.size());
+  EXPECT_LE(max_double_value, parsed_policy[0].values.begin()->second);
+}
+
 // Test policy mutation methods
 class FeaturePolicyMutationTest : public testing::Test {
  protected:
@@ -433,6 +469,10 @@
 
   const PolicyValue min_value = PolicyValue(false);
   const PolicyValue max_value = PolicyValue(true);
+  const PolicyValue min_double_value =
+      PolicyValue(2.0, mojom::PolicyValueType::kDecDouble);
+  const PolicyValue max_double_value =
+      PolicyValue::CreateMaxPolicyValue(mojom::PolicyValueType::kDecDouble);
 
   ParsedFeaturePolicy test_policy = {
       {mojom::FeaturePolicyFeature::kFullscreen,
diff --git a/third_party/blink/renderer/core/fetch/body_stream_buffer.cc b/third_party/blink/renderer/core/fetch/body_stream_buffer.cc
index 67b8769..266f0816 100644
--- a/third_party/blink/renderer/core/fetch/body_stream_buffer.cc
+++ b/third_party/blink/renderer/core/fetch/body_stream_buffer.cc
@@ -349,7 +349,7 @@
     return;
   }
 
-  if (stream_->IsInternalStreamMissing()) {
+  if (stream_->IsBroken()) {
     stream_broken_ = true;
     exception_state.ThrowDOMException(
         DOMExceptionCode::kInvalidStateError,
@@ -513,20 +513,13 @@
 
   if (made_from_readable_stream_) {
     ScriptState::Scope scope(script_state_);
-    // We need to have |reader| alive by some means (as noted in
-    // ReadableStreamDataBytesConsumer). Based on the following facts:
-    //  - This function is used only from Tee and StartLoading.
-    //  - This branch cannot be taken when called from Tee.
-    //  - StartLoading makes HasPendingActivity return true while loading.
-    //  - ReadableStream holds a reference to |reader| inside JS.
-    // we don't need to keep the reader explicitly.
-    ScriptValue reader = stream_->getReader(script_state_, exception_state);
+    auto* consumer = MakeGarbageCollected<ReadableStreamBytesConsumer>(
+        script_state_, stream_, exception_state);
     if (exception_state.HadException()) {
       stream_broken_ = true;
       return nullptr;
     }
-    return MakeGarbageCollected<ReadableStreamBytesConsumer>(script_state_,
-                                                             reader);
+    return consumer;
   }
   // We need to call these before calling CloseAndLockAndDisturb.
   const base::Optional<bool> is_closed = IsStreamClosed(exception_state);
diff --git a/third_party/blink/renderer/core/fetch/readable_stream_bytes_consumer.cc b/third_party/blink/renderer/core/fetch/readable_stream_bytes_consumer.cc
index 3f89e4b..a05744f77 100644
--- a/third_party/blink/renderer/core/fetch/readable_stream_bytes_consumer.cc
+++ b/third_party/blink/renderer/core/fetch/readable_stream_bytes_consumer.cc
@@ -12,7 +12,6 @@
 #include "third_party/blink/renderer/bindings/core/v8/script_value.h"
 #include "third_party/blink/renderer/bindings/core/v8/v8_iterator_result_value.h"
 #include "third_party/blink/renderer/bindings/core/v8/v8_uint8_array.h"
-#include "third_party/blink/renderer/core/streams/readable_stream_operations.h"
 #include "third_party/blink/renderer/platform/bindings/scoped_persistent.h"
 #include "third_party/blink/renderer/platform/bindings/script_state.h"
 #include "third_party/blink/renderer/platform/bindings/v8_binding_macros.h"
@@ -97,10 +96,10 @@
 
 ReadableStreamBytesConsumer::ReadableStreamBytesConsumer(
     ScriptState* script_state,
-    ScriptValue stream_reader)
-    : reader_(script_state->GetIsolate(), stream_reader.V8Value()),
-      script_state_(script_state) {
-}
+    ReadableStream* stream,
+    ExceptionState& exception_state)
+    : read_handle_(stream->GetReadHandle(script_state, exception_state)),
+      script_state_(script_state) {}
 
 ReadableStreamBytesConsumer::~ReadableStreamBytesConsumer() {}
 
@@ -124,11 +123,8 @@
   if (!is_reading_) {
     is_reading_ = true;
     ScriptState::Scope scope(script_state_);
-    ScriptValue reader(script_state_,
-                       reader_.NewLocal(script_state_->GetIsolate()));
-    // The owner must retain the reader.
-    DCHECK(!reader.IsEmpty());
-    ReadableStreamOperations::DefaultReaderRead(script_state_, reader)
+    DCHECK(read_handle_);
+    read_handle_->Read(script_state_)
         .Then(OnFulfilled::CreateFunction(script_state_, this),
               OnRejected::CreateFunction(script_state_, this))
         .MarkAsHandled();
@@ -162,7 +158,7 @@
     return;
   state_ = PublicState::kClosed;
   ClearClient();
-  reader_.Clear();
+  read_handle_ = nullptr;
 }
 
 BytesConsumer::PublicState ReadableStreamBytesConsumer::GetPublicState() const {
@@ -174,7 +170,7 @@
 }
 
 void ReadableStreamBytesConsumer::Trace(blink::Visitor* visitor) {
-  visitor->Trace(reader_);
+  visitor->Trace(read_handle_);
   visitor->Trace(client_);
   visitor->Trace(pending_buffer_);
   visitor->Trace(script_state_);
@@ -182,7 +178,7 @@
 }
 
 void ReadableStreamBytesConsumer::Dispose() {
-  reader_.Clear();
+  read_handle_ = nullptr;
 }
 
 void ReadableStreamBytesConsumer::OnRead(DOMUint8Array* buffer) {
@@ -207,7 +203,7 @@
     return;
   DCHECK_EQ(state_, PublicState::kReadableOrWaiting);
   state_ = PublicState::kClosed;
-  reader_.Clear();
+  read_handle_ = nullptr;
   Client* client = client_;
   ClearClient();
   if (client)
@@ -222,7 +218,7 @@
     return;
   DCHECK_EQ(state_, PublicState::kReadableOrWaiting);
   state_ = PublicState::kErrored;
-  reader_.Clear();
+  read_handle_ = nullptr;
   Client* client = client_;
   ClearClient();
   if (client)
diff --git a/third_party/blink/renderer/core/fetch/readable_stream_bytes_consumer.h b/third_party/blink/renderer/core/fetch/readable_stream_bytes_consumer.h
index beb0cf0..21a54ccb 100644
--- a/third_party/blink/renderer/core/fetch/readable_stream_bytes_consumer.h
+++ b/third_party/blink/renderer/core/fetch/readable_stream_bytes_consumer.h
@@ -9,8 +9,9 @@
 #include "base/memory/scoped_refptr.h"
 #include "third_party/blink/renderer/bindings/core/v8/script_value.h"
 #include "third_party/blink/renderer/core/core_export.h"
+#include "third_party/blink/renderer/core/streams/readable_stream.h"
 #include "third_party/blink/renderer/core/typed_arrays/dom_typed_array.h"
-#include "third_party/blink/renderer/platform/bindings/trace_wrapper_v8_reference.h"
+#include "third_party/blink/renderer/platform/bindings/trace_wrapper_member.h"
 #include "third_party/blink/renderer/platform/heap/handle.h"
 #include "third_party/blink/renderer/platform/loader/fetch/bytes_consumer.h"
 #include "third_party/blink/renderer/platform/wtf/forward.h"
@@ -19,15 +20,14 @@
 
 class ScriptState;
 
-// This class is a BytesConsumer pulling bytes from ReadableStream
-// implemented with V8 Extras.
+// This class is a BytesConsumer pulling bytes from a ReadableStream.
 // The stream will be immediately locked by the consumer and will never be
 // released.
 class CORE_EXPORT ReadableStreamBytesConsumer final : public BytesConsumer {
   USING_PRE_FINALIZER(ReadableStreamBytesConsumer, Dispose);
 
  public:
-  ReadableStreamBytesConsumer(ScriptState*, ScriptValue stream_reader);
+  ReadableStreamBytesConsumer(ScriptState*, ReadableStream*, ExceptionState&);
   ~ReadableStreamBytesConsumer() override;
 
   Result BeginRead(const char** buffer, size_t* available) override;
@@ -52,7 +52,7 @@
   void OnRejected();
   void Notify();
 
-  TraceWrapperV8Reference<v8::Value> reader_;
+  TraceWrapperMember<ReadableStream::ReadHandle> read_handle_;
   Member<ScriptState> script_state_;
   Member<BytesConsumer::Client> client_;
   Member<DOMUint8Array> pending_buffer_;
diff --git a/third_party/blink/renderer/core/fetch/readable_stream_bytes_consumer_test.cc b/third_party/blink/renderer/core/fetch/readable_stream_bytes_consumer_test.cc
index 7bdef5d9..7407865 100644
--- a/third_party/blink/renderer/core/fetch/readable_stream_bytes_consumer_test.cc
+++ b/third_party/blink/renderer/core/fetch/readable_stream_bytes_consumer_test.cc
@@ -55,12 +55,9 @@
   ASSERT_TRUE(stream);
   ASSERT_FALSE(exception_state.HadException());
 
-  ScriptValue reader = stream->getReader(script_state, exception_state);
-  ASSERT_FALSE(reader.IsEmpty());
-  ASSERT_FALSE(exception_state.HadException());
-
   Persistent<BytesConsumer> consumer =
-      MakeGarbageCollected<ReadableStreamBytesConsumer>(script_state, reader);
+      MakeGarbageCollected<ReadableStreamBytesConsumer>(script_state, stream,
+                                                        ASSERT_NO_EXCEPTION);
 
   EXPECT_EQ(PublicState::kReadableOrWaiting, consumer->GetPublicState());
 }
@@ -68,7 +65,6 @@
 TEST(ReadableStreamBytesConsumerTest, EmptyStream) {
   V8TestingScope scope;
   ScriptState* script_state = scope.GetScriptState();
-  ExceptionState& exception_state = scope.GetExceptionState();
 
   auto* underlying_source =
       MakeGarbageCollected<TestUnderlyingSource>(script_state);
@@ -76,12 +72,9 @@
       script_state, underlying_source, 0);
   underlying_source->Close();
 
-  ScriptValue reader = stream->getReader(script_state, exception_state);
-  ASSERT_FALSE(reader.IsEmpty());
-  ASSERT_FALSE(exception_state.HadException());
-
   Persistent<BytesConsumer> consumer =
-      MakeGarbageCollected<ReadableStreamBytesConsumer>(script_state, reader);
+      MakeGarbageCollected<ReadableStreamBytesConsumer>(script_state, stream,
+                                                        ASSERT_NO_EXCEPTION);
 
   Persistent<MockClient> client = MockClient::Create();
   consumer->SetClient(client);
@@ -111,7 +104,6 @@
 TEST(ReadableStreamBytesConsumerTest, ErroredStream) {
   V8TestingScope scope;
   ScriptState* script_state = scope.GetScriptState();
-  ExceptionState& exception_state = scope.GetExceptionState();
 
   auto* underlying_source =
       MakeGarbageCollected<TestUnderlyingSource>(script_state);
@@ -120,12 +112,9 @@
   underlying_source->SetError(
       ScriptValue(script_state, v8::Undefined(script_state->GetIsolate())));
 
-  ScriptValue reader = stream->getReader(script_state, exception_state);
-  ASSERT_FALSE(reader.IsEmpty());
-  ASSERT_FALSE(exception_state.HadException());
-
   Persistent<BytesConsumer> consumer =
-      MakeGarbageCollected<ReadableStreamBytesConsumer>(script_state, reader);
+      MakeGarbageCollected<ReadableStreamBytesConsumer>(script_state, stream,
+                                                        ASSERT_NO_EXCEPTION);
   Persistent<MockClient> client = MockClient::Create();
   consumer->SetClient(client);
   Checkpoint checkpoint;
@@ -154,7 +143,6 @@
 TEST(ReadableStreamBytesConsumerTest, TwoPhaseRead) {
   V8TestingScope scope;
   ScriptState* script_state = scope.GetScriptState();
-  ExceptionState& exception_state = scope.GetExceptionState();
 
   auto* underlying_source =
       MakeGarbageCollected<TestUnderlyingSource>(script_state);
@@ -181,11 +169,9 @@
     underlying_source->Close();
   }
 
-  ScriptValue reader = stream->getReader(script_state, exception_state);
-  ASSERT_FALSE(reader.IsEmpty());
-  ASSERT_FALSE(exception_state.HadException());
   Persistent<BytesConsumer> consumer =
-      MakeGarbageCollected<ReadableStreamBytesConsumer>(script_state, reader);
+      MakeGarbageCollected<ReadableStreamBytesConsumer>(script_state, stream,
+                                                        ASSERT_NO_EXCEPTION);
   Persistent<MockClient> client = MockClient::Create();
   consumer->SetClient(client);
   Checkpoint checkpoint;
@@ -266,7 +252,6 @@
 TEST(ReadableStreamBytesConsumerTest, EnqueueUndefined) {
   V8TestingScope scope;
   ScriptState* script_state = scope.GetScriptState();
-  ExceptionState& exception_state = scope.GetExceptionState();
 
   auto* underlying_source =
       MakeGarbageCollected<TestUnderlyingSource>(script_state);
@@ -276,11 +261,9 @@
       ScriptValue(script_state, v8::Undefined(script_state->GetIsolate())));
   underlying_source->Close();
 
-  ScriptValue reader = stream->getReader(script_state, exception_state);
-  ASSERT_FALSE(reader.IsEmpty());
-  ASSERT_FALSE(exception_state.HadException());
   Persistent<BytesConsumer> consumer =
-      MakeGarbageCollected<ReadableStreamBytesConsumer>(script_state, reader);
+      MakeGarbageCollected<ReadableStreamBytesConsumer>(script_state, stream,
+                                                        ASSERT_NO_EXCEPTION);
   Persistent<MockClient> client = MockClient::Create();
   consumer->SetClient(client);
   Checkpoint checkpoint;
@@ -309,7 +292,6 @@
 TEST(ReadableStreamBytesConsumerTest, EnqueueNull) {
   V8TestingScope scope;
   ScriptState* script_state = scope.GetScriptState();
-  ExceptionState& exception_state = scope.GetExceptionState();
 
   auto* underlying_source =
       MakeGarbageCollected<TestUnderlyingSource>(script_state);
@@ -319,11 +301,9 @@
       ScriptValue(script_state, v8::Null(script_state->GetIsolate())));
   underlying_source->Close();
 
-  ScriptValue reader = stream->getReader(script_state, exception_state);
-  ASSERT_FALSE(reader.IsEmpty());
-  ASSERT_FALSE(exception_state.HadException());
   Persistent<BytesConsumer> consumer =
-      MakeGarbageCollected<ReadableStreamBytesConsumer>(script_state, reader);
+      MakeGarbageCollected<ReadableStreamBytesConsumer>(script_state, stream,
+                                                        ASSERT_NO_EXCEPTION);
   Persistent<MockClient> client = MockClient::Create();
   consumer->SetClient(client);
   Checkpoint checkpoint;
@@ -352,7 +332,6 @@
 TEST(ReadableStreamBytesConsumerTest, EnqueueString) {
   V8TestingScope scope;
   ScriptState* script_state = scope.GetScriptState();
-  ExceptionState& exception_state = scope.GetExceptionState();
 
   auto* underlying_source =
       MakeGarbageCollected<TestUnderlyingSource>(script_state);
@@ -362,11 +341,9 @@
       ScriptValue(script_state, V8String(script_state->GetIsolate(), "hello")));
   underlying_source->Close();
 
-  ScriptValue reader = stream->getReader(script_state, exception_state);
-  ASSERT_FALSE(reader.IsEmpty());
-  ASSERT_FALSE(exception_state.HadException());
   Persistent<BytesConsumer> consumer =
-      MakeGarbageCollected<ReadableStreamBytesConsumer>(script_state, reader);
+      MakeGarbageCollected<ReadableStreamBytesConsumer>(script_state, stream,
+                                                        ASSERT_NO_EXCEPTION);
   Persistent<MockClient> client = MockClient::Create();
   consumer->SetClient(client);
   Checkpoint checkpoint;
diff --git a/third_party/blink/renderer/core/frame/frame_serializer_test.cc b/third_party/blink/renderer/core/frame/frame_serializer_test.cc
index f97d0643..8a513ed 100644
--- a/third_party/blink/renderer/core/frame/frame_serializer_test.cc
+++ b/third_party/blink/renderer/core/frame/frame_serializer_test.cc
@@ -97,7 +97,7 @@
 
     WebURLResponse response;
     response.SetMIMEType("text/html");
-    response.SetHTTPStatusCode(status_code);
+    response.SetHttpStatusCode(status_code);
 
     platform_->GetURLLoaderMockFactory()->RegisterErrorURL(
         KURL(base_url_, file), response, error);
diff --git a/third_party/blink/renderer/core/frame/frame_view.h b/third_party/blink/renderer/core/frame/frame_view.h
index 5b70233..c23ce6bd 100644
--- a/third_party/blink/renderer/core/frame/frame_view.h
+++ b/third_party/blink/renderer/core/frame/frame_view.h
@@ -15,7 +15,11 @@
 class CORE_EXPORT FrameView : public EmbeddedContentView {
  public:
   ~FrameView() override = default;
-  virtual void UpdateViewportIntersectionsForSubtree() = 0;
+
+  // parent_flags is the result of calling GetIntersectionObservationFlags on
+  // the LocalFrameView parent of this FrameView (if any). It contains dirty
+  // bits based on whether geometry may have changed in the parent frame.
+  virtual void UpdateViewportIntersectionsForSubtree(unsigned parent_flags) = 0;
 
   virtual bool GetIntrinsicSizingInfo(IntrinsicSizingInfo&) const = 0;
   virtual bool HasIntrinsicSizingInfo() const = 0;
diff --git a/third_party/blink/renderer/core/frame/local_frame_test.cc b/third_party/blink/renderer/core/frame/local_frame_test.cc
index c1aafd6..a3ddc968 100644
--- a/third_party/blink/renderer/core/frame/local_frame_test.cc
+++ b/third_party/blink/renderer/core/frame/local_frame_test.cc
@@ -178,7 +178,7 @@
 }
 
 TEST_F(LocalFrameTest, IsLazyLoadingImageAllowedWithFeatureDisabled) {
-  ScopedLazyFrameLoadingForTest scoped_lazy_frame_loading_for_test(false);
+  ScopedLazyImageLoadingForTest scoped_lazy_image_loading_for_test(false);
   std::unique_ptr<DummyPageHolder> page_holder = DummyPageHolder::Create(
       IntSize(800, 600), nullptr, nullptr, &DisableDataSaverHoldbackInSettings);
   EXPECT_FALSE(page_holder->GetFrame().IsLazyLoadingImageAllowed());
diff --git a/third_party/blink/renderer/core/frame/local_frame_view.cc b/third_party/blink/renderer/core/frame/local_frame_view.cc
index 3b45fc3f..cd7d73a 100644
--- a/third_party/blink/renderer/core/frame/local_frame_view.cc
+++ b/third_party/blink/renderer/core/frame/local_frame_view.cc
@@ -45,7 +45,6 @@
 #include "third_party/blink/renderer/core/animation/document_animations.h"
 #include "third_party/blink/renderer/core/css/font_face_set_document.h"
 #include "third_party/blink/renderer/core/css/style_change_reason.h"
-#include "third_party/blink/renderer/core/dom/element_visibility_observer.h"
 #include "third_party/blink/renderer/core/dom/static_node_list.h"
 #include "third_party/blink/renderer/core/editing/compute_layer_selection.h"
 #include "third_party/blink/renderer/core/editing/drag_caret.h"
@@ -82,6 +81,7 @@
 #include "third_party/blink/renderer/core/input/event_handler.h"
 #include "third_party/blink/renderer/core/inspector/inspector_trace_events.h"
 #include "third_party/blink/renderer/core/intersection_observer/intersection_observation.h"
+#include "third_party/blink/renderer/core/intersection_observer/intersection_observer.h"
 #include "third_party/blink/renderer/core/intersection_observer/intersection_observer_controller.h"
 #include "third_party/blink/renderer/core/intersection_observer/intersection_observer_init.h"
 #include "third_party/blink/renderer/core/layout/adjust_for_absolute_zoom.h"
@@ -376,6 +376,13 @@
   }
 }
 
+void LocalFrameView::OnViewportIntersectionChanged(
+    const HeapVector<Member<IntersectionObserverEntry>>& entries) {
+  bool is_visible = entries.back()->intersectionRatio() > 0;
+  UpdateVisibility(is_visible);
+  UpdateRenderThrottlingStatus(!is_visible, subtree_throttled_);
+}
+
 void LocalFrameView::SetupRenderThrottling() {
   if (visibility_observer_)
     return;
@@ -388,17 +395,12 @@
   if (!target_element)
     return;
 
-  visibility_observer_ = MakeGarbageCollected<ElementVisibilityObserver>(
-      target_element, WTF::BindRepeating(
-                          [](LocalFrameView* frame_view, bool is_visible) {
-                            if (!frame_view)
-                              return;
-                            frame_view->UpdateVisibility(is_visible);
-                            frame_view->UpdateRenderThrottlingStatus(
-                                !is_visible, frame_view->subtree_throttled_);
-                          },
-                          WrapWeakPersistent(this)));
-  visibility_observer_->Start();
+  visibility_observer_ = IntersectionObserver::Create(
+      {}, {IntersectionObserver::kMinimumThreshold},
+      &target_element->GetDocument(),
+      WTF::BindRepeating(&LocalFrameView::OnViewportIntersectionChanged,
+                         WrapWeakPersistent(this)));
+  visibility_observer_->observe(target_element);
 }
 
 void LocalFrameView::Dispose() {
@@ -1068,10 +1070,11 @@
                "LocalFrameView::UpdateViewportIntersectionsForSubtree");
   SCOPED_UMA_AND_UKM_TIMER(EnsureUkmAggregator(),
                            LocalFrameUkmAggregator::kIntersectionObservation);
-  UpdateViewportIntersectionsForSubtree();
+  UpdateViewportIntersectionsForSubtree(0);
 #if DCHECK_IS_ON()
   DCHECK(was_dirty || !NeedsLayout());
 #endif
+  DeliverSynchronousIntersectionObservations();
 }
 
 LayoutSVGRoot* LocalFrameView::EmbeddedReplacedContent() const {
@@ -3661,6 +3664,9 @@
     const CullRect& cull_rect) {
   DCHECK(PaintOutsideOfLifecycleIsAllowed(context, *this));
 
+  base::AutoReset<bool> past_layout_lifecycle_resetter(
+      &past_layout_lifecycle_update_, true);
+
   ForAllNonThrottledLocalFrameViews([](LocalFrameView& frame_view) {
     frame_view.Lifecycle().AdvanceTo(DocumentLifecycle::kInPaint);
   });
@@ -3678,6 +3684,9 @@
     const CullRect& cull_rect) {
   DCHECK(PaintOutsideOfLifecycleIsAllowed(context, *this));
 
+  base::AutoReset<bool> past_layout_lifecycle_resetter(
+      &past_layout_lifecycle_update_, true);
+
   ForAllNonThrottledLocalFrameViews([](LocalFrameView& frame_view) {
     frame_view.Lifecycle().AdvanceTo(DocumentLifecycle::kInPaint);
   });
@@ -3869,7 +3878,8 @@
     CollectAnnotatedRegions(*curr, regions);
 }
 
-void LocalFrameView::UpdateViewportIntersectionsForSubtree() {
+void LocalFrameView::UpdateViewportIntersectionsForSubtree(
+    unsigned parent_flags) {
   // TODO(dcheng): Since LocalFrameView tree updates are deferred, FrameViews
   // might still be in the LocalFrameView hierarchy even though the associated
   // Document is already detached. Investigate if this check and a similar check
@@ -3878,28 +3888,42 @@
   if (!GetFrame().GetDocument()->IsActive())
     return;
 
+  unsigned flags = GetIntersectionObservationFlags(parent_flags);
+
   if (!NeedsLayout()) {
     // Notify javascript IntersectionObservers
     if (GetFrame().GetDocument()->GetIntersectionObserverController()) {
       GetFrame()
           .GetDocument()
           ->GetIntersectionObserverController()
-          ->ComputeTrackedIntersectionObservations();
+          ->ComputeTrackedIntersectionObservations(flags);
     }
+    intersection_observation_state_ = kNotNeeded;
   }
 
   for (Frame* child = frame_->Tree().FirstChild(); child;
        child = child->Tree().NextSibling()) {
-    child->View()->UpdateViewportIntersectionsForSubtree();
+    child->View()->UpdateViewportIntersectionsForSubtree(flags);
   }
 
   for (HTMLPortalElement* portal :
        DocumentPortals::From(*frame_->GetDocument()).GetPortals()) {
-    if (portal->ContentFrame())
-      portal->ContentFrame()->View()->UpdateViewportIntersectionsForSubtree();
+    if (portal->ContentFrame()) {
+      portal->ContentFrame()->View()->UpdateViewportIntersectionsForSubtree(
+          flags);
+    }
   }
+}
 
-  intersection_observation_state_ = kNotNeeded;
+void LocalFrameView::DeliverSynchronousIntersectionObservations() {
+  if (IntersectionObserverController* controller =
+          GetFrame().GetDocument()->GetIntersectionObserverController()) {
+    controller->DeliverIntersectionObservations(
+        IntersectionObserver::kDeliverDuringPostLifecycleSteps);
+  }
+  ForAllChildLocalFrameViews([](LocalFrameView& frame_view) {
+    frame_view.DeliverSynchronousIntersectionObservations();
+  });
 }
 
 void LocalFrameView::UpdateThrottlingStatusForSubtree() {
@@ -3922,10 +3946,6 @@
   });
 }
 
-void LocalFrameView::UpdateRenderThrottlingStatusForTesting() {
-  visibility_observer_->DeliverObservationsForTesting();
-}
-
 void LocalFrameView::CrossOriginStatusChanged() {
   // Cross-domain status is not stored as a dirty bit within LocalFrameView,
   // so force-invalidate throttling status when it changes regardless of
@@ -4044,7 +4064,8 @@
     root->paint_artifact_compositor_->SetNeedsUpdate();
 }
 
-unsigned LocalFrameView::GetIntersectionObservationFlags() const {
+unsigned LocalFrameView::GetIntersectionObservationFlags(
+    unsigned parent_flags) const {
   unsigned flags = 0;
 
   const LocalFrame& target_frame = GetFrame();
@@ -4057,21 +4078,15 @@
 
   // Observers with explicit roots only need to be checked on the same frame,
   // since in this case target and root must be in the same document.
-  if (intersection_observation_state_ != kNotNeeded)
-    flags |= IntersectionObservation::kExplicitRootObserversNeedUpdate;
+  if (intersection_observation_state_ != kNotNeeded) {
+    flags |= (IntersectionObservation::kExplicitRootObserversNeedUpdate |
+              IntersectionObservation::kImplicitRootObserversNeedUpdate);
+  }
 
   // For observers with implicit roots, we need to check state on the whole
-  // local frame tree.
-  const LocalFrameView* local_root_view = target_frame.LocalFrameRoot().View();
-  for (const LocalFrameView* view = this; view;
-       view = view->ParentFrameView()) {
-    if (view->intersection_observation_state_ != kNotNeeded) {
-      flags |= IntersectionObservation::kImplicitRootObserversNeedUpdate;
-      break;
-    }
-    if (view == local_root_view)
-      break;
-  }
+  // local frame tree, as passed down from the parent.
+  flags |= (parent_flags &
+            IntersectionObservation::kImplicitRootObserversNeedUpdate);
 
   return flags;
 }
@@ -4140,18 +4155,13 @@
           blink::features::kAvoidFlashBetweenNavigation) &&
       document && document->Url().ProtocolIsInHTTPFamily() &&
       document->IsHTMLDocument()) {
-    GetFrame()
-        .GetTaskRunner(TaskType::kInternalDefault)
-        ->PostDelayedTask(FROM_HERE,
-                          WTF::Bind(&LocalFrameView::DeferredCommitsTimerFired,
-                                    WrapWeakPersistent(this)),
-                          GetCommitDelayForAvoidFlashBetweenNavigation());
-    GetFrame().GetPage()->GetChromeClient().StartDeferringCommits();
+    GetFrame().GetPage()->GetChromeClient().StartDeferringCommits(
+        GetCommitDelayForAvoidFlashBetweenNavigation());
   }
   GetFrame().GetPage()->GetChromeClient().BeginLifecycleUpdates();
 }
 
-void LocalFrameView::DeferredCommitsTimerFired() {
+void LocalFrameView::StopDeferringCommits() {
   if (GetFrame().GetPage())
     GetFrame().GetPage()->GetChromeClient().StopDeferringCommits();
 }
diff --git a/third_party/blink/renderer/core/frame/local_frame_view.h b/third_party/blink/renderer/core/frame/local_frame_view.h
index 9b41011b..92c3df1 100644
--- a/third_party/blink/renderer/core/frame/local_frame_view.h
+++ b/third_party/blink/renderer/core/frame/local_frame_view.h
@@ -63,12 +63,13 @@
 class Cursor;
 class DisplayItemClient;
 class DocumentLifecycle;
-class ElementVisibilityObserver;
 class FloatRect;
 class FloatSize;
 class FragmentAnchor;
 class Frame;
 class FrameViewAutoSizeInfo;
+class IntersectionObserver;
+class IntersectionObserverEntry;
 class JSONObject;
 class JankTracker;
 class KURL;
@@ -218,7 +219,7 @@
 
   // Get the InstersectionObservation::ComputeFlags for target elements in this
   // view.
-  unsigned GetIntersectionObservationFlags() const;
+  unsigned GetIntersectionObservationFlags(unsigned parent_flags) const;
 
   void SetPaintArtifactCompositorNeedsUpdate() const;
 
@@ -604,10 +605,6 @@
   bool IsHiddenForThrottling() const { return hidden_for_throttling_; }
   void SetupRenderThrottling();
 
-  // For testing, run pending intersection observer notifications for this
-  // frame.
-  void UpdateRenderThrottlingStatusForTesting();
-
   void BeginLifecycleUpdates();
 
   // Shorthands of LayoutView's corresponding methods.
@@ -778,9 +775,7 @@
   void SetupPrintContext();
   void ClearPrintContext();
 
-  // The callback invoked when the timer triggers to stop deferring commits
-  // on navigation.
-  void DeferredCommitsTimerFired();
+  void StopDeferringCommits();
 
   // Returns whether the lifecycle was succesfully updated to the
   // target state.
@@ -806,6 +801,8 @@
   void PrePaint();
   void PaintTree();
   void UpdateStyleAndLayoutIfNeededRecursive();
+  void OnViewportIntersectionChanged(
+      const HeapVector<Member<IntersectionObserverEntry>>& entries);
 
   void PushPaintArtifactToCompositor(
       CompositorElementIdSet& composited_element_ids);
@@ -867,7 +864,9 @@
   template <typename Function>
   void ForAllNonThrottledLocalFrameViews(const Function&);
 
-  void UpdateViewportIntersectionsForSubtree() override;
+  void UpdateViewportIntersectionsForSubtree(unsigned parent_flags) override;
+  void DeliverSynchronousIntersectionObservations();
+
   void UpdateThrottlingStatusForSubtree();
 
   void NotifyResizeObservers();
@@ -993,7 +992,7 @@
 
   bool needs_focus_on_fragment_;
 
-  Member<ElementVisibilityObserver> visibility_observer_;
+  Member<IntersectionObserver> visibility_observer_;
 
   IntRect remote_viewport_intersection_;
 
diff --git a/third_party/blink/renderer/core/frame/mhtml_loading_test.cc b/third_party/blink/renderer/core/frame/mhtml_loading_test.cc
index 8226270..366c161 100644
--- a/third_party/blink/renderer/core/frame/mhtml_loading_test.cc
+++ b/third_party/blink/renderer/core/frame/mhtml_loading_test.cc
@@ -70,7 +70,7 @@
     params->url = url;
     params->response = WebURLResponse(url);
     params->response.SetMIMEType("multipart/related");
-    params->response.SetHTTPStatusCode(200);
+    params->response.SetHttpStatusCode(200);
     params->response.SetExpectedContentLength(buffer->size());
     auto body_loader = std::make_unique<StaticDataNavigationBodyLoader>();
     body_loader->Write(*buffer);
diff --git a/third_party/blink/renderer/core/frame/remote_frame_view.cc b/third_party/blink/renderer/core/frame/remote_frame_view.cc
index bc61bf9..f60cd4b 100644
--- a/third_party/blink/renderer/core/frame/remote_frame_view.cc
+++ b/third_party/blink/renderer/core/frame/remote_frame_view.cc
@@ -6,12 +6,12 @@
 
 #include "third_party/blink/public/common/frame/frame_owner_element_type.h"
 #include "third_party/blink/renderer/core/dom/document.h"
-#include "third_party/blink/renderer/core/dom/element_visibility_observer.h"
 #include "third_party/blink/renderer/core/frame/local_frame.h"
 #include "third_party/blink/renderer/core/frame/local_frame_view.h"
 #include "third_party/blink/renderer/core/frame/remote_frame.h"
 #include "third_party/blink/renderer/core/frame/remote_frame_client.h"
 #include "third_party/blink/renderer/core/html/html_frame_owner_element.h"
+#include "third_party/blink/renderer/core/intersection_observer/intersection_observer.h"
 #include "third_party/blink/renderer/core/intersection_observer/intersection_observer_entry.h"
 #include "third_party/blink/renderer/core/layout/layout_embedded_content.h"
 #include "third_party/blink/renderer/core/layout/layout_view.h"
@@ -83,7 +83,13 @@
   return view;
 }
 
-void RemoteFrameView::UpdateViewportIntersectionsForSubtree() {
+void RemoteFrameView::UpdateViewportIntersectionsForSubtree(
+    unsigned parent_flags) {
+  if (!(parent_flags &
+        IntersectionObservation::kImplicitRootObserversNeedUpdate)) {
+    return;
+  }
+
   LayoutEmbeddedContent* owner = remote_frame_->OwnerLayoutObject();
   if (!owner)
     return;
@@ -335,6 +341,13 @@
   remote_frame_->Client()->VisibilityChanged(visibility);
 }
 
+void RemoteFrameView::OnViewportIntersectionChanged(
+    const HeapVector<Member<IntersectionObserverEntry>>& entries) {
+  bool is_visible = entries.back()->intersectionRatio() > 0;
+  UpdateVisibility(is_visible);
+  UpdateRenderThrottlingStatus(!is_visible, subtree_throttled_);
+}
+
 void RemoteFrameView::SetupRenderThrottling() {
   if (visibility_observer_)
     return;
@@ -343,15 +356,12 @@
   if (!target_element)
     return;
 
-  visibility_observer_ = MakeGarbageCollected<ElementVisibilityObserver>(
-      target_element, WTF::BindRepeating(
-                          [](RemoteFrameView* remote_view, bool is_visible) {
-                            remote_view->UpdateVisibility(is_visible);
-                            remote_view->UpdateRenderThrottlingStatus(
-                                !is_visible, remote_view->subtree_throttled_);
-                          },
-                          WrapWeakPersistent(this)));
-  visibility_observer_->Start();
+  visibility_observer_ = IntersectionObserver::Create(
+      {}, {IntersectionObserver::kMinimumThreshold},
+      &target_element->GetDocument(),
+      WTF::BindRepeating(&RemoteFrameView::OnViewportIntersectionChanged,
+                         WrapWeakPersistent(this)));
+  visibility_observer_->observe(target_element);
 }
 
 void RemoteFrameView::UpdateRenderThrottlingStatus(bool hidden,
diff --git a/third_party/blink/renderer/core/frame/remote_frame_view.h b/third_party/blink/renderer/core/frame/remote_frame_view.h
index d80aecf..ae5db5a 100644
--- a/third_party/blink/renderer/core/frame/remote_frame_view.h
+++ b/third_party/blink/renderer/core/frame/remote_frame_view.h
@@ -19,8 +19,9 @@
 
 namespace blink {
 class CullRect;
-class ElementVisibilityObserver;
 class GraphicsContext;
+class IntersectionObserver;
+class IntersectionObserverEntry;
 class LocalFrameView;
 class RemoteFrame;
 
@@ -58,7 +59,7 @@
   void Show() override;
   void SetParentVisible(bool) override;
 
-  void UpdateViewportIntersectionsForSubtree() override;
+  void UpdateViewportIntersectionsForSubtree(unsigned parent_flags) override;
 
   bool GetIntrinsicSizingInfo(IntrinsicSizingInfo&) const override;
 
@@ -83,6 +84,8 @@
   // owner.
   LocalFrameView* ParentLocalRootFrameView() const;
 
+  void OnViewportIntersectionChanged(
+      const HeapVector<Member<IntersectionObserverEntry>>& entries);
   void UpdateRenderThrottlingStatus(bool hidden, bool subtree_throttled);
   bool CanThrottleRendering() const;
   void SetupRenderThrottling();
@@ -103,7 +106,7 @@
   blink::mojom::FrameVisibility visibility_ =
       blink::mojom::FrameVisibility::kRenderedInViewport;
 
-  Member<ElementVisibilityObserver> visibility_observer_;
+  Member<IntersectionObserver> visibility_observer_;
   bool subtree_throttled_ = false;
   bool hidden_for_throttling_ = false;
   IntrinsicSizingInfo intrinsic_sizing_info_;
diff --git a/third_party/blink/renderer/core/frame/web_local_frame_impl.cc b/third_party/blink/renderer/core/frame/web_local_frame_impl.cc
index f9a24aac..6f19b08f 100644
--- a/third_party/blink/renderer/core/frame/web_local_frame_impl.cc
+++ b/third_party/blink/renderer/core/frame/web_local_frame_impl.cc
@@ -736,38 +736,9 @@
 }
 
 void WebLocalFrameImpl::AddMessageToConsole(const WebConsoleMessage& message) {
-  // TODO(devlin): Use WebConsoleMessage::LogWebConsoleMessage here.
-
   DCHECK(GetFrame());
-
-  MessageLevel web_core_message_level = kInfoMessageLevel;
-  switch (message.level) {
-    case mojom::ConsoleMessageLevel::kVerbose:
-      web_core_message_level = kVerboseMessageLevel;
-      break;
-    case mojom::ConsoleMessageLevel::kInfo:
-      web_core_message_level = kInfoMessageLevel;
-      break;
-    case mojom::ConsoleMessageLevel::kWarning:
-      web_core_message_level = kWarningMessageLevel;
-      break;
-    case mojom::ConsoleMessageLevel::kError:
-      web_core_message_level = kErrorMessageLevel;
-      break;
-  }
-
-  MessageSource message_source = message.nodes.empty()
-                                     ? kOtherMessageSource
-                                     : kRecommendationMessageSource;
-  Vector<DOMNodeId> nodes;
-  for (const blink::WebNode& web_node : message.nodes)
-    nodes.push_back(DOMNodeIds::IdForNode(&(*web_node)));
-  ConsoleMessage* console_message = ConsoleMessage::Create(
-      message_source, web_core_message_level, message.text,
-      SourceLocation::Create(message.url, message.line_number,
-                             message.column_number, nullptr));
-  console_message->SetNodes(GetFrame(), std::move(nodes));
-  GetFrame()->GetDocument()->AddConsoleMessage(console_message);
+  GetFrame()->GetDocument()->AddConsoleMessage(
+      ConsoleMessage::CreateFromWebConsoleMessage(message, GetFrame()));
 }
 
 void WebLocalFrameImpl::Alert(const WebString& message) {
diff --git a/third_party/blink/renderer/core/html/forms/internal_popup_menu.cc b/third_party/blink/renderer/core/html/forms/internal_popup_menu.cc
index 2e9105d..a89f2e91 100644
--- a/third_party/blink/renderer/core/html/forms/internal_popup_menu.cc
+++ b/third_party/blink/renderer/core/html/forms/internal_popup_menu.cc
@@ -231,6 +231,10 @@
 
 void InternalPopupMenu::WriteDocument(SharedBuffer* data) {
   HTMLSelectElement& owner_element = *owner_element_;
+  // When writing the document, we ensure the ComputedStyle of the select
+  // element's items (see AddElementStyle). This requires a style-clean tree.
+  // See Element::EnsureComputedStyle for further explanation.
+  owner_element.GetDocument().UpdateStyleAndLayoutTree();
   IntRect anchor_rect_in_screen = chrome_client_->ViewportToScreen(
       owner_element.VisibleBoundsInVisualViewport(),
       owner_element.GetDocument().View());
diff --git a/third_party/blink/renderer/core/html/forms/internal_popup_menu_test.cc b/third_party/blink/renderer/core/html/forms/internal_popup_menu_test.cc
new file mode 100644
index 0000000..078c828
--- /dev/null
+++ b/third_party/blink/renderer/core/html/forms/internal_popup_menu_test.cc
@@ -0,0 +1,50 @@
+// 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 "third_party/blink/renderer/core/html/forms/internal_popup_menu.h"
+
+#include "build/build_config.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/renderer/core/dom/document.h"
+#include "third_party/blink/renderer/core/dom/element.h"
+#include "third_party/blink/renderer/core/html/forms/html_select_element.h"
+#include "third_party/blink/renderer/core/loader/empty_clients.h"
+#include "third_party/blink/renderer/core/testing/dummy_page_holder.h"
+#include "third_party/blink/renderer/platform/shared_buffer.h"
+
+namespace blink {
+
+// InternalPopupMenuTest is not used on Android, and its Platform implementation
+// does not provide the resources (as in GetDataResource) needed by
+// InternalPopupMenu::WriteDocument.
+#if !defined(OS_ANDROID)
+
+TEST(InternalPopupMenuTest, WriteDocumentInStyleDirtyTree) {
+  auto dummy_page_holder_ = DummyPageHolder::Create(IntSize(800, 600));
+  Document& document = dummy_page_holder_->GetDocument();
+  document.body()->SetInnerHTMLFromString(R"HTML(
+    <select id="select">
+        <option value="foo">Foo</option>
+        <option value="bar" style="display:none">Bar</option>
+    </select>
+  )HTML");
+  document.View()->UpdateAllLifecyclePhases(
+      DocumentLifecycle::LifecycleUpdateReason::kTest);
+  HTMLSelectElement* select =
+      ToHTMLSelectElement(document.getElementById("select"));
+  ASSERT_TRUE(select);
+  InternalPopupMenu* menu =
+      InternalPopupMenu::Create(EmptyChromeClient::Create(), *select);
+
+  document.body()->SetInlineStyleProperty(CSSPropertyColor, "blue");
+
+  scoped_refptr<SharedBuffer> buffer = SharedBuffer::Create();
+
+  // Don't DCHECK in Element::EnsureComputedStyle.
+  static_cast<PagePopupClient*>(menu)->WriteDocument(buffer.get());
+}
+
+#endif  // defined(OS_ANDROID)
+
+}  // namespace blink
diff --git a/third_party/blink/renderer/core/html/lazy_load_frame_observer.cc b/third_party/blink/renderer/core/html/lazy_load_frame_observer.cc
index efd3975..5039504e 100644
--- a/third_party/blink/renderer/core/html/lazy_load_frame_observer.cc
+++ b/third_party/blink/renderer/core/html/lazy_load_frame_observer.cc
@@ -38,17 +38,17 @@
 // could break their functionality, so these heuristics are used to recognize
 // likely hidden frames and immediately load them so that they can function
 // properly.
-bool IsFrameProbablyHidden(const DOMRectReadOnly& bounding_client_rect,
+bool IsFrameProbablyHidden(const LayoutRect& bounding_client_rect,
                            const Element& element) {
   // Tiny frames that are 4x4 or smaller are likely not intended to be seen by
   // the user. Note that this condition includes frames marked as
   // "display:none", since those frames would have dimensions of 0x0.
-  if (bounding_client_rect.width() < 4.1 || bounding_client_rect.height() < 4.1)
+  if (bounding_client_rect.Width() < 4.1 || bounding_client_rect.Height() < 4.1)
     return true;
 
   // Frames that are positioned completely off the page above or to the left are
   // likely never intended to be visible to the user.
-  if (bounding_client_rect.right() < 0.0 || bounding_client_rect.bottom() < 0.0)
+  if (bounding_client_rect.MaxX() < 0.0 || bounding_client_rect.MaxY() < 0.0)
     return true;
 
   const ComputedStyle* style = element.GetComputedStyle();
@@ -142,7 +142,7 @@
   if (entries.back()->isIntersecting()) {
     RecordInitialDeferralAction(
         FrameInitialDeferralAction::kLoadedNearOrInViewport);
-  } else if (IsFrameProbablyHidden(*entries.back()->boundingClientRect(),
+  } else if (IsFrameProbablyHidden(entries.back()->GetGeometry().TargetRect(),
                                    *element_)) {
     RecordInitialDeferralAction(FrameInitialDeferralAction::kLoadedHidden);
   } else {
@@ -206,7 +206,8 @@
   DCHECK(!entries.IsEmpty());
   DCHECK_EQ(element_, entries.back()->target());
 
-  if (IsFrameProbablyHidden(*entries.back()->boundingClientRect(), *element_)) {
+  if (IsFrameProbablyHidden(entries.back()->GetGeometry().TargetRect(),
+                            *element_)) {
     visibility_metrics_observer_->disconnect();
     visibility_metrics_observer_.Clear();
     return;
diff --git a/third_party/blink/renderer/core/html/media/autoplay_policy.cc b/third_party/blink/renderer/core/html/media/autoplay_policy.cc
index 4887fa0..af5cb42 100644
--- a/third_party/blink/renderer/core/html/media/autoplay_policy.cc
+++ b/third_party/blink/renderer/core/html/media/autoplay_policy.cc
@@ -14,12 +14,13 @@
 #include "third_party/blink/public/web/web_settings.h"
 #include "third_party/blink/public/web/web_user_media_client.h"
 #include "third_party/blink/renderer/core/dom/document.h"
-#include "third_party/blink/renderer/core/dom/element_visibility_observer.h"
 #include "third_party/blink/renderer/core/frame/local_frame.h"
 #include "third_party/blink/renderer/core/frame/settings.h"
 #include "third_party/blink/renderer/core/html/media/autoplay_uma_helper.h"
 #include "third_party/blink/renderer/core/html/media/html_media_element.h"
 #include "third_party/blink/renderer/core/inspector/console_message.h"
+#include "third_party/blink/renderer/core/intersection_observer/intersection_observer.h"
+#include "third_party/blink/renderer/core/intersection_observer/intersection_observer_entry.h"
 #include "third_party/blink/renderer/core/page/page.h"
 #include "third_party/blink/renderer/platform/network/network_state_notifier.h"
 #include "third_party/blink/renderer/platform/runtime_enabled_features.h"
@@ -195,23 +196,22 @@
   // We might end up in a situation where the previous
   // observer didn't had time to fire yet. We can avoid
   // creating a new one in this case.
-  if (autoplay_visibility_observer_)
+  if (autoplay_intersection_observer_)
     return;
 
-  autoplay_visibility_observer_ =
-      MakeGarbageCollected<ElementVisibilityObserver>(
-          element_,
-          WTF::BindRepeating(&AutoplayPolicy::OnVisibilityChangedForAutoplay,
-                             WrapWeakPersistent(this)));
-  autoplay_visibility_observer_->Start();
+  autoplay_intersection_observer_ = IntersectionObserver::Create(
+      {}, {IntersectionObserver::kMinimumThreshold}, &element_->GetDocument(),
+      WTF::BindRepeating(&AutoplayPolicy::OnIntersectionChangedForAutoplay,
+                         WrapWeakPersistent(this)));
+  autoplay_intersection_observer_->observe(element_);
 }
 
 void AutoplayPolicy::StopAutoplayMutedWhenVisible() {
-  if (!autoplay_visibility_observer_)
+  if (!autoplay_intersection_observer_)
     return;
 
-  autoplay_visibility_observer_->Stop();
-  autoplay_visibility_observer_ = nullptr;
+  autoplay_intersection_observer_->disconnect();
+  autoplay_intersection_observer_ = nullptr;
 }
 
 bool AutoplayPolicy::RequestAutoplayUnmute() {
@@ -339,7 +339,9 @@
   autoplay_initiated_ = false;
 }
 
-void AutoplayPolicy::OnVisibilityChangedForAutoplay(bool is_visible) {
+void AutoplayPolicy::OnIntersectionChangedForAutoplay(
+    const HeapVector<Member<IntersectionObserverEntry>>& entries) {
+  bool is_visible = (entries.back()->intersectionRatio() > 0);
   if (!is_visible) {
     if (element_->can_autoplay_ && element_->Autoplay()) {
       element_->PauseInternal();
@@ -401,7 +403,7 @@
 
 void AutoplayPolicy::Trace(Visitor* visitor) {
   visitor->Trace(element_);
-  visitor->Trace(autoplay_visibility_observer_);
+  visitor->Trace(autoplay_intersection_observer_);
   visitor->Trace(autoplay_uma_helper_);
 }
 
diff --git a/third_party/blink/renderer/core/html/media/autoplay_policy.h b/third_party/blink/renderer/core/html/media/autoplay_policy.h
index 7a1af1ce..f4bc1d7 100644
--- a/third_party/blink/renderer/core/html/media/autoplay_policy.h
+++ b/third_party/blink/renderer/core/html/media/autoplay_policy.h
@@ -14,8 +14,9 @@
 
 class AutoplayUmaHelper;
 class Document;
-class ElementVisibilityObserver;
 class HTMLMediaElement;
+class IntersectionObserver;
+class IntersectionObserverEntry;
 
 // AutoplayPolicy is the class for handles autoplay logics.
 class CORE_EXPORT AutoplayPolicy final
@@ -139,7 +140,8 @@
 
   // Called when the video visibility changes while autoplaying muted, will
   // pause the video when invisible and resume the video when visible.
-  void OnVisibilityChangedForAutoplay(bool is_visible);
+  void OnIntersectionChangedForAutoplay(
+      const HeapVector<Member<IntersectionObserverEntry>>& entries);
 
   // Returns whether the current autoplay policy is
   // kDocumentUserActivationRequired. This is a helper method for readability.
@@ -151,7 +153,7 @@
   bool locked_pending_user_gesture_ : 1;
 
   Member<HTMLMediaElement> element_ = nullptr;
-  Member<ElementVisibilityObserver> autoplay_visibility_observer_ = nullptr;
+  Member<IntersectionObserver> autoplay_intersection_observer_ = nullptr;
 
   Member<AutoplayUmaHelper> autoplay_uma_helper_;
 
diff --git a/third_party/blink/renderer/core/html/media/autoplay_uma_helper.cc b/third_party/blink/renderer/core/html/media/autoplay_uma_helper.cc
index 86af101..8ab2e83 100644
--- a/third_party/blink/renderer/core/html/media/autoplay_uma_helper.cc
+++ b/third_party/blink/renderer/core/html/media/autoplay_uma_helper.cc
@@ -9,13 +9,14 @@
 #include "third_party/blink/public/platform/interface_provider.h"
 #include "third_party/blink/public/platform/platform.h"
 #include "third_party/blink/renderer/core/dom/document.h"
-#include "third_party/blink/renderer/core/dom/element_visibility_observer.h"
 #include "third_party/blink/renderer/core/dom/events/event.h"
 #include "third_party/blink/renderer/core/frame/local_frame.h"
 #include "third_party/blink/renderer/core/frame/settings.h"
 #include "third_party/blink/renderer/core/frame/use_counter.h"
 #include "third_party/blink/renderer/core/html/media/autoplay_policy.h"
 #include "third_party/blink/renderer/core/html/media/html_media_element.h"
+#include "third_party/blink/renderer/core/intersection_observer/intersection_observer.h"
+#include "third_party/blink/renderer/core/intersection_observer/intersection_observer_entry.h"
 #include "third_party/blink/renderer/core/page/page.h"
 #include "third_party/blink/renderer/platform/histogram.h"
 #include "third_party/blink/renderer/platform/network/network_state_notifier.h"
@@ -58,9 +59,9 @@
 AutoplayUmaHelper::AutoplayUmaHelper(HTMLMediaElement* element)
     : ContextLifecycleObserver(nullptr),
       element_(element),
-      muted_video_play_method_visibility_observer_(nullptr),
+      muted_video_play_method_intersection_observer_(nullptr),
       is_visible_(false),
-      muted_video_offscreen_duration_visibility_observer_(nullptr) {
+      muted_video_offscreen_duration_intersection_observer_(nullptr) {
   element->addEventListener(event_type_names::kLoadstart, this, false);
 }
 
@@ -237,16 +238,19 @@
   SetContext(&element_->GetDocument());
 }
 
-void AutoplayUmaHelper::OnVisibilityChangedForMutedVideoPlayMethodBecomeVisible(
-    bool is_visible) {
-  if (!is_visible || !muted_video_play_method_visibility_observer_)
+void AutoplayUmaHelper::
+    OnIntersectionChangedForMutedVideoPlayMethodBecomeVisible(
+        const HeapVector<Member<IntersectionObserverEntry>>& entries) {
+  bool is_visible = (entries.back()->intersectionRatio() > 0);
+  if (!is_visible || !muted_video_play_method_intersection_observer_)
     return;
 
   MaybeStopRecordingMutedVideoPlayMethodBecomeVisible(true);
 }
 
-void AutoplayUmaHelper::OnVisibilityChangedForMutedVideoOffscreenDuration(
-    bool is_visible) {
+void AutoplayUmaHelper::OnIntersectionChangedForMutedVideoOffscreenDuration(
+    const HeapVector<Member<IntersectionObserverEntry>>& entries) {
+  bool is_visible = (entries.back()->intersectionRatio() > 0);
   if (is_visible == is_visible_)
     return;
 
@@ -297,28 +301,27 @@
       !element_->IsHTMLVideoElement() || !element_->muted())
     return;
 
-  muted_video_play_method_visibility_observer_ =
-      MakeGarbageCollected<ElementVisibilityObserver>(
-          element_,
-          WTF::BindRepeating(
-              &AutoplayUmaHelper::
-                  OnVisibilityChangedForMutedVideoPlayMethodBecomeVisible,
-              WrapWeakPersistent(this)));
-  muted_video_play_method_visibility_observer_->Start();
+  muted_video_play_method_intersection_observer_ = IntersectionObserver::Create(
+      {}, {IntersectionObserver::kMinimumThreshold}, &element_->GetDocument(),
+      WTF::BindRepeating(
+          &AutoplayUmaHelper::
+              OnIntersectionChangedForMutedVideoPlayMethodBecomeVisible,
+          WrapWeakPersistent(this)));
+  muted_video_play_method_intersection_observer_->observe(element_);
   SetContext(&element_->GetDocument());
 }
 
 void AutoplayUmaHelper::MaybeStopRecordingMutedVideoPlayMethodBecomeVisible(
     bool visible) {
-  if (!muted_video_play_method_visibility_observer_)
+  if (!muted_video_play_method_intersection_observer_)
     return;
 
   DEFINE_STATIC_LOCAL(BooleanHistogram, histogram,
                       ("Media.Video.Autoplay.Muted.PlayMethod.BecomesVisible"));
 
   histogram.Count(visible);
-  muted_video_play_method_visibility_observer_->Stop();
-  muted_video_play_method_visibility_observer_ = nullptr;
+  muted_video_play_method_intersection_observer_->disconnect();
+  muted_video_play_method_intersection_observer_ = nullptr;
   MaybeUnregisterContextDestroyedObserver();
 }
 
@@ -330,19 +333,21 @@
   // Start recording muted video playing offscreen duration.
   muted_video_autoplay_offscreen_start_time_ = CurrentTimeTicks();
   is_visible_ = false;
-  muted_video_offscreen_duration_visibility_observer_ = MakeGarbageCollected<
-      ElementVisibilityObserver>(
-      element_,
-      WTF::BindRepeating(
-          &AutoplayUmaHelper::OnVisibilityChangedForMutedVideoOffscreenDuration,
-          WrapWeakPersistent(this)));
-  muted_video_offscreen_duration_visibility_observer_->Start();
+  muted_video_offscreen_duration_intersection_observer_ =
+      IntersectionObserver::Create(
+          {}, {IntersectionObserver::kMinimumThreshold},
+          &element_->GetDocument(),
+          WTF::BindRepeating(
+              &AutoplayUmaHelper::
+                  OnIntersectionChangedForMutedVideoOffscreenDuration,
+              WrapWeakPersistent(this)));
+  muted_video_offscreen_duration_intersection_observer_->observe(element_);
   element_->addEventListener(event_type_names::kPause, this, false);
   SetContext(&element_->GetDocument());
 }
 
 void AutoplayUmaHelper::MaybeStopRecordingMutedVideoOffscreenDuration() {
-  if (!muted_video_offscreen_duration_visibility_observer_)
+  if (!muted_video_offscreen_duration_intersection_observer_)
     return;
 
   if (!is_visible_) {
@@ -357,8 +362,8 @@
       muted_video_autoplay_offscreen_duration_, TimeDelta::FromMilliseconds(1),
       kMaxOffscreenDurationUma, kOffscreenDurationUmaBucketCount);
 
-  muted_video_offscreen_duration_visibility_observer_->Stop();
-  muted_video_offscreen_duration_visibility_observer_ = nullptr;
+  muted_video_offscreen_duration_intersection_observer_->disconnect();
+  muted_video_offscreen_duration_intersection_observer_ = nullptr;
   muted_video_autoplay_offscreen_duration_ = TimeDelta();
   MaybeUnregisterMediaElementPauseListener();
   MaybeUnregisterContextDestroyedObserver();
@@ -371,22 +376,22 @@
 }
 
 void AutoplayUmaHelper::MaybeUnregisterMediaElementPauseListener() {
-  if (muted_video_offscreen_duration_visibility_observer_)
+  if (muted_video_offscreen_duration_intersection_observer_)
     return;
   element_->removeEventListener(event_type_names::kPause, this, false);
 }
 
 bool AutoplayUmaHelper::ShouldListenToContextDestroyed() const {
-  return muted_video_play_method_visibility_observer_ ||
-         muted_video_offscreen_duration_visibility_observer_;
+  return muted_video_play_method_intersection_observer_ ||
+         muted_video_offscreen_duration_intersection_observer_;
 }
 
 void AutoplayUmaHelper::Trace(Visitor* visitor) {
   NativeEventListener::Trace(visitor);
   ContextLifecycleObserver::Trace(visitor);
   visitor->Trace(element_);
-  visitor->Trace(muted_video_play_method_visibility_observer_);
-  visitor->Trace(muted_video_offscreen_duration_visibility_observer_);
+  visitor->Trace(muted_video_play_method_intersection_observer_);
+  visitor->Trace(muted_video_offscreen_duration_intersection_observer_);
 }
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/html/media/autoplay_uma_helper.h b/third_party/blink/renderer/core/html/media/autoplay_uma_helper.h
index 03f9dce..c3eed3a 100644
--- a/third_party/blink/renderer/core/html/media/autoplay_uma_helper.h
+++ b/third_party/blink/renderer/core/html/media/autoplay_uma_helper.h
@@ -47,8 +47,9 @@
 };
 
 class Document;
-class ElementVisibilityObserver;
 class HTMLMediaElement;
+class IntersectionObserver;
+class IntersectionObserverEntry;
 
 class CORE_EXPORT AutoplayUmaHelper : public NativeEventListener,
                                       public ContextLifecycleObserver {
@@ -96,8 +97,10 @@
   void MaybeStartRecordingMutedVideoOffscreenDuration();
   void MaybeStopRecordingMutedVideoOffscreenDuration();
 
-  void OnVisibilityChangedForMutedVideoOffscreenDuration(bool is_visibile);
-  void OnVisibilityChangedForMutedVideoPlayMethodBecomeVisible(bool is_visible);
+  void OnIntersectionChangedForMutedVideoOffscreenDuration(
+      const HeapVector<Member<IntersectionObserverEntry>>& entries);
+  void OnIntersectionChangedForMutedVideoPlayMethodBecomeVisible(
+      const HeapVector<Member<IntersectionObserverEntry>>& entries);
 
   bool ShouldListenToContextDestroyed() const;
 
@@ -110,8 +113,7 @@
   // The observer is used to observe whether a muted video autoplaying by play()
   // method become visible at some point.
   // The UMA is pending for recording as long as this observer is non-null.
-  Member<ElementVisibilityObserver>
-      muted_video_play_method_visibility_observer_;
+  Member<IntersectionObserver> muted_video_play_method_intersection_observer_;
 
   // -----------------------------------------------------------------------
   // Variables used for recording the duration of autoplay muted video playing
@@ -131,8 +133,8 @@
   // The observer is used to observer an autoplaying muted video changing it's
   // visibility, which is used for offscreen duration UMA.  The UMA is pending
   // for recording as long as this observer is non-null.
-  Member<ElementVisibilityObserver>
-      muted_video_offscreen_duration_visibility_observer_;
+  Member<IntersectionObserver>
+      muted_video_offscreen_duration_intersection_observer_;
 
   TimeTicks load_start_time_;
 };
diff --git a/third_party/blink/renderer/core/html/media/html_media_element.cc b/third_party/blink/renderer/core/html/media/html_media_element.cc
index 07966766..4896f2e6 100644
--- a/third_party/blink/renderer/core/html/media/html_media_element.cc
+++ b/third_party/blink/renderer/core/html/media/html_media_element.cc
@@ -52,7 +52,6 @@
 #include "third_party/blink/renderer/core/dom/dom_exception.h"
 #include "third_party/blink/renderer/core/dom/dom_node_ids.h"
 #include "third_party/blink/renderer/core/dom/element_traversal.h"
-#include "third_party/blink/renderer/core/dom/element_visibility_observer.h"
 #include "third_party/blink/renderer/core/dom/events/event.h"
 #include "third_party/blink/renderer/core/dom/events/event_queue.h"
 #include "third_party/blink/renderer/core/dom/shadow_root.h"
@@ -83,6 +82,8 @@
 #include "third_party/blink/renderer/core/html/track/video_track_list.h"
 #include "third_party/blink/renderer/core/html_names.h"
 #include "third_party/blink/renderer/core/inspector/console_message.h"
+#include "third_party/blink/renderer/core/intersection_observer/intersection_observer.h"
+#include "third_party/blink/renderer/core/intersection_observer/intersection_observer_entry.h"
 #include "third_party/blink/renderer/core/layout/layout_media.h"
 #include "third_party/blink/renderer/core/layout/layout_view.h"
 #include "third_party/blink/renderer/core/page/chrome_client.h"
@@ -512,7 +513,7 @@
       remote_playback_client_(nullptr),
       media_controls_(nullptr),
       controls_list_(HTMLMediaElementControlsList::Create(this)),
-      lazy_load_visibility_observer_(nullptr) {
+      lazy_load_intersection_observer_(nullptr) {
   DVLOG(1) << "HTMLMediaElement(" << (void*)this << ")";
 
   LocalFrame* frame = document.GetFrame();
@@ -1860,12 +1861,12 @@
     // element actually becomes visible to complete the load.
     if (IsHTMLVideoElement() && web_media_player_->DidLazyLoad() &&
         !is_potentially_playing) {
-      lazy_load_visibility_observer_ =
-          MakeGarbageCollected<ElementVisibilityObserver>(
-              this, WTF::BindRepeating(
-                        &HTMLMediaElement::OnVisibilityChangedForLazyLoad,
-                        WrapWeakPersistent(this)));
-      lazy_load_visibility_observer_->Start();
+      lazy_load_intersection_observer_ = IntersectionObserver::Create(
+          {}, {IntersectionObserver::kMinimumThreshold}, &GetDocument(),
+          WTF::BindRepeating(
+              &HTMLMediaElement::OnIntersectionChangedForLazyLoad,
+              WrapWeakPersistent(this)));
+      lazy_load_intersection_observer_->observe(this);
     }
   }
 
@@ -2445,9 +2446,9 @@
   DVLOG(3) << "playInternal(" << (void*)this << ")";
 
   // Playback aborts any lazy loading.
-  if (lazy_load_visibility_observer_) {
-    lazy_load_visibility_observer_->Stop();
-    lazy_load_visibility_observer_ = nullptr;
+  if (lazy_load_intersection_observer_) {
+    lazy_load_intersection_observer_->disconnect();
+    lazy_load_intersection_observer_ = nullptr;
   }
 
   // 4.8.12.8. Playing the media resource
@@ -3456,9 +3457,9 @@
 void HTMLMediaElement::StopPeriodicTimers() {
   progress_event_timer_.Stop();
   playback_progress_timer_.Stop();
-  if (lazy_load_visibility_observer_) {
-    lazy_load_visibility_observer_->Stop();
-    lazy_load_visibility_observer_ = nullptr;
+  if (lazy_load_intersection_observer_) {
+    lazy_load_intersection_observer_->disconnect();
+    lazy_load_intersection_observer_ = nullptr;
   }
 }
 
@@ -3915,7 +3916,7 @@
   visitor->Trace(autoplay_policy_);
   visitor->Trace(media_controls_);
   visitor->Trace(controls_list_);
-  visitor->Trace(lazy_load_visibility_observer_);
+  visitor->Trace(lazy_load_intersection_observer_);
   Supplementable<HTMLMediaElement>::Trace(visitor);
   HTMLElement::Trace(visitor);
   ContextLifecycleStateObserver::Trace(visitor);
@@ -4203,13 +4204,15 @@
     web_media_player_->BecameDominantVisibleContent(mostly_filling_viewport_);
 }
 
-void HTMLMediaElement::OnVisibilityChangedForLazyLoad(bool is_visible) {
+void HTMLMediaElement::OnIntersectionChangedForLazyLoad(
+    const HeapVector<Member<IntersectionObserverEntry>>& entries) {
+  bool is_visible = (entries.back()->intersectionRatio() > 0);
   if (!is_visible || !web_media_player_)
     return;
 
   web_media_player_->OnBecameVisible();
-  lazy_load_visibility_observer_->Stop();
-  lazy_load_visibility_observer_ = nullptr;
+  lazy_load_intersection_observer_->disconnect();
+  lazy_load_intersection_observer_ = nullptr;
 }
 
 STATIC_ASSERT_ENUM(WebMediaPlayer::kReadyStateHaveNothing,
diff --git a/third_party/blink/renderer/core/html/media/html_media_element.h b/third_party/blink/renderer/core/html/media/html_media_element.h
index de34bce2..7d3c74fa 100644
--- a/third_party/blink/renderer/core/html/media/html_media_element.h
+++ b/third_party/blink/renderer/core/html/media/html_media_element.h
@@ -59,7 +59,6 @@
 class AutoplayPolicy;
 class ContentType;
 class CueTimeline;
-class ElementVisibilityObserver;
 class EnumerationHistogram;
 class Event;
 class EventQueue;
@@ -68,6 +67,8 @@
 class HTMLMediaSource;
 class HTMLSourceElement;
 class HTMLTrackElement;
+class IntersectionObserver;
+class IntersectionObserverEntry;
 class KURL;
 class MediaError;
 class MediaStreamDescriptor;
@@ -554,7 +555,8 @@
 
   EnumerationHistogram& ShowControlsHistogram() const;
 
-  void OnVisibilityChangedForLazyLoad(bool);
+  void OnIntersectionChangedForLazyLoad(
+      const HeapVector<Member<IntersectionObserverEntry>>& entries);
 
   void OnRemovedFromDocumentTimerFired(TimerBase*);
 
@@ -759,7 +761,7 @@
   Member<MediaControls> media_controls_;
   Member<HTMLMediaElementControlsList> controls_list_;
 
-  Member<ElementVisibilityObserver> lazy_load_visibility_observer_;
+  Member<IntersectionObserver> lazy_load_intersection_observer_;
 };
 
 inline bool IsHTMLMediaElement(const HTMLElement& element) {
diff --git a/third_party/blink/renderer/core/html/media/html_media_element_test.cc b/third_party/blink/renderer/core/html/media/html_media_element_test.cc
index c0f3c7ec..8e1e9c1d1 100644
--- a/third_party/blink/renderer/core/html/media/html_media_element_test.cc
+++ b/third_party/blink/renderer/core/html/media/html_media_element_test.cc
@@ -128,7 +128,7 @@
   }
 
   bool HasLazyLoadObserver() const {
-    return !!Media()->lazy_load_visibility_observer_;
+    return !!Media()->lazy_load_intersection_observer_;
   }
 
   ExecutionContext* GetExecutionContext() const {
diff --git a/third_party/blink/renderer/core/html/media/media_custom_controls_fullscreen_detector.cc b/third_party/blink/renderer/core/html/media/media_custom_controls_fullscreen_detector.cc
index 401fa08..80b809d 100644
--- a/third_party/blink/renderer/core/html/media/media_custom_controls_fullscreen_detector.cc
+++ b/third_party/blink/renderer/core/html/media/media_custom_controls_fullscreen_detector.cc
@@ -147,19 +147,15 @@
   // We only want a single notification, then stop observing.
   viewport_intersection_observer_->disconnect();
 
-  const Member<IntersectionObserverEntry>& last_entry = entries.back();
+  const IntersectionGeometry& geometry = entries.back()->GetGeometry();
 
   // Target and intersection rects must be converted from CSS to device pixels.
   float zoom = VideoElement().GetLayoutObject()->StyleRef().EffectiveZoom();
-  DOMRectReadOnly* target_rect = last_entry->boundingClientRect();
-  FloatSize target_size(target_rect->width(), target_rect->height());
+  LayoutSize target_size = geometry.TargetRect().Size();
   target_size.Scale(zoom);
-  DOMRectReadOnly* intersection_rect = last_entry->intersectionRect();
-  FloatSize intersection_size(intersection_rect->width(),
-                              intersection_rect->height());
+  LayoutSize intersection_size = geometry.IntersectionRect().Size();
   intersection_size.Scale(zoom);
-  DOMRectReadOnly* root_rect = last_entry->rootBounds();
-  FloatSize root_size(root_rect->width(), root_rect->height());
+  LayoutSize root_size = geometry.RootRect().Size();
 
   bool is_dominant = ComputeIsDominantVideoForTests(
       RoundedIntSize(target_size), RoundedIntSize(root_size),
diff --git a/third_party/blink/renderer/core/inspector/console_message.cc b/third_party/blink/renderer/core/inspector/console_message.cc
index cec47da..3a790f7 100644
--- a/third_party/blink/renderer/core/inspector/console_message.cc
+++ b/third_party/blink/renderer/core/inspector/console_message.cc
@@ -12,6 +12,7 @@
 #include "third_party/blink/renderer/core/workers/worker_thread.h"
 #include "third_party/blink/renderer/platform/wtf/assertions.h"
 #include "third_party/blink/renderer/platform/wtf/time.h"
+#include "third_party/blink/renderer/platform/wtf/vector.h"
 
 namespace blink {
 
@@ -61,6 +62,44 @@
   return console_message;
 }
 
+ConsoleMessage* ConsoleMessage::CreateFromWebConsoleMessage(
+    const WebConsoleMessage& message,
+    LocalFrame* local_frame) {
+  MessageLevel web_core_message_level = kInfoMessageLevel;
+  switch (message.level) {
+    case mojom::ConsoleMessageLevel::kVerbose:
+      web_core_message_level = kVerboseMessageLevel;
+      break;
+    case mojom::ConsoleMessageLevel::kInfo:
+      web_core_message_level = kInfoMessageLevel;
+      break;
+    case mojom::ConsoleMessageLevel::kWarning:
+      web_core_message_level = kWarningMessageLevel;
+      break;
+    case mojom::ConsoleMessageLevel::kError:
+      web_core_message_level = kErrorMessageLevel;
+      break;
+  }
+
+  MessageSource message_source = message.nodes.empty()
+                                     ? kOtherMessageSource
+                                     : kRecommendationMessageSource;
+
+  ConsoleMessage* console_message = ConsoleMessage::Create(
+      message_source, web_core_message_level, message.text,
+      SourceLocation::Create(message.url, message.line_number,
+                             message.column_number, nullptr));
+
+  if (local_frame) {
+    Vector<DOMNodeId> nodes;
+    for (const WebNode& web_node : message.nodes)
+      nodes.push_back(DOMNodeIds::IdForNode(&(*web_node)));
+    console_message->SetNodes(local_frame, std::move(nodes));
+  }
+
+  return console_message;
+}
+
 ConsoleMessage::ConsoleMessage(MessageSource source,
                                MessageLevel level,
                                const String& message,
diff --git a/third_party/blink/renderer/core/inspector/console_message.h b/third_party/blink/renderer/core/inspector/console_message.h
index 5105e9ca..8c46275 100644
--- a/third_party/blink/renderer/core/inspector/console_message.h
+++ b/third_party/blink/renderer/core/inspector/console_message.h
@@ -18,6 +18,7 @@
 class LocalFrame;
 class SourceLocation;
 class WorkerThread;
+struct WebConsoleMessage;
 
 class CORE_EXPORT ConsoleMessage final
     : public GarbageCollectedFinalized<ConsoleMessage> {
@@ -47,6 +48,11 @@
                                           std::unique_ptr<SourceLocation>,
                                           WorkerThread*);
 
+  // Creates a ConsoleMessage from a similar WebConsoleMessage.
+  static ConsoleMessage* CreateFromWebConsoleMessage(
+      const WebConsoleMessage& message,
+      LocalFrame* local_frame);
+
   ConsoleMessage(MessageSource,
                  MessageLevel,
                  const String& message,
diff --git a/third_party/blink/renderer/core/intersection_observer/element_intersection_observer_data.cc b/third_party/blink/renderer/core/intersection_observer/element_intersection_observer_data.cc
index 9cac7f96..67692b7 100644
--- a/third_party/blink/renderer/core/intersection_observer/element_intersection_observer_data.cc
+++ b/third_party/blink/renderer/core/intersection_observer/element_intersection_observer_data.cc
@@ -33,8 +33,10 @@
 }
 
 void ElementIntersectionObserverData::ComputeObservations(unsigned flags) {
-  for (auto& observation : intersection_observations_)
-    observation.value->Compute(flags);
+  HeapVector<Member<IntersectionObservation>> observations_to_process;
+  CopyValuesToVector(intersection_observations_, observations_to_process);
+  for (auto& observation : observations_to_process)
+    observation->Compute(flags);
 }
 
 void ElementIntersectionObserverData::Trace(blink::Visitor* visitor) {
diff --git a/third_party/blink/renderer/core/intersection_observer/intersection_geometry.cc b/third_party/blink/renderer/core/intersection_observer/intersection_geometry.cc
index 7abf8a13..6cc4e04f 100644
--- a/third_party/blink/renderer/core/intersection_observer/intersection_geometry.cc
+++ b/third_party/blink/renderer/core/intersection_observer/intersection_geometry.cc
@@ -32,20 +32,17 @@
   return descendant;
 }
 
-void MapRectUpToDocument(LayoutRect& rect,
-                         const LayoutObject& descendant,
-                         const Document& document) {
-  FloatQuad mapped_quad = descendant.LocalToAncestorQuad(
-      FloatQuad(FloatRect(rect)), document.GetLayoutView(),
-      kUseTransforms | kApplyContainerFlip);
+void MapRectUpToDocument(LayoutRect& rect, const LayoutObject& descendant) {
+  FloatQuad mapped_quad =
+      descendant.LocalToAncestorQuad(FloatQuad(FloatRect(rect)), nullptr,
+                                     kUseTransforms | kApplyContainerFlip);
   rect = LayoutRect(mapped_quad.BoundingBox());
 }
 
 void MapRectDownToDocument(LayoutRect& rect,
-                           LayoutBoxModelObject* ancestor,
                            const Document& document) {
   FloatQuad mapped_quad = document.GetLayoutView()->AncestorToLocalQuad(
-      ancestor, FloatQuad(FloatRect(rect)),
+      nullptr, FloatQuad(FloatRect(rect)),
       kUseTransforms | kApplyContainerFlip | kTraverseDocumentBoundaries);
   rect = LayoutRect(mapped_quad.BoundingBox());
 }
@@ -65,6 +62,29 @@
   return frame_root ? frame_root->ContentLayoutObject() : nullptr;
 }
 
+bool ComputeIsVisible(LayoutObject* target, const LayoutRect& rect) {
+  DCHECK(RuntimeEnabledFeatures::IntersectionObserverV2Enabled());
+  if (target->GetDocument()
+          .GetFrame()
+          ->LocalFrameRoot()
+          .MayBeOccludedOrObscuredByRemoteAncestor()) {
+    return false;
+  }
+  if (target->HasDistortingVisualEffects())
+    return false;
+  // TODO(layout-dev): This should hit-test the intersection rect, not the
+  // target rect; it's not helpful to know that the portion of the target that
+  // is clipped is also occluded. To do that, the intersection rect must be
+  // mapped down to the local space of the target element.
+  HitTestResult result(target->HitTestForOcclusion(rect));
+  return (!result.InnerNode() || result.InnerNode() == target->GetNode());
+}
+
+static const unsigned kConstructorFlagsMask =
+    IntersectionGeometry::kShouldReportRootBounds |
+    IntersectionGeometry::kShouldComputeVisibility |
+    IntersectionGeometry::kShouldTrackFractionOfRoot;
+
 }  // namespace
 
 IntersectionGeometry::IntersectionGeometry(Element* root_element,
@@ -72,165 +92,55 @@
                                            const Vector<Length>& root_margin,
                                            const Vector<float>& thresholds,
                                            unsigned flags)
-    : root_(root_element ? root_element->GetLayoutObject()
-                         : LocalRootView(target_element)),
-      target_(target_element.GetLayoutObject()),
-      root_margin_(root_margin),
-      thresholds_(thresholds),
-      flags_(flags),
+    : flags_(flags & kConstructorFlagsMask),
       intersection_ratio_(0),
       threshold_index_(0) {
   DCHECK(root_margin.IsEmpty() || root_margin.size() == 4);
-  if (root_element)
-    flags_ &= ~kRootIsImplicit;
-  else
-    flags_ |= kRootIsImplicit;
-  flags_ &= ~kIsVisible;
-  if (CanComputeGeometry(root_element, target_element))
-    ComputeGeometry();
+  ComputeGeometry(root_element, target_element, root_margin, thresholds);
 }
 
 IntersectionGeometry::~IntersectionGeometry() = default;
 
-bool IntersectionGeometry::CanComputeGeometry(Element* root,
-                                              Element& target) const {
-  if (root && !root->isConnected())
-    return false;
-  if (!root_ || !root_->IsBox())
-    return false;
-  if (!target.isConnected())
-    return false;
-  if (!target_ || (!target_->IsBoxModelObject() && !target_->IsText()))
-    return false;
-  if (root && !IsContainingBlockChainDescendant(target_, root_))
-    return false;
-  return true;
-}
-
-void IntersectionGeometry::InitializeTargetRect() {
-  if (target_->IsBox()) {
-    target_rect_ =
-        LayoutRect(ToLayoutBoxModelObject(target_)->BorderBoundingBox());
-  } else if (target_->IsLayoutInline()) {
-    target_rect_ = ToLayoutInline(target_)->LinesBoundingBox();
+void IntersectionGeometry::ComputeGeometry(Element* root_element,
+                                           Element& target_element,
+                                           const Vector<Length>& root_margin,
+                                           const Vector<float>& thresholds) {
+  LayoutObject* target = target_element.GetLayoutObject();
+  LayoutObject* root;
+  if (root_element) {
+    root = root_element->GetLayoutObject();
+    flags_ &= ~kRootIsImplicit;
   } else {
-    target_rect_ = ToLayoutText(target_)->LinesBoundingBox();
+    root = LocalRootView(target_element);
+    flags_ |= kRootIsImplicit;
   }
-}
-
-void IntersectionGeometry::InitializeRootRect() {
-  if (root_->IsLayoutView() && root_->GetDocument().IsInMainFrame()) {
-    // The main frame is a bit special as the scrolling viewport can differ in
-    // size from the LayoutView itself. There's two situations this occurs in:
-    // 1) The ForceZeroLayoutHeight quirk setting is used in Android WebView for
-    // compatibility and sets the initial-containing-block's (a.k.a.
-    // LayoutView) height to 0. Thus, we can't use its size for intersection
-    // testing. Use the FrameView geometry instead.
-    // 2) An element wider than the ICB can cause us to resize the FrameView so
-    // we can zoom out to fit the entire element width.
-    root_rect_ = ToLayoutView(root_)->OverflowClipRect(LayoutPoint());
-  } else if (root_->IsBox() && root_->HasOverflowClip()) {
-    root_rect_ = LayoutRect(ToLayoutBox(root_)->PhysicalContentBoxRect());
-  } else {
-    root_rect_ = LayoutRect(ToLayoutBoxModelObject(root_)->BorderBoundingBox());
-  }
-  ApplyRootMargin();
-}
-
-void IntersectionGeometry::ApplyRootMargin() {
-  if (root_margin_.IsEmpty())
+  if (!target_element.isConnected())
+    return;
+  if (root_element && !root_element->isConnected())
+    return;
+  if (!root || !root->IsBox())
+    return;
+  if (!target || (!target->IsBoxModelObject() && !target->IsText()))
+    return;
+  if (root_element && !IsContainingBlockChainDescendant(target, root))
     return;
 
-  // TODO(szager): Make sure the spec is clear that left/right margins are
-  // resolved against width and not height.
-  LayoutUnit top_margin = ComputeMargin(root_margin_[0], root_rect_.Height());
-  LayoutUnit right_margin = ComputeMargin(root_margin_[1], root_rect_.Width());
-  LayoutUnit bottom_margin =
-      ComputeMargin(root_margin_[2], root_rect_.Height());
-  LayoutUnit left_margin = ComputeMargin(root_margin_[3], root_rect_.Width());
+  DCHECK(!target_element.GetDocument().View()->NeedsLayout());
 
-  root_rect_.SetX(root_rect_.X() - left_margin);
-  root_rect_.SetWidth(root_rect_.Width() + left_margin + right_margin);
-  root_rect_.SetY(root_rect_.Y() - top_margin);
-  root_rect_.SetHeight(root_rect_.Height() + top_margin + bottom_margin);
-}
-
-unsigned IntersectionGeometry::FirstThresholdGreaterThan(float ratio) const {
-  unsigned result = 0;
-  while (result < thresholds_.size() && thresholds_[result] <= ratio)
-    ++result;
-  return result;
-}
-
-bool IntersectionGeometry::ClipToRoot() {
-  // Map and clip rect into root element coordinates.
-  // TODO(szager): the writing mode flipping needs a test.
-  LayoutBox* local_ancestor = nullptr;
-  if (!RootIsImplicit() || root_->GetDocument().IsInMainFrame())
-    local_ancestor = ToLayoutBox(root_);
-
-  LayoutView* layout_view = target_->GetDocument().GetLayoutView();
-
-  unsigned flags = kDefaultVisualRectFlags | kEdgeInclusive;
-  if (!layout_view->NeedsPaintPropertyUpdate() &&
-      !layout_view->DescendantNeedsPaintPropertyUpdate()) {
-    flags |= kUseGeometryMapper;
-  }
-  bool does_intersect = target_->MapToVisualRectInAncestorSpace(
-      local_ancestor, intersection_rect_, static_cast<VisualRectFlags>(flags));
-  if (!does_intersect || !local_ancestor)
-    return does_intersect;
-  if (local_ancestor->HasOverflowClip())
-    intersection_rect_.Move(-local_ancestor->ScrolledContentOffset());
-  LayoutRect root_clip_rect(root_rect_);
-  local_ancestor->FlipForWritingMode(root_clip_rect);
-  return does_intersect & intersection_rect_.InclusiveIntersect(root_clip_rect);
-}
-
-void IntersectionGeometry::MapTargetRectToTargetFrameCoordinates() {
-  Document& target_document = target_->GetDocument();
-  MapRectUpToDocument(target_rect_, *target_, target_document);
-}
-
-void IntersectionGeometry::MapRootRectToRootFrameCoordinates() {
-  root_rect_ = LayoutRect(
-      root_
-          ->LocalToAncestorQuad(
-              FloatQuad(FloatRect(root_rect_)),
-              RootIsImplicit() ? nullptr : root_->GetDocument().GetLayoutView(),
-              kUseTransforms | kApplyContainerFlip)
-          .BoundingBox());
-}
-
-void IntersectionGeometry::MapIntersectionRectToTargetFrameCoordinates() {
-  Document& target_document = target_->GetDocument();
-  if (RootIsImplicit()) {
-    LocalFrame* target_frame = target_document.GetFrame();
-    Frame& root_frame = target_frame->Tree().Top();
-    if (target_frame != &root_frame)
-      MapRectDownToDocument(intersection_rect_, nullptr, target_document);
+  LayoutRect target_rect = InitializeTargetRect(target);
+  LayoutRect intersection_rect = target_rect;
+  LayoutRect root_rect = InitializeRootRect(root, root_margin);
+  bool does_intersect = ClipToRoot(root, target, root_rect, intersection_rect);
+  MapRectUpToDocument(target_rect, *target);
+  if (does_intersect) {
+    if (RootIsImplicit())
+      MapRectDownToDocument(intersection_rect, target->GetDocument());
+    else
+      MapRectUpToDocument(intersection_rect, *root);
   } else {
-    MapRectUpToDocument(intersection_rect_, *root_, root_->GetDocument());
+    intersection_rect = LayoutRect();
   }
-}
-
-void IntersectionGeometry::ComputeGeometry() {
-  InitializeTargetRect();
-  intersection_rect_ = target_rect_;
-  InitializeRootRect();
-  DCHECK(root_);
-  DCHECK(target_);
-  DCHECK(!target_->GetDocument().View()->NeedsLayout());
-  bool does_intersect = ClipToRoot();
-  MapTargetRectToTargetFrameCoordinates();
-  if (does_intersect)
-    MapIntersectionRectToTargetFrameCoordinates();
-  else
-    intersection_rect_ = LayoutRect();
-  // Small optimization: if we're not going to report root bounds, don't bother
-  // transforming them to the frame.
-  if (ShouldReportRootBounds())
-    MapRootRectToRootFrameCoordinates();
+  MapRectUpToDocument(root_rect, *root);
 
   // Some corner cases for threshold index:
   //   - If target rect is zero area, because it has zero width and/or zero
@@ -249,11 +159,11 @@
 
   if (does_intersect) {
     const LayoutRect comparison_rect =
-        ShouldTrackFractionOfRoot() ? RootRect() : TargetRect();
+        ShouldTrackFractionOfRoot() ? root_rect : target_rect;
     if (comparison_rect.IsEmpty()) {
       intersection_ratio_ = 1;
     } else {
-      const LayoutSize& intersection_size = IntersectionRect().Size();
+      const LayoutSize& intersection_size = intersection_rect.Size();
       const float intersection_area = intersection_size.Width().ToFloat() *
                                       intersection_size.Height().ToFloat();
       const LayoutSize& comparison_size = comparison_rect.Size();
@@ -261,71 +171,112 @@
                                      comparison_size.Height().ToFloat();
       intersection_ratio_ = intersection_area / area_of_interest;
     }
-    threshold_index_ = FirstThresholdGreaterThan(intersection_ratio_);
+    threshold_index_ =
+        FirstThresholdGreaterThan(intersection_ratio_, thresholds);
   } else {
     intersection_ratio_ = 0;
     threshold_index_ = 0;
   }
-  ComputeVisibility();
-}
-
-void IntersectionGeometry::ComputeVisibility() {
-  if (!IsIntersecting() || !ShouldComputeVisibility())
-    return;
-  DCHECK(RuntimeEnabledFeatures::IntersectionObserverV2Enabled());
-  if (target_->GetDocument()
-          .GetFrame()
-          ->LocalFrameRoot()
-          .MayBeOccludedOrObscuredByRemoteAncestor()) {
-    return;
-  }
-  if (target_->HasDistortingVisualEffects())
-    return;
-  // TODO(layout-dev): This should hit-test the intersection rect, not the
-  // target rect; it's not helpful to know that the portion of the target that
-  // is clipped is also occluded. To do that, the intersection rect must be
-  // mapped down to the local space of the target element.
-  HitTestResult result(target_->HitTestForOcclusion(TargetRect()));
-  if (!result.InnerNode() || result.InnerNode() == target_->GetNode())
+  if (IsIntersecting() && ShouldComputeVisibility() &&
+      ComputeIsVisible(target, target_rect))
     flags_ |= kIsVisible;
+
+  // Convert to un-zoomed CSS pixels
+  FloatRect target_float_rect(target_rect);
+  AdjustForAbsoluteZoom::AdjustFloatRect(target_float_rect, *target);
+  target_rect_ = LayoutRect(target_float_rect);
+  FloatRect intersection_float_rect(intersection_rect);
+  AdjustForAbsoluteZoom::AdjustFloatRect(intersection_float_rect, *target);
+  intersection_rect_ = LayoutRect(intersection_float_rect);
+  FloatRect root_float_rect(root_rect);
+  AdjustForAbsoluteZoom::AdjustFloatRect(root_float_rect, *root);
+  root_rect_ = LayoutRect(root_float_rect);
 }
 
-LayoutRect IntersectionGeometry::UnZoomedTargetRect() const {
-  if (!target_)
-    return target_rect_;
-  FloatRect rect(target_rect_);
-  AdjustForAbsoluteZoom::AdjustFloatRect(rect, *target_);
-  return LayoutRect(rect);
+LayoutRect IntersectionGeometry::InitializeTargetRect(LayoutObject* target) {
+  if (target->IsBox())
+    return LayoutRect(ToLayoutBoxModelObject(target)->BorderBoundingBox());
+  if (target->IsLayoutInline())
+    return ToLayoutInline(target)->LinesBoundingBox();
+  return ToLayoutText(target)->LinesBoundingBox();
 }
 
-LayoutRect IntersectionGeometry::UnZoomedIntersectionRect() const {
-  if (!target_)
-    return intersection_rect_;
-  FloatRect rect(intersection_rect_);
-  AdjustForAbsoluteZoom::AdjustFloatRect(rect, *target_);
-  return LayoutRect(rect);
+LayoutRect IntersectionGeometry::InitializeRootRect(
+    LayoutObject* root,
+    const Vector<Length>& margin) {
+  LayoutRect result;
+  if (root->IsLayoutView() && root->GetDocument().IsInMainFrame()) {
+    // The main frame is a bit special as the scrolling viewport can differ in
+    // size from the LayoutView itself. There's two situations this occurs in:
+    // 1) The ForceZeroLayoutHeight quirk setting is used in Android WebView for
+    // compatibility and sets the initial-containing-block's (a.k.a.
+    // LayoutView) height to 0. Thus, we can't use its size for intersection
+    // testing. Use the FrameView geometry instead.
+    // 2) An element wider than the ICB can cause us to resize the FrameView so
+    // we can zoom out to fit the entire element width.
+    result = ToLayoutView(root)->OverflowClipRect(LayoutPoint());
+  } else if (root->IsBox() && root->HasOverflowClip()) {
+    result = LayoutRect(ToLayoutBox(root)->PhysicalContentBoxRect());
+  } else {
+    result = LayoutRect(ToLayoutBoxModelObject(root)->BorderBoundingBox());
+  }
+  ApplyRootMargin(result, margin);
+  return result;
 }
 
-LayoutRect IntersectionGeometry::UnZoomedRootRect() const {
-  if (!root_)
-    return root_rect_;
-  FloatRect rect(root_rect_);
-  AdjustForAbsoluteZoom::AdjustFloatRect(rect, *root_);
-  return LayoutRect(rect);
+void IntersectionGeometry::ApplyRootMargin(LayoutRect& rect,
+                                           const Vector<Length>& margin) {
+  if (margin.IsEmpty())
+    return;
+
+  // TODO(szager): Make sure the spec is clear that left/right margins are
+  // resolved against width and not height.
+  LayoutUnit top_margin = ComputeMargin(margin[0], rect.Height());
+  LayoutUnit right_margin = ComputeMargin(margin[1], rect.Width());
+  LayoutUnit bottom_margin = ComputeMargin(margin[2], rect.Height());
+  LayoutUnit left_margin = ComputeMargin(margin[3], rect.Width());
+
+  rect.SetX(rect.X() - left_margin);
+  rect.SetWidth(rect.Width() + left_margin + right_margin);
+  rect.SetY(rect.Y() - top_margin);
+  rect.SetHeight(rect.Height() + top_margin + bottom_margin);
 }
 
-IntersectionObserverEntry* IntersectionGeometry::CreateEntry(
-    Element* target,
-    DOMHighResTimeStamp timestamp) {
-  FloatRect root_bounds(UnZoomedRootRect());
-  FloatRect* root_bounds_pointer =
-      ShouldReportRootBounds() ? &root_bounds : nullptr;
-  IntersectionObserverEntry* entry =
-      MakeGarbageCollected<IntersectionObserverEntry>(
-          timestamp, IntersectionRatio(), FloatRect(UnZoomedTargetRect()),
-          root_bounds_pointer, FloatRect(UnZoomedIntersectionRect()),
-          IsIntersecting(), IsVisible(), target);
-  return entry;
+bool IntersectionGeometry::ClipToRoot(LayoutObject* root,
+                                      LayoutObject* target,
+                                      const LayoutRect& root_rect,
+                                      LayoutRect& intersection_rect) {
+  // Map and clip rect into root element coordinates.
+  // TODO(szager): the writing mode flipping needs a test.
+  LayoutBox* local_ancestor = nullptr;
+  if (!RootIsImplicit() || root->GetDocument().IsInMainFrame())
+    local_ancestor = ToLayoutBox(root);
+
+  LayoutView* layout_view = target->GetDocument().GetLayoutView();
+
+  unsigned flags = kDefaultVisualRectFlags | kEdgeInclusive;
+  if (!layout_view->NeedsPaintPropertyUpdate() &&
+      !layout_view->DescendantNeedsPaintPropertyUpdate()) {
+    flags |= kUseGeometryMapper;
+  }
+  bool does_intersect = target->MapToVisualRectInAncestorSpace(
+      local_ancestor, intersection_rect, static_cast<VisualRectFlags>(flags));
+  if (!does_intersect || !local_ancestor)
+    return does_intersect;
+  if (local_ancestor->HasOverflowClip())
+    intersection_rect.Move(-local_ancestor->ScrolledContentOffset());
+  LayoutRect root_clip_rect(root_rect);
+  local_ancestor->FlipForWritingMode(root_clip_rect);
+  return does_intersect & intersection_rect.InclusiveIntersect(root_clip_rect);
+}
+
+unsigned IntersectionGeometry::FirstThresholdGreaterThan(
+    float ratio,
+    const Vector<float>& thresholds) const {
+  unsigned result = 0;
+  while (result < thresholds.size() && thresholds[result] <= ratio)
+    ++result;
+  return result;
 }
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/intersection_observer/intersection_geometry.h b/third_party/blink/renderer/core/intersection_observer/intersection_geometry.h
index 6714cbb..5296d0b 100644
--- a/third_party/blink/renderer/core/intersection_observer/intersection_geometry.h
+++ b/third_party/blink/renderer/core/intersection_observer/intersection_geometry.h
@@ -5,6 +5,7 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_CORE_INTERSECTION_OBSERVER_INTERSECTION_GEOMETRY_H_
 #define THIRD_PARTY_BLINK_RENDERER_CORE_INTERSECTION_OBSERVER_INTERSECTION_GEOMETRY_H_
 
+#include "third_party/blink/renderer/core/core_export.h"
 #include "third_party/blink/renderer/core/dom/dom_high_res_time_stamp.h"
 #include "third_party/blink/renderer/platform/geometry/layout_rect.h"
 #include "third_party/blink/renderer/platform/geometry/length.h"
@@ -14,7 +15,6 @@
 namespace blink {
 
 class Element;
-class IntersectionObserverEntry;
 class LayoutObject;
 
 // Computes the intersection between an ancestor (root) element and a
@@ -24,9 +24,7 @@
 //
 // If the root argument to the constructor is null, computes the intersection
 // of the target with the top-level frame viewport (AKA the "implicit root").
-class IntersectionGeometry {
-  STACK_ALLOCATED();
-
+class CORE_EXPORT IntersectionGeometry {
  public:
   enum Flags {
     // These flags should passed to the constructor
@@ -44,6 +42,7 @@
                        const Vector<Length>& root_margin,
                        const Vector<float>& thresholds,
                        unsigned flags);
+  IntersectionGeometry(const IntersectionGeometry&) = default;
   ~IntersectionGeometry();
 
   bool ShouldReportRootBounds() const {
@@ -56,23 +55,10 @@
     return flags_ & kShouldTrackFractionOfRoot;
   }
 
-  LayoutObject* Root() const { return root_; }
-  LayoutObject* Target() const { return target_; }
-
-  // Client rect in the coordinate system of the frame containing target.
+  // These are all in CSS pixels
   LayoutRect TargetRect() const { return target_rect_; }
-  // Target rect in CSS pixels
-  LayoutRect UnZoomedTargetRect() const;
-
-  // Client rect in the coordinate system of the frame containing target.
   LayoutRect IntersectionRect() const { return intersection_rect_; }
-  // Intersection rect in CSS pixels
-  LayoutRect UnZoomedIntersectionRect() const;
-
-  // Client rect in the coordinate system of the frame containing root.
   LayoutRect RootRect() const { return root_rect_; }
-  // Root rect in CSS pixels
-  LayoutRect UnZoomedRootRect() const;
 
   IntRect IntersectionIntRect() const {
     return PixelSnappedIntRect(intersection_rect_);
@@ -87,26 +73,22 @@
   bool IsIntersecting() const { return threshold_index_ > 0; }
   bool IsVisible() const { return flags_ & kIsVisible; }
 
-  IntersectionObserverEntry* CreateEntry(Element* target,
-                                         DOMHighResTimeStamp timestamp);
-
  private:
-  void ComputeGeometry();
-  bool CanComputeGeometry(Element* root, Element& target) const;
-  void InitializeTargetRect();
-  void InitializeRootRect();
-  bool ClipToRoot();
-  void MapTargetRectToTargetFrameCoordinates();
-  void MapRootRectToRootFrameCoordinates();
-  void MapIntersectionRectToTargetFrameCoordinates();
-  void ApplyRootMargin();
-  unsigned FirstThresholdGreaterThan(float ratio) const;
-  void ComputeVisibility();
+  void ComputeGeometry(Element* root_element,
+                       Element& target_element,
+                       const Vector<Length>& root_margin,
+                       const Vector<float>& thresholds);
+  LayoutRect InitializeTargetRect(LayoutObject* target);
+  LayoutRect InitializeRootRect(LayoutObject* root,
+                                const Vector<Length>& margin);
+  void ApplyRootMargin(LayoutRect& rect, const Vector<Length>& margin);
+  bool ClipToRoot(LayoutObject* root,
+                  LayoutObject* target,
+                  const LayoutRect& root_rect,
+                  LayoutRect& intersection_rect);
+  unsigned FirstThresholdGreaterThan(float ratio,
+                                     const Vector<float>& thresholds) const;
 
-  LayoutObject* root_;
-  LayoutObject* target_;
-  const Vector<Length>& root_margin_;
-  const Vector<float>& thresholds_;
   LayoutRect target_rect_;
   LayoutRect intersection_rect_;
   LayoutRect root_rect_;
diff --git a/third_party/blink/renderer/core/intersection_observer/intersection_observation.cc b/third_party/blink/renderer/core/intersection_observer/intersection_observation.cc
index 39eecb4..3e0a9804 100644
--- a/third_party/blink/renderer/core/intersection_observer/intersection_observation.cc
+++ b/third_party/blink/renderer/core/intersection_observer/intersection_observation.cc
@@ -76,10 +76,9 @@
 
   if (last_threshold_index_ != geometry.ThresholdIndex() ||
       last_is_visible_ != geometry.IsVisible()) {
-    entries_.push_back(geometry.CreateEntry(Target(), timestamp));
-    To<Document>(Observer()->GetExecutionContext())
-        ->EnsureIntersectionObserverController()
-        .ScheduleIntersectionObserverForDelivery(*Observer());
+    entries_.push_back(MakeGarbageCollected<IntersectionObserverEntry>(
+        geometry, timestamp, Target()));
+    Observer()->SetNeedsDelivery();
     SetLastThresholdIndex(geometry.ThresholdIndex());
     SetWasVisible(geometry.IsVisible());
   }
diff --git a/third_party/blink/renderer/core/intersection_observer/intersection_observer.cc b/third_party/blink/renderer/core/intersection_observer/intersection_observer.cc
index 62e6b14..821f9fb 100644
--- a/third_party/blink/renderer/core/intersection_observer/intersection_observer.cc
+++ b/third_party/blink/renderer/core/intersection_observer/intersection_observer.cc
@@ -5,6 +5,7 @@
 #include "third_party/blink/renderer/core/intersection_observer/intersection_observer.h"
 
 #include <algorithm>
+#include <limits>
 
 #include "base/macros.h"
 #include "third_party/blink/renderer/bindings/core/v8/v8_intersection_observer_callback.h"
@@ -20,7 +21,6 @@
 #include "third_party/blink/renderer/core/intersection_observer/element_intersection_observer_data.h"
 #include "third_party/blink/renderer/core/intersection_observer/intersection_observer_controller.h"
 #include "third_party/blink/renderer/core/intersection_observer/intersection_observer_delegate.h"
-#include "third_party/blink/renderer/core/intersection_observer/intersection_observer_entry.h"
 #include "third_party/blink/renderer/core/intersection_observer/intersection_observer_init.h"
 #include "third_party/blink/renderer/core/layout/layout_view.h"
 #include "third_party/blink/renderer/core/timing/dom_window_performance.h"
@@ -41,8 +41,12 @@
                                    IntersectionObserver::EventCallback callback)
       : context_(context), callback_(std::move(callback)) {}
 
+  IntersectionObserver::DeliveryBehavior GetDeliveryBehavior() const override {
+    return IntersectionObserver::kDeliverDuringPostLifecycleSteps;
+  }
+
   void Deliver(const HeapVector<Member<IntersectionObserverEntry>>& entries,
-               IntersectionObserver&) override {
+               IntersectionObserver& observer) override {
     callback_.Run(entries);
   }
 
@@ -136,6 +140,8 @@
 }  // anonymous namespace
 
 static bool throttle_delay_enabled = true;
+const float IntersectionObserver::kMinimumThreshold =
+    std::numeric_limits<float>::min();
 
 void IntersectionObserver::SetThrottleDelayEnabledForTesting(bool enabled) {
   throttle_delay_enabled = enabled;
@@ -230,7 +236,8 @@
       root_is_implicit_(root ? 0 : 1),
       track_visibility_(track_visibility ? 1 : 0),
       track_fraction_of_root_(semantics == kFractionOfRoot),
-      always_report_root_bounds_(always_report_root_bounds ? 1 : 0) {
+      always_report_root_bounds_(always_report_root_bounds ? 1 : 0),
+      needs_delivery_(0) {
   switch (root_margin.size()) {
     case 0:
       break;
@@ -331,6 +338,7 @@
 
 HeapVector<Member<IntersectionObserverEntry>> IntersectionObserver::takeRecords(
     ExceptionState& exception_state) {
+  needs_delivery_ = 0;
   HeapVector<Member<IntersectionObserverEntry>> entries;
   for (auto& observation : observations_)
     observation->TakeRecords(entries);
@@ -369,14 +377,24 @@
   return -1;
 }
 
-unsigned IntersectionObserver::FirstThresholdGreaterThan(float ratio) const {
-  unsigned result = 0;
-  while (result < thresholds_.size() && thresholds_[result] <= ratio)
-    ++result;
-  return result;
+void IntersectionObserver::SetNeedsDelivery() {
+  if (needs_delivery_)
+    return;
+  needs_delivery_ = 1;
+  To<Document>(GetExecutionContext())
+      ->EnsureIntersectionObserverController()
+      .ScheduleIntersectionObserverForDelivery(*this);
+}
+
+IntersectionObserver::DeliveryBehavior
+IntersectionObserver::GetDeliveryBehavior() const {
+  return delegate_->GetDeliveryBehavior();
 }
 
 void IntersectionObserver::Deliver() {
+  if (!needs_delivery_)
+    return;
+  needs_delivery_ = 0;
   HeapVector<Member<IntersectionObserverEntry>> entries;
   for (auto& observation : observations_)
     observation->TakeRecords(entries);
diff --git a/third_party/blink/renderer/core/intersection_observer/intersection_observer.h b/third_party/blink/renderer/core/intersection_observer/intersection_observer.h
index 9d08b5b..dc21800b 100644
--- a/third_party/blink/renderer/core/intersection_observer/intersection_observer.h
+++ b/third_party/blink/renderer/core/intersection_observer/intersection_observer.h
@@ -57,6 +57,20 @@
   //                         ////////////////////
   enum ThresholdInterpretation { kFractionOfTarget, kFractionOfRoot };
 
+  // This value can be used to detect transitions between non-intersecting or
+  // edge-adjacent (i.e., zero area) state, and intersecting by any non-zero
+  // number of pixels.
+  static const float kMinimumThreshold;
+
+  // Used to specify when callbacks should be invoked with new notifications.
+  // Blink-internal users of IntersectionObserver will have their callbacks
+  // invoked synchronously at the end of a lifecycle update. Javascript
+  // observers will PostTask to invoke their callbacks.
+  enum DeliveryBehavior {
+    kDeliverDuringPostLifecycleSteps,
+    kPostTaskToDeliver
+  };
+
   static IntersectionObserver* Create(const IntersectionObserverInit*,
                                       IntersectionObserverDelegate&,
                                       ExceptionState&);
@@ -124,7 +138,8 @@
   const Length& RightMargin() const { return right_margin_; }
   const Length& BottomMargin() const { return bottom_margin_; }
   const Length& LeftMargin() const { return left_margin_; }
-  unsigned FirstThresholdGreaterThan(float ratio) const;
+  void SetNeedsDelivery();
+  DeliveryBehavior GetDeliveryBehavior() const;
   void Deliver();
 
   // Returns false if this observer has an explicit root element which has been
@@ -156,6 +171,7 @@
   unsigned track_visibility_ : 1;
   unsigned track_fraction_of_root_ : 1;
   unsigned always_report_root_bounds_ : 1;
+  unsigned needs_delivery_ : 1;
 };
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/intersection_observer/intersection_observer_controller.cc b/third_party/blink/renderer/core/intersection_observer/intersection_observer_controller.cc
index 7982aec..d64e1340 100644
--- a/third_party/blink/renderer/core/intersection_observer/intersection_observer_controller.cc
+++ b/third_party/blink/renderer/core/intersection_observer/intersection_observer_controller.cc
@@ -38,16 +38,20 @@
           FROM_HERE,
           WTF::Bind(
               &IntersectionObserverController::DeliverIntersectionObservations,
-              WrapWeakPersistent(this)));
+              WrapWeakPersistent(this),
+              IntersectionObserver::kPostTaskToDeliver));
 }
 
 void IntersectionObserverController::ScheduleIntersectionObserverForDelivery(
     IntersectionObserver& observer) {
   pending_intersection_observers_.insert(&observer);
-  PostTaskToDeliverObservations();
+  if (observer.GetDeliveryBehavior() ==
+      IntersectionObserver::kPostTaskToDeliver)
+    PostTaskToDeliverObservations();
 }
 
-void IntersectionObserverController::DeliverIntersectionObservations() {
+void IntersectionObserverController::DeliverIntersectionObservations(
+    IntersectionObserver::DeliveryBehavior behavior) {
   ExecutionContext* context = GetExecutionContext();
   if (!context) {
     pending_intersection_observers_.clear();
@@ -56,21 +60,26 @@
   // TODO(yukishiino): Remove this CHECK once https://crbug.com/809784 gets
   // resolved.
   CHECK(!context->IsContextDestroyed());
-  pending_intersection_observers_.swap(intersection_observers_being_invoked_);
-  for (auto& observer : intersection_observers_being_invoked_)
+  for (auto& observer : pending_intersection_observers_) {
+    if (observer->GetDeliveryBehavior() == behavior)
+      intersection_observers_being_invoked_.push_back(observer);
+  }
+  for (auto& observer : intersection_observers_being_invoked_) {
+    pending_intersection_observers_.erase(observer);
     observer->Deliver();
+  }
   intersection_observers_being_invoked_.clear();
 }
 
-void IntersectionObserverController::ComputeTrackedIntersectionObservations() {
+void IntersectionObserverController::ComputeTrackedIntersectionObservations(
+    unsigned flags) {
   if (Document* document = To<Document>(GetExecutionContext())) {
     TRACE_EVENT0("blink",
                  "IntersectionObserverController::"
                  "computeTrackedIntersectionObservations");
-    unsigned flags;
-    if (LocalFrameView* target_view = document->View())
-      flags = target_view->GetIntersectionObservationFlags();
-    for (auto& element : tracked_observation_targets_)
+    HeapVector<Member<Element>> elements_to_process;
+    CopyToVector(tracked_observation_targets_, elements_to_process);
+    for (auto& element : elements_to_process)
       element->ComputeIntersectionObservations(flags);
   }
 }
diff --git a/third_party/blink/renderer/core/intersection_observer/intersection_observer_controller.h b/third_party/blink/renderer/core/intersection_observer/intersection_observer_controller.h
index ee4e980..7eb4650 100644
--- a/third_party/blink/renderer/core/intersection_observer/intersection_observer_controller.h
+++ b/third_party/blink/renderer/core/intersection_observer/intersection_observer_controller.h
@@ -32,8 +32,16 @@
   virtual ~IntersectionObserverController();
 
   void ScheduleIntersectionObserverForDelivery(IntersectionObserver&);
-  void DeliverIntersectionObservations();
-  void ComputeTrackedIntersectionObservations();
+
+  // Immediately deliver all notifications for all observers for which
+  // (observer->GetDeliveryBehavior() == behavior).
+  void DeliverIntersectionObservations(
+      IntersectionObserver::DeliveryBehavior behavior);
+
+  // The flags argument is composed of values from
+  // IntersectionObservation::ComputeFlags. They are dirty bits that control
+  // whether an IntersectionObserver needs to do any work.
+  void ComputeTrackedIntersectionObservations(unsigned flags);
   void AddTrackedTarget(Element&);
   void RemoveTrackedTarget(Element&);
 
@@ -55,7 +63,7 @@
       pending_intersection_observers_;
   // TODO(https://crbug.com/796145): Remove this hack once on-stack objects
   // get supported by either of wrapper-tracing or unified GC.
-  HeapHashSet<TraceWrapperMember<IntersectionObserver>>
+  HeapVector<TraceWrapperMember<IntersectionObserver>>
       intersection_observers_being_invoked_;
 };
 
diff --git a/third_party/blink/renderer/core/intersection_observer/intersection_observer_delegate.h b/third_party/blink/renderer/core/intersection_observer/intersection_observer_delegate.h
index 7924ce0..8633bb7 100644
--- a/third_party/blink/renderer/core/intersection_observer/intersection_observer_delegate.h
+++ b/third_party/blink/renderer/core/intersection_observer/intersection_observer_delegate.h
@@ -5,6 +5,7 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_CORE_INTERSECTION_OBSERVER_INTERSECTION_OBSERVER_DELEGATE_H_
 #define THIRD_PARTY_BLINK_RENDERER_CORE_INTERSECTION_OBSERVER_INTERSECTION_OBSERVER_DELEGATE_H_
 
+#include "third_party/blink/renderer/core/intersection_observer/intersection_observer.h"
 #include "third_party/blink/renderer/platform/bindings/name_client.h"
 #include "third_party/blink/renderer/platform/heap/handle.h"
 
@@ -19,6 +20,10 @@
       public NameClient {
  public:
   virtual ~IntersectionObserverDelegate() = default;
+
+  virtual IntersectionObserver::DeliveryBehavior GetDeliveryBehavior()
+      const = 0;
+
   virtual void Deliver(const HeapVector<Member<IntersectionObserverEntry>>&,
                        IntersectionObserver&) = 0;
   virtual ExecutionContext* GetExecutionContext() const = 0;
diff --git a/third_party/blink/renderer/core/intersection_observer/intersection_observer_entry.cc b/third_party/blink/renderer/core/intersection_observer/intersection_observer_entry.cc
index 7707b151..2506f1b 100644
--- a/third_party/blink/renderer/core/intersection_observer/intersection_observer_entry.cc
+++ b/third_party/blink/renderer/core/intersection_observer/intersection_observer_entry.cc
@@ -5,35 +5,32 @@
 #include "third_party/blink/renderer/core/intersection_observer/intersection_observer_entry.h"
 
 #include "third_party/blink/renderer/core/dom/element.h"
+#include "third_party/blink/renderer/core/geometry/dom_rect_read_only.h"
 
 namespace blink {
 
 IntersectionObserverEntry::IntersectionObserverEntry(
+    const IntersectionGeometry& geometry,
     DOMHighResTimeStamp time,
-    double intersection_ratio,
-    const FloatRect& bounding_client_rect,
-    const FloatRect* root_bounds,
-    const FloatRect& intersection_rect,
-    bool is_intersecting,
-    bool is_visible,
     Element* target)
-    : time_(time),
-      intersection_ratio_(intersection_ratio),
-      bounding_client_rect_(
-          DOMRectReadOnly::FromFloatRect(bounding_client_rect)),
-      root_bounds_(root_bounds ? DOMRectReadOnly::FromFloatRect(*root_bounds)
-                               : nullptr),
-      intersection_rect_(DOMRectReadOnly::FromFloatRect(intersection_rect)),
-      target_(target),
-      is_intersecting_(is_intersecting),
-      is_visible_(is_visible)
+    : geometry_(geometry), time_(time), target_(target) {}
 
-{}
+DOMRectReadOnly* IntersectionObserverEntry::boundingClientRect() const {
+  return DOMRectReadOnly::FromFloatRect(FloatRect(geometry_.TargetRect()));
+}
+
+DOMRectReadOnly* IntersectionObserverEntry::rootBounds() const {
+  if (geometry_.ShouldReportRootBounds())
+    return DOMRectReadOnly::FromFloatRect(FloatRect(geometry_.RootRect()));
+  return nullptr;
+}
+
+DOMRectReadOnly* IntersectionObserverEntry::intersectionRect() const {
+  return DOMRectReadOnly::FromFloatRect(
+      FloatRect(geometry_.IntersectionRect()));
+}
 
 void IntersectionObserverEntry::Trace(blink::Visitor* visitor) {
-  visitor->Trace(bounding_client_rect_);
-  visitor->Trace(root_bounds_);
-  visitor->Trace(intersection_rect_);
   visitor->Trace(target_);
   ScriptWrappable::Trace(visitor);
 }
diff --git a/third_party/blink/renderer/core/intersection_observer/intersection_observer_entry.h b/third_party/blink/renderer/core/intersection_observer/intersection_observer_entry.h
index c1daa4b..c42ea26 100644
--- a/third_party/blink/renderer/core/intersection_observer/intersection_observer_entry.h
+++ b/third_party/blink/renderer/core/intersection_observer/intersection_observer_entry.h
@@ -6,48 +6,42 @@
 #define THIRD_PARTY_BLINK_RENDERER_CORE_INTERSECTION_OBSERVER_INTERSECTION_OBSERVER_ENTRY_H_
 
 #include "third_party/blink/renderer/core/dom/dom_high_res_time_stamp.h"
-#include "third_party/blink/renderer/core/geometry/dom_rect_read_only.h"
+#include "third_party/blink/renderer/core/intersection_observer/intersection_geometry.h"
 #include "third_party/blink/renderer/platform/bindings/script_wrappable.h"
 #include "third_party/blink/renderer/platform/geometry/int_rect.h"
 #include "third_party/blink/renderer/platform/heap/handle.h"
 
 namespace blink {
 
+class DOMRectReadOnly;
 class Element;
 
 class CORE_EXPORT IntersectionObserverEntry final : public ScriptWrappable {
   DEFINE_WRAPPERTYPEINFO();
 
  public:
-  IntersectionObserverEntry(DOMHighResTimeStamp timestamp,
-                            double intersection_ratio,
-                            const FloatRect& bounding_client_rect,
-                            const FloatRect* root_bounds,
-                            const FloatRect& intersection_rect,
-                            bool is_intersecting,
-                            bool is_visible,
-                            Element*);
+  IntersectionObserverEntry(const IntersectionGeometry& geometry,
+                            DOMHighResTimeStamp timestamp,
+                            Element* target);
 
+  // IDL interface
   double time() const { return time_; }
-  double intersectionRatio() const { return intersection_ratio_; }
-  DOMRectReadOnly* boundingClientRect() const { return bounding_client_rect_; }
-  DOMRectReadOnly* rootBounds() const { return root_bounds_; }
-  DOMRectReadOnly* intersectionRect() const { return intersection_rect_; }
-  bool isIntersecting() const { return is_intersecting_; }
-  bool isVisible() const { return is_visible_; }
+  double intersectionRatio() const { return geometry_.IntersectionRatio(); }
+  DOMRectReadOnly* boundingClientRect() const;
+  DOMRectReadOnly* rootBounds() const;
+  DOMRectReadOnly* intersectionRect() const;
+  bool isIntersecting() const { return geometry_.IsIntersecting(); }
+  bool isVisible() const { return geometry_.IsVisible(); }
   Element* target() const { return target_.Get(); }
 
+  // blink-internal interface
+  const IntersectionGeometry& GetGeometry() const { return geometry_; }
   void Trace(blink::Visitor*) override;
 
  private:
+  IntersectionGeometry geometry_;
   DOMHighResTimeStamp time_;
-  double intersection_ratio_;
-  Member<DOMRectReadOnly> bounding_client_rect_;
-  Member<DOMRectReadOnly> root_bounds_;
-  Member<DOMRectReadOnly> intersection_rect_;
   Member<Element> target_;
-  bool is_intersecting_;
-  bool is_visible_;
 };
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/intersection_observer/intersection_observer_test.cc b/third_party/blink/renderer/core/intersection_observer/intersection_observer_test.cc
index 295aeed..bb96b98 100644
--- a/third_party/blink/renderer/core/intersection_observer/intersection_observer_test.cc
+++ b/third_party/blink/renderer/core/intersection_observer/intersection_observer_test.cc
@@ -26,6 +26,12 @@
  public:
   TestIntersectionObserverDelegate(Document& document)
       : document_(document), call_count_(0) {}
+  // TODO(szager): Add tests for the synchronous delivery code path. There is
+  // already some indirect coverage by unit tests exercising features that rely
+  // on it, but we should have some direct coverage in here.
+  IntersectionObserver::DeliveryBehavior GetDeliveryBehavior() const override {
+    return IntersectionObserver::kPostTaskToDeliver;
+  }
   void Deliver(const HeapVector<Member<IntersectionObserverEntry>>& entries,
                IntersectionObserver&) override {
     call_count_++;
@@ -38,11 +44,11 @@
   FloatRect LastIntersectionRect() const {
     if (entries_.IsEmpty())
       return FloatRect();
-    const IntersectionObserverEntry* entry = entries_.back();
-    return FloatRect(entry->intersectionRect()->x(),
-                     entry->intersectionRect()->y(),
-                     entry->intersectionRect()->width(),
-                     entry->intersectionRect()->height());
+    const IntersectionGeometry& geometry = entries_.back()->GetGeometry();
+    return FloatRect(geometry.IntersectionRect().X(),
+                     geometry.IntersectionRect().Y(),
+                     geometry.IntersectionRect().Width(),
+                     geometry.IntersectionRect().Height());
   }
 
   void Trace(blink::Visitor* visitor) override {
diff --git a/third_party/blink/renderer/core/invisible_dom/invisible_dom.cc b/third_party/blink/renderer/core/invisible_dom/invisible_dom.cc
index 58f6e93..7a71cc10 100644
--- a/third_party/blink/renderer/core/invisible_dom/invisible_dom.cc
+++ b/third_party/blink/renderer/core/invisible_dom/invisible_dom.cc
@@ -10,6 +10,8 @@
 
 namespace blink {
 bool InvisibleDOM::IsInsideInvisibleSubtree(const Node& node) {
+  if (!RuntimeEnabledFeatures::InvisibleDOMEnabled())
+    return false;
   if (!node.CanParticipateInFlatTree())
     return false;
   for (Node& ancestor : FlatTreeTraversal::InclusiveAncestorsOf(node)) {
@@ -22,6 +24,8 @@
 }
 
 Element* InvisibleDOM::InvisibleRoot(const Node& node) {
+  if (!RuntimeEnabledFeatures::InvisibleDOMEnabled())
+    return nullptr;
   Element* root = nullptr;
   for (Node& ancestor : FlatTreeTraversal::InclusiveAncestorsOf(node)) {
     if (ancestor.IsElementNode() &&
@@ -34,6 +38,8 @@
 
 bool InvisibleDOM::ActivateRangeIfNeeded(
     const EphemeralRangeInFlatTree& range) {
+  if (!RuntimeEnabledFeatures::InvisibleDOMEnabled())
+    return false;
   if (range.IsNull() || range.IsCollapsed())
     return false;
   HeapVector<Member<Element>> elements_to_activate;
diff --git a/third_party/blink/renderer/core/invisible_dom/invisible_dom.h b/third_party/blink/renderer/core/invisible_dom/invisible_dom.h
index adf323c8..33e87e4 100644
--- a/third_party/blink/renderer/core/invisible_dom/invisible_dom.h
+++ b/third_party/blink/renderer/core/invisible_dom/invisible_dom.h
@@ -21,6 +21,7 @@
   static bool IsInsideInvisibleSubtree(const Node& node);
 
   // Highest inclusive ancestor that has the invisible attribute.
+  // Will always return non-null value if IsInsideInvisibleSubtree() is true.
   static Element* InvisibleRoot(const Node&);
 
   // Activates all the nodes within |range|. Returns true if at least one
diff --git a/third_party/blink/renderer/core/layout/layout_box.cc b/third_party/blink/renderer/core/layout/layout_box.cc
index bf21a63..8584bb6 100644
--- a/third_party/blink/renderer/core/layout/layout_box.cc
+++ b/third_party/blink/renderer/core/layout/layout_box.cc
@@ -122,6 +122,8 @@
       max_preferred_logical_width_(-1),
       inline_box_wrapper_(nullptr) {
   SetIsBox();
+  if (blink::IsHTMLLegendElement(node))
+    SetIsHTMLLegendElement();
 }
 
 LayoutBox::~LayoutBox() {
diff --git a/third_party/blink/renderer/core/layout/layout_deprecated_flexible_box.cc b/third_party/blink/renderer/core/layout/layout_deprecated_flexible_box.cc
index bdad70e..af9790f 100644
--- a/third_party/blink/renderer/core/layout/layout_deprecated_flexible_box.cc
+++ b/third_party/blink/renderer/core/layout/layout_deprecated_flexible_box.cc
@@ -161,10 +161,10 @@
     for (RootInlineBox* box = block_flow->FirstRootBox(); box;
          box = box->NextRootBox()) {
       if (++count == line_count)
-        return (box->LineBottom() + (include_bottom
-                                         ? (block_flow->BorderBottom() +
-                                            block_flow->PaddingBottom())
-                                         : LayoutUnit()))
+        return (box->LineBottomWithLeading() +
+                (include_bottom ? (block_flow->BorderBottom() +
+                                   block_flow->PaddingBottom())
+                                : LayoutUnit()))
             .ToInt();
     }
     return -1;
diff --git a/third_party/blink/renderer/core/layout/layout_fieldset.cc b/third_party/blink/renderer/core/layout/layout_fieldset.cc
index 1877a13..f0c42eb 100644
--- a/third_party/blink/renderer/core/layout/layout_fieldset.cc
+++ b/third_party/blink/renderer/core/layout/layout_fieldset.cc
@@ -156,7 +156,7 @@
     if (legend->IsFloatingOrOutOfFlowPositioned())
       continue;
 
-    if (IsHTMLLegendElement(legend->GetNode()))
+    if (legend->IsHTMLLegendElement())
       return ToLayoutBox(legend);
   }
   return nullptr;
diff --git a/third_party/blink/renderer/core/layout/layout_flexible_box.cc b/third_party/blink/renderer/core/layout/layout_flexible_box.cc
index bd9197f0..b72f775 100644
--- a/third_party/blink/renderer/core/layout/layout_flexible_box.cc
+++ b/third_party/blink/renderer/core/layout/layout_flexible_box.cc
@@ -722,9 +722,9 @@
         location.TransposedPoint());
 }
 
-bool LayoutFlexibleBox::MainAxisLengthIsDefinite(
-    const LayoutBox& child,
-    const Length& flex_basis) const {
+bool LayoutFlexibleBox::MainAxisLengthIsDefinite(const LayoutBox& child,
+                                                 const Length& flex_basis,
+                                                 bool add_to_cb) const {
   if (flex_basis.IsAuto())
     return false;
   if (flex_basis.IsPercentOrCalc()) {
@@ -732,7 +732,11 @@
       return true;
     if (has_definite_height_ == SizeDefiniteness::kIndefinite)
       return false;
-    bool definite = child.ComputePercentageLogicalHeight(flex_basis) != -1;
+    LayoutBlock* cb = nullptr;
+    bool definite =
+        child.ContainingBlockLogicalHeightForPercentageResolution(&cb) != -1;
+    if (add_to_cb)
+      cb->AddPercentHeightDescendant(const_cast<LayoutBox*>(&child));
     if (in_layout_) {
       // We can reach this code even while we're not laying ourselves out, such
       // as from mainSizeForPercentageResolution.
@@ -754,7 +758,8 @@
       return true;
     if (has_definite_height_ == SizeDefiniteness::kIndefinite)
       return false;
-    bool definite = child.ComputePercentageLogicalHeight(length) != -1;
+    bool definite =
+        child.ContainingBlockLogicalHeightForPercentageResolution() != -1;
     has_definite_height_ =
         definite ? SizeDefiniteness::kDefinite : SizeDefiniteness::kIndefinite;
     return definite;
@@ -767,10 +772,18 @@
 void LayoutFlexibleBox::CacheChildMainSize(const LayoutBox& child) {
   DCHECK(!child.NeedsLayout());
   LayoutUnit main_size;
-  if (MainAxisIsInlineAxis(child))
+  if (MainAxisIsInlineAxis(child)) {
     main_size = child.MaxPreferredLogicalWidth();
-  else
-    main_size = child.LogicalHeight();
+  } else {
+    if (FlexBasisForChild(child).IsPercentOrCalc() &&
+        !MainAxisLengthIsDefinite(child, FlexBasisForChild(child))) {
+      main_size = child.IntrinsicContentLogicalHeight() +
+                  child.BorderAndPaddingLogicalHeight() +
+                  child.ScrollbarLogicalHeight();
+    } else {
+      main_size = child.LogicalHeight();
+    }
+  }
   intrinsic_size_along_main_axis_.Set(&child, main_size);
   relaid_out_children_.insert(&child);
 }
@@ -1113,7 +1126,7 @@
   // 2) of the flexbox spec.
   // We need to check for the flexbox to have a definite main size.
   // We make up a percentage to check whether we have a definite size.
-  if (!MainAxisLengthIsDefinite(child, Length::Percent(0)))
+  if (!MainAxisLengthIsDefinite(child, Length::Percent(0), false))
     return false;
 
   if (MainAxisIsInlineAxis(child))
diff --git a/third_party/blink/renderer/core/layout/layout_flexible_box.h b/third_party/blink/renderer/core/layout/layout_flexible_box.h
index 56a1382e..318a5b8f 100644
--- a/third_party/blink/renderer/core/layout/layout_flexible_box.h
+++ b/third_party/blink/renderer/core/layout/layout_flexible_box.h
@@ -155,7 +155,8 @@
       ChildLayoutType = kLayoutIfNeeded);
   void AdjustAlignmentForChild(LayoutBox& child, LayoutUnit);
   bool MainAxisLengthIsDefinite(const LayoutBox& child,
-                                const Length& flex_basis) const;
+                                const Length& flex_basis,
+                                bool add_to_cb = true) const;
   bool CrossAxisLengthIsDefinite(const LayoutBox& child,
                                  const Length& flex_basis) const;
   bool NeedToStretchChildLogicalHeight(const LayoutBox& child) const;
diff --git a/third_party/blink/renderer/core/layout/layout_object.cc b/third_party/blink/renderer/core/layout/layout_object.cc
index 53e0e1f2..29169fd5 100644
--- a/third_party/blink/renderer/core/layout/layout_object.cc
+++ b/third_party/blink/renderer/core/layout/layout_object.cc
@@ -491,9 +491,10 @@
   return false;
 }
 
-bool LayoutObject::IsRenderedLegend() const {
-  if (!IsBox() || !IsHTMLLegendElement(GetNode()))
-    return false;
+bool LayoutObject::IsRenderedLegendInternal() const {
+  DCHECK(IsBox());
+  DCHECK(IsHTMLLegendElement());
+
   if (IsFloatingOrOutOfFlowPositioned())
     return false;
   const auto* parent = Parent();
@@ -852,11 +853,21 @@
   if (object->IsLayoutScrollbarPart())
     return false;
 
-  // In general we can't relayout a flex item independently of its container;
-  // not only is the result incorrect due to the override size that's set, it
-  // also messes with the cached main size on the flexbox.
-  if (object->IsBox() && ToLayoutBox(object)->IsFlexItemIncludingNG())
-    return false;
+  if (const LayoutBox* layout_box = ToLayoutBoxOrNull(object)) {
+    // In general we can't relayout a flex item independently of its container;
+    // not only is the result incorrect due to the override size that's set, it
+    // also messes with the cached main size on the flexbox.
+    if (layout_box->IsFlexItemIncludingNG())
+      return false;
+
+    // In LayoutNG, if box has any OOF descendants, they are propagated to
+    // parent. Therefore, we must mark parent chain for layout.
+    if (layout_box->GetCachedLayoutResult() &&
+        layout_box->GetCachedLayoutResult()
+                ->OutOfFlowPositionedDescendants()
+                .size() > 0)
+      return false;
+  }
 
   // Inside multicol it's generally problematic to allow relayout roots. The
   // multicol container itself may be scheduled for relayout as well (due to
diff --git a/third_party/blink/renderer/core/layout/layout_object.h b/third_party/blink/renderer/core/layout/layout_object.h
index 33b219c2..71e57d4 100644
--- a/third_party/blink/renderer/core/layout/layout_object.h
+++ b/third_party/blink/renderer/core/layout/layout_object.h
@@ -1008,10 +1008,21 @@
     return bitfields_.IsGlobalRootScroller();
   }
 
+  bool IsHTMLLegendElement() const { return bitfields_.IsHTMLLegendElement(); }
+
   // Return true if this is the "rendered legend" of a fieldset. They get
   // special treatment, in that they establish a new formatting context, and
   // shrink to fit if no logical width is specified.
-  bool IsRenderedLegend() const;
+  //
+  // This function is performance sensitive.
+  inline bool IsRenderedLegend() const {
+    if (LIKELY(!IsHTMLLegendElement()))
+      return false;
+
+    return IsRenderedLegendInternal();
+  }
+
+  bool IsRenderedLegendInternal() const;
 
   // The pseudo element style can be cached or uncached.  Use the cached method
   // if the pseudo element doesn't respect any pseudo classes (and therefore
@@ -1243,6 +1254,7 @@
   void SetIsGlobalRootScroller(bool is_global_root_scroller) {
     bitfields_.SetIsGlobalRootScroller(is_global_root_scroller);
   }
+  void SetIsHTMLLegendElement() { bitfields_.SetIsHTMLLegendElement(true); }
 
   virtual void Paint(const PaintInfo&) const;
 
@@ -2580,6 +2592,7 @@
           is_effective_root_scroller_(false),
           is_global_root_scroller_(false),
           pending_update_first_line_image_observers_(false),
+          is_html_legend_element_(false),
           positioned_state_(kIsStaticallyPositioned),
           selection_state_(static_cast<unsigned>(SelectionState::kNone)),
           subtree_paint_property_update_reasons_(
@@ -2822,6 +2835,10 @@
     ADD_BOOLEAN_BITFIELD(pending_update_first_line_image_observers_,
                          PendingUpdateFirstLineImageObservers);
 
+    // Whether this object's |Node| is a HTMLLegendElement. Used to increase
+    // performance of |IsRenderedLegend| which is performance sensitive.
+    ADD_BOOLEAN_BITFIELD(is_html_legend_element_, IsHTMLLegendElement);
+
    private:
     // This is the cached 'position' value of this object
     // (see ComputedStyle::position).
diff --git a/third_party/blink/renderer/core/layout/layout_theme_mac.mm b/third_party/blink/renderer/core/layout/layout_theme_mac.mm
index 7929f5f..b2514bb 100644
--- a/third_party/blink/renderer/core/layout/layout_theme_mac.mm
+++ b/third_party/blink/renderer/core/layout/layout_theme_mac.mm
@@ -793,11 +793,11 @@
   SetSizeFromFont(style, SearchFieldSizes());
 }
 
-const int kSearchFieldBorderWidth = 2;
+const uint8_t kSearchFieldBorderWidth = 2;
 void LayoutThemeMac::AdjustSearchFieldStyle(ComputedStyle& style) const {
   // Override border.
   style.ResetBorder();
-  const short border_width = kSearchFieldBorderWidth * style.EffectiveZoom();
+  const float border_width = kSearchFieldBorderWidth * style.EffectiveZoom();
   style.SetBorderLeftWidth(border_width);
   style.SetBorderLeftStyle(EBorderStyle::kInset);
   style.SetBorderRightWidth(border_width);
diff --git a/third_party/blink/renderer/core/layout/ng/inline/ng_inline_node.cc b/third_party/blink/renderer/core/layout/ng/inline/ng_inline_node.cc
index 1712ef8..951deac 100644
--- a/third_party/blink/renderer/core/layout/ng/inline/ng_inline_node.cc
+++ b/third_party/blink/renderer/core/layout/ng/inline/ng_inline_node.cc
@@ -304,7 +304,7 @@
         break;
       }
       node = GetLayoutObjectForParentNode(node);
-      if (node == block) {
+      if (node == block || !node) {
         // Set |node| to |nullptr| to break out of the outer loop.
         node = nullptr;
         break;
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 1fa2cfb..573869b 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
@@ -102,6 +102,7 @@
       mode_(mode),
       constraint_space_(space),
       exclusion_space_(exclusion_space),
+      break_token_(break_token),
       break_iterator_(items_data_.text_content),
       shaper_(items_data_.text_content),
       spacing_(items_data_.text_content),
@@ -396,6 +397,7 @@
          (item.Type() == NGInlineItem::kControl &&
           Text()[item.StartOffset()] == kTabulationCharacter));
   DCHECK(&shape_result);
+  DCHECK_EQ(auto_wrap_, item.Style()->AutoWrap());
 
   // If we're trailing, only trailing spaces can be included in this line.
   if (state_ == LineBreakState::kTrailing) {
@@ -470,10 +472,12 @@
     return HandleOverflow();
   }
 
-  // Add the rest of the item if !auto_wrap.
-  // Because the start position may need to reshape, run ShapingLineBreaker
-  // with max available width.
-  BreakText(item_result, item, shape_result, LayoutUnit::Max());
+  // Add the whole item if !auto_wrap. The previous line should not have wrapped
+  // in the middle of nowrap item.
+  DCHECK_EQ(item_result->start_offset, item.StartOffset());
+  DCHECK_EQ(item_result->end_offset, item.EndOffset());
+  item_result->inline_size = shape_result.SnappedWidth().ClampNegativeToZero();
+  item_result->shape_result = ShapeResultView::Create(&shape_result);
 
   if (item.IsSymbolMarker()) {
     LayoutUnit symbol_width = LayoutListMarker::WidthOfSymbol(*item.Style());
@@ -481,9 +485,9 @@
       item_result->inline_size = symbol_width;
   }
 
-  DCHECK_EQ(item_result->end_offset, item.EndOffset());
   DCHECK(!item_result->may_break_inside);
-  item_result->can_break_after = false;
+  DCHECK(!item_result->can_break_after);
+  trailing_whitespace_ = WhitespaceState::kUnknown;
   position_ += item_result->inline_size;
   MoveToNextOf(item);
 }
@@ -779,6 +783,12 @@
   const String& text = Text();
   DCHECK(item.Style());
   const ComputedStyle& style = *item.Style();
+
+  if (!auto_wrap_) {
+    state_ = LineBreakState::kDone;
+    return;
+  }
+
   if (style.CollapseWhiteSpace()) {
     if (text[offset_] != kSpaceCharacter) {
       state_ = LineBreakState::kDone;
@@ -796,7 +806,7 @@
   } else {
     // Find the end of the run of space characters in this item.
     // Other white space characters (e.g., tab) are not included in this item.
-    DCHECK(!auto_wrap_ || style.BreakOnlyAfterWhiteSpace());
+    DCHECK(style.BreakOnlyAfterWhiteSpace());
     unsigned end = offset_;
     while (end < item.EndOffset() && IsBreakableSpace(text[end]))
       end++;
@@ -1415,8 +1425,6 @@
     if (!item_results_->IsEmpty())
       Rewind(0);
     state_ = LineBreakState::kContinue;
-    trailing_whitespace_ = WhitespaceState::kLeading;
-    SetCurrentStyle(line_info_->LineStyle());
     return;
   }
 
@@ -1476,21 +1484,59 @@
     // Use |results[new_end - 1].end_offset| because it may have been truncated
     // and may not be equal to |results[new_end].start_offset|.
     MoveToNextOf(item_results[new_end - 1]);
+    trailing_whitespace_ = WhitespaceState::kUnknown;
   } else {
     // When rewinding all items, use |results[0].start_offset|.
     const NGInlineItemResult& first_remove = item_results[new_end];
     item_index_ = first_remove.item_index;
     offset_ = first_remove.start_offset;
+    trailing_whitespace_ = WhitespaceState::kLeading;
   }
+  SetCurrentStyle(ComputeCurrentStyle(new_end));
 
   item_results.Shrink(new_end);
 
-  trailing_whitespace_ = WhitespaceState::kUnknown;
   trailing_collapsible_space_.reset();
   SetLineEndFragment(nullptr);
   UpdatePosition();
 }
 
+// Returns the style to use for |item_result_index|. Normally when handling
+// items sequentially, the current style is updated on open/close tag. When
+// rewinding, this function computes the style for the specified item.
+const ComputedStyle& NGLineBreaker::ComputeCurrentStyle(
+    unsigned item_result_index) const {
+  NGInlineItemResults& item_results = *item_results_;
+
+  // Use the current item if it can compute the current style.
+  const NGInlineItem* item = item_results[item_result_index].item;
+  DCHECK(item);
+  if (item->Type() == NGInlineItem::kText ||
+      item->Type() == NGInlineItem::kCloseTag) {
+    DCHECK(item->Style());
+    return *item->Style();
+  }
+
+  // Otherwise look back an item that can compute the current style.
+  while (item_result_index) {
+    item = item_results[--item_result_index].item;
+    if (item->Type() == NGInlineItem::kText ||
+        item->Type() == NGInlineItem::kOpenTag) {
+      DCHECK(item->Style());
+      return *item->Style();
+    }
+    if (item->Type() == NGInlineItem::kCloseTag)
+      return item->GetLayoutObject()->Parent()->StyleRef();
+  }
+
+  // Use the style at the beginning of the line if no items are available.
+  if (break_token_) {
+    DCHECK(break_token_->Style());
+    return *break_token_->Style();
+  }
+  return line_info_->LineStyle();
+}
+
 void NGLineBreaker::SetCurrentStyle(const ComputedStyle& style) {
   auto_wrap_ = style.AutoWrap();
 
diff --git a/third_party/blink/renderer/core/layout/ng/inline/ng_line_breaker.h b/third_party/blink/renderer/core/layout/ng/inline/ng_line_breaker.h
index c53f005..61fd33e 100644
--- a/third_party/blink/renderer/core/layout/ng/inline/ng_line_breaker.h
+++ b/third_party/blink/renderer/core/layout/ng/inline/ng_line_breaker.h
@@ -145,6 +145,7 @@
   void HandleOverflow();
   void Rewind(unsigned new_end);
 
+  const ComputedStyle& ComputeCurrentStyle(unsigned item_result_index) const;
   void SetCurrentStyle(const ComputedStyle&);
 
   void MoveToNextOf(const NGInlineItem&);
@@ -224,6 +225,7 @@
   NGLineBreakerMode mode_;
   const NGConstraintSpace& constraint_space_;
   NGExclusionSpace* exclusion_space_;
+  scoped_refptr<const NGInlineBreakToken> break_token_;
   scoped_refptr<const ComputedStyle> current_style_;
 
   LazyLineBreakIterator break_iterator_;
diff --git a/third_party/blink/renderer/core/layout/ng/ng_block_node.cc b/third_party/blink/renderer/core/layout/ng/ng_block_node.cc
index 39c1440e..24f9c29f 100644
--- a/third_party/blink/renderer/core/layout/ng/ng_block_node.cc
+++ b/third_party/blink/renderer/core/layout/ng/ng_block_node.cc
@@ -304,6 +304,8 @@
     }
   }
 
+  // TODO(layoutng) Can UpdateMarkerTextIfNeeded call be moved
+  // somewhere else? List items need up-to-date markers before layout.
   if (IsListItem())
     ToLayoutNGListItem(box_)->UpdateMarkerTextIfNeeded();
 }
@@ -346,6 +348,11 @@
     WritingMode container_writing_mode,
     const MinMaxSizeInput& input,
     const NGConstraintSpace* constraint_space) {
+  // TODO(layoutng) Can UpdateMarkerTextIfNeeded call be moved
+  // somewhere else? List items need up-to-date markers before layout.
+  if (IsListItem())
+    ToLayoutNGListItem(box_)->UpdateMarkerTextIfNeeded();
+
   bool is_orthogonal_flow_root =
       !IsParallelWritingMode(container_writing_mode, Style().GetWritingMode());
 
diff --git a/third_party/blink/renderer/core/loader/document_loader_test.cc b/third_party/blink/renderer/core/loader/document_loader_test.cc
index 948708c..ee83a089 100644
--- a/third_party/blink/renderer/core/loader/document_loader_test.cc
+++ b/third_party/blink/renderer/core/loader/document_loader_test.cc
@@ -99,7 +99,7 @@
     bool FillNavigationParamsResponse(WebNavigationParams* params) override {
       params->response = WebURLResponse(params->url);
       params->response.SetMIMEType("text/html");
-      params->response.SetHTTPStatusCode(200);
+      params->response.SetHttpStatusCode(200);
 
       std::string data("<html><body>foo</body></html>");
       for (size_t i = 0; i < data.size(); i++)
@@ -216,7 +216,7 @@
   WebURL url =
       url_test_helpers::ToKURL("https://examplenoupgrade.com/foo.html");
   WebURLResponse response(url);
-  response.SetHTTPStatusCode(200);
+  response.SetHttpStatusCode(200);
   response.SetHTTPHeaderField("mixed-content", "noupgrade");
   url_test_helpers::RegisterMockedURLLoadWithCustomResponse(
       url, test::CoreTestDataPath("foo.html"), response);
diff --git a/third_party/blink/renderer/core/loader/empty_clients.h b/third_party/blink/renderer/core/loader/empty_clients.h
index 154a7b8..2189154 100644
--- a/third_party/blink/renderer/core/loader/empty_clients.h
+++ b/third_party/blink/renderer/core/loader/empty_clients.h
@@ -102,7 +102,7 @@
                      const cc::OverscrollBehavior&) override {}
 
   void BeginLifecycleUpdates() override {}
-  void StartDeferringCommits() override {}
+  void StartDeferringCommits(base::TimeDelta timeout) override {}
   void StopDeferringCommits() override {}
 
   bool HadFormInteraction() const override { return false; }
diff --git a/third_party/blink/renderer/core/loader/frame_loader.cc b/third_party/blink/renderer/core/loader/frame_loader.cc
index 5622122..6028d4d 100644
--- a/third_party/blink/renderer/core/loader/frame_loader.cc
+++ b/third_party/blink/renderer/core/loader/frame_loader.cc
@@ -706,6 +706,10 @@
 
     if (frame_->Owner() && frame_->Owner()->GetSandboxFlags() & kSandboxOrigin)
       return false;
+
+    frame_->GetDocument()->ProcessJavaScriptUrl(
+        url, request.ShouldCheckMainWorldContentSecurityPolicy());
+    return false;
   }
 
   if (!request.OriginDocument()->GetSecurityOrigin()->CanDisplay(url)) {
@@ -940,12 +944,6 @@
     LocalFrame::ConsumeTransientUserActivation(frame_);
   }
 
-  if (url.ProtocolIsJavaScript()) {
-    frame_->GetDocument()->ProcessJavaScriptUrl(
-        url, request.ShouldCheckMainWorldContentSecurityPolicy());
-    return;
-  }
-
   Client()->BeginNavigation(
       resource_request, origin_document, nullptr /* document_loader */,
       navigation_type, policy, has_transient_activation, frame_load_type,
diff --git a/third_party/blink/renderer/core/loader/modulescript/document_module_script_fetcher.cc b/third_party/blink/renderer/core/loader/modulescript/document_module_script_fetcher.cc
index c64bd35..0245b87e 100644
--- a/third_party/blink/renderer/core/loader/modulescript/document_module_script_fetcher.cc
+++ b/third_party/blink/renderer/core/loader/modulescript/document_module_script_fetcher.cc
@@ -53,7 +53,7 @@
 
 bool DocumentModuleScriptFetcher::FetchIfLayeredAPI(
     FetchParameters& fetch_params) {
-  if (!RuntimeEnabledFeatures::LayeredAPIEnabled())
+  if (!RuntimeEnabledFeatures::BuiltInModuleInfraEnabled())
     return false;
 
   KURL layered_api_url = blink::layered_api::GetInternalURL(fetch_params.Url());
diff --git a/third_party/blink/renderer/core/loader/modulescript/module_tree_linker.cc b/third_party/blink/renderer/core/loader/modulescript/module_tree_linker.cc
index 6ac05a4..15a715d 100644
--- a/third_party/blink/renderer/core/loader/modulescript/module_tree_linker.cc
+++ b/third_party/blink/renderer/core/loader/modulescript/module_tree_linker.cc
@@ -170,7 +170,7 @@
   // href="https://github.com/drufball/layered-apis/blob/master/spec.md#fetch-a-module-script-graph"
   // step="1">Set url to the layered API fetching URL given url and the current
   // settings object's API base URL.</spec>
-  if (RuntimeEnabledFeatures::LayeredAPIEnabled())
+  if (RuntimeEnabledFeatures::BuiltInModuleInfraEnabled())
     url = blink::layered_api::ResolveFetchingURL(url);
 
 #if DCHECK_IS_ON()
diff --git a/third_party/blink/renderer/core/loader/resource/font_resource_test.cc b/third_party/blink/renderer/core/loader/resource/font_resource_test.cc
index 3c8d6ed..5659be4a 100644
--- a/third_party/blink/renderer/core/loader/resource/font_resource_test.cc
+++ b/third_party/blink/renderer/core/loader/resource/font_resource_test.cc
@@ -43,7 +43,7 @@
        ResourceFetcherRevalidateDeferedResourceFromTwoInitiators) {
   KURL url("http://127.0.0.1:8000/font.woff");
   ResourceResponse response(url);
-  response.SetHTTPStatusCode(200);
+  response.SetHttpStatusCode(200);
   response.SetHTTPHeaderField(http_names::kETag, "1234567890");
   Platform::Current()->GetURLLoaderMockFactory()->RegisterURL(
       url, WrappedResourceResponse(response), "");
@@ -103,7 +103,7 @@
 TEST_F(FontResourceTest, CacheAwareFontLoading) {
   KURL url("http://127.0.0.1:8000/font.woff");
   ResourceResponse response(url);
-  response.SetHTTPStatusCode(200);
+  response.SetHttpStatusCode(200);
   Platform::Current()->GetURLLoaderMockFactory()->RegisterURL(
       url, WrappedResourceResponse(response), "");
 
diff --git a/third_party/blink/renderer/core/loader/resource/image_resource_test.cc b/third_party/blink/renderer/core/loader/resource/image_resource_test.cc
index 999b65d2..6bbc749a 100644
--- a/third_party/blink/renderer/core/loader/resource/image_resource_test.cc
+++ b/third_party/blink/renderer/core/loader/resource/image_resource_test.cc
@@ -187,7 +187,7 @@
                      size_t data_size) {
   ResourceResponse resource_response(url);
   resource_response.SetMimeType(mime_type);
-  resource_response.SetHTTPStatusCode(200);
+  resource_response.SetHttpStatusCode(200);
   image_resource->NotifyStartLoad();
   image_resource->ResponseReceived(resource_response);
   image_resource->AppendData(data, data_size);
@@ -277,7 +277,7 @@
   resource_response.SetMimeType("image/jpeg");
   resource_response.SetExpectedContentLength(
       kJpegImageSubrangeWithDimensionsLength);
-  resource_response.SetHTTPStatusCode(206);
+  resource_response.SetHttpStatusCode(206);
   resource_response.SetHTTPHeaderField(
       "content-range", BuildContentRange(kJpegImageSubrangeWithDimensionsLength,
                                          sizeof(kJpegImage)));
@@ -1067,7 +1067,7 @@
 
   image_resource->SetRevalidatingRequest(ResourceRequest(url));
   ResourceResponse resource_response(url);
-  resource_response.SetHTTPStatusCode(304);
+  resource_response.SetHttpStatusCode(304);
 
   image_resource->ResponseReceived(resource_response);
 
@@ -1102,7 +1102,7 @@
 
   image_resource->SetRevalidatingRequest(ResourceRequest(url));
   ResourceResponse resource_response(url);
-  resource_response.SetHTTPStatusCode(304);
+  resource_response.SetHttpStatusCode(304);
   image_resource->ResponseReceived(resource_response);
 
   EXPECT_FALSE(image_resource->ErrorOccurred());
@@ -1358,7 +1358,7 @@
   partial_response.SetMimeType("image/jpeg");
   partial_response.SetExpectedContentLength(
       kJpegImageSubrangeWithoutDimensionsLength);
-  partial_response.SetHTTPStatusCode(206);
+  partial_response.SetHttpStatusCode(206);
   partial_response.SetHTTPHeaderField(
       "content-range",
       BuildContentRange(kJpegImageSubrangeWithoutDimensionsLength,
@@ -1483,7 +1483,7 @@
   ResourceResponse bad_response(test_url);
   bad_response.SetMimeType("image/jpeg");
   bad_response.SetExpectedContentLength(sizeof(kBadData));
-  bad_response.SetHTTPStatusCode(206);
+  bad_response.SetHttpStatusCode(206);
   bad_response.SetHTTPHeaderField(
       "content-range", BuildContentRange(sizeof(kBadData), sizeof(kJpegImage)));
 
@@ -1527,7 +1527,7 @@
   ResourceResponse bad_response(test_url);
   bad_response.SetMimeType("image/jpeg");
   bad_response.SetExpectedContentLength(sizeof(kBadData));
-  bad_response.SetHTTPStatusCode(206);
+  bad_response.SetHttpStatusCode(206);
   bad_response.SetHTTPHeaderField(
       "content-range", BuildContentRange(sizeof(kBadData), sizeof(kJpegImage)));
 
@@ -1591,7 +1591,7 @@
     partial_response.SetMimeType("image/jpeg");
     partial_response.SetExpectedContentLength(
         kJpegImageSubrangeWithoutDimensionsLength);
-    partial_response.SetHTTPStatusCode(206);
+    partial_response.SetHttpStatusCode(206);
     partial_response.SetHTTPHeaderField(
         "content-range",
         BuildContentRange(kJpegImageSubrangeWithoutDimensionsLength,
@@ -1741,7 +1741,7 @@
     ResourceResponse resource_response(test_url);
     resource_response.SetMimeType("imapge/jpeg");
     resource_response.SetExpectedContentLength(sizeof(kJpegImage));
-    resource_response.SetHTTPStatusCode(test.status_code);
+    resource_response.SetHttpStatusCode(test.status_code);
     if (test.content_range != g_null_atom)
       resource_response.SetHTTPHeaderField("content-range", test.content_range);
     image_resource->Loader()->DidReceiveResponse(
@@ -1802,7 +1802,7 @@
     ResourceResponse resource_response(test_url);
     resource_response.SetMimeType("image/jpeg");
     resource_response.SetExpectedContentLength(test.data_size);
-    resource_response.SetHTTPStatusCode(test.status_code);
+    resource_response.SetHttpStatusCode(test.status_code);
     if (test.content_range != g_null_atom)
       resource_response.SetHTTPHeaderField("content-range", test.content_range);
     image_resource->Loader()->DidReceiveResponse(
@@ -1838,7 +1838,7 @@
     ResourceResponse resource_response(test_url);
     resource_response.SetMimeType("image/jpeg");
     resource_response.SetExpectedContentLength(sizeof(kBadImageData));
-    resource_response.SetHTTPStatusCode(status_code);
+    resource_response.SetHttpStatusCode(status_code);
     image_resource->Loader()->DidReceiveResponse(
         WrappedResourceResponse(resource_response));
     image_resource->Loader()->DidReceiveData(kBadImageData,
diff --git a/third_party/blink/renderer/core/loader/threadable_loader_test.cc b/third_party/blink/renderer/core/loader/threadable_loader_test.cc
index 5cb0456..a082c596 100644
--- a/third_party/blink/renderer/core/loader/threadable_loader_test.cc
+++ b/third_party/blink/renderer/core/loader/threadable_loader_test.cc
@@ -119,7 +119,7 @@
 
   WebURLResponse response;
   response.SetCurrentRequestUrl(url);
-  response.SetHTTPStatusCode(301);
+  response.SetHttpStatusCode(301);
   response.SetLoadTiming(timing);
   response.AddHTTPHeaderField("Location", SuccessURL().GetString());
   response.AddHTTPHeaderField("Access-Control-Allow-Origin", "http://fake.url");
@@ -136,7 +136,7 @@
 
   WebURLResponse response;
   response.SetCurrentRequestUrl(url);
-  response.SetHTTPStatusCode(301);
+  response.SetHttpStatusCode(301);
   response.SetLoadTiming(timing);
   response.AddHTTPHeaderField("Location", RedirectLoopURL().GetString());
   response.AddHTTPHeaderField("Access-Control-Allow-Origin", "http://fake.url");
diff --git a/third_party/blink/renderer/core/page/chrome_client.cc b/third_party/blink/renderer/core/page/chrome_client.cc
index e6d554d..e630fd49 100644
--- a/third_party/blink/renderer/core/page/chrome_client.cc
+++ b/third_party/blink/renderer/core/page/chrome_client.cc
@@ -106,14 +106,10 @@
     SandboxFlags sandbox_flags,
     const FeaturePolicy::FeatureState& opener_feature_state,
     const SessionStorageNamespaceId& session_storage_namespace_id) {
-// Popups during page unloading is a feature being put behind a policy and
-// needing an easily-mergeable change. See https://crbug.com/936080 .
-#if 0
   if (!CanOpenUIElementIfDuringPageDismissal(
           frame->Tree().Top(), UIElementType::kPopup, g_empty_string)) {
     return nullptr;
   }
-#endif
 
   return CreateWindowDelegate(frame, r, features, navigation_policy,
                               sandbox_flags, opener_feature_state,
diff --git a/third_party/blink/renderer/core/page/chrome_client.h b/third_party/blink/renderer/core/page/chrome_client.h
index 91d3cd8..27de71c 100644
--- a/third_party/blink/renderer/core/page/chrome_client.h
+++ b/third_party/blink/renderer/core/page/chrome_client.h
@@ -139,7 +139,7 @@
   virtual bool HadFormInteraction() const = 0;
 
   virtual void BeginLifecycleUpdates() = 0;
-  virtual void StartDeferringCommits() = 0;
+  virtual void StartDeferringCommits(base::TimeDelta timeout) = 0;
   virtual void StopDeferringCommits() = 0;
 
   // Start a system drag and drop operation.
diff --git a/third_party/blink/renderer/core/page/chrome_client_impl.cc b/third_party/blink/renderer/core/page/chrome_client_impl.cc
index f21bf5e..15910249 100644
--- a/third_party/blink/renderer/core/page/chrome_client_impl.cc
+++ b/third_party/blink/renderer/core/page/chrome_client_impl.cc
@@ -840,6 +840,13 @@
     UIElementType ui_element_type,
     const String& dialog_message,
     Document::PageDismissalType dismissal_type) const {
+  // TODO(https://crbug.com/937569): Remove this in Chrome 82.
+  if (ui_element_type == ChromeClient::UIElementType::kPopup &&
+      web_view_->Client() &&
+      web_view_->Client()->AllowPopupsDuringPageUnload()) {
+    return true;
+  }
+
   StringBuilder builder;
   builder.Append("Blocked ");
   builder.Append(UIElementTypeToString(ui_element_type));
@@ -938,8 +945,8 @@
     web_view_->WidgetClient()->ScheduleAnimation();
 }
 
-void ChromeClientImpl::StartDeferringCommits() {
-  web_view_->StartDeferringCommits();
+void ChromeClientImpl::StartDeferringCommits(base::TimeDelta timeout) {
+  web_view_->StartDeferringCommits(timeout);
 }
 
 void ChromeClientImpl::StopDeferringCommits() {
diff --git a/third_party/blink/renderer/core/page/chrome_client_impl.h b/third_party/blink/renderer/core/page/chrome_client_impl.h
index 7aa0867..d3ae2e0 100644
--- a/third_party/blink/renderer/core/page/chrome_client_impl.h
+++ b/third_party/blink/renderer/core/page/chrome_client_impl.h
@@ -68,7 +68,7 @@
   void TakeFocus(WebFocusType) override;
   void FocusedNodeChanged(Node* from_node, Node* to_node) override;
   void BeginLifecycleUpdates() override;
-  void StartDeferringCommits() override;
+  void StartDeferringCommits(base::TimeDelta timeout) override;
   void StopDeferringCommits() override;
   bool HadFormInteraction() const override;
   void StartDragging(LocalFrame*,
diff --git a/third_party/blink/renderer/core/paint/inline_painter.cc b/third_party/blink/renderer/core/paint/inline_painter.cc
index c410d15..490a7efe 100644
--- a/third_party/blink/renderer/core/paint/inline_painter.cc
+++ b/third_party/blink/renderer/core/paint/inline_painter.cc
@@ -7,6 +7,7 @@
 #include "third_party/blink/renderer/core/layout/layout_block_flow.h"
 #include "third_party/blink/renderer/core/layout/layout_inline.h"
 #include "third_party/blink/renderer/core/paint/line_box_list_painter.h"
+#include "third_party/blink/renderer/core/paint/ng/ng_inline_box_fragment_painter.h"
 #include "third_party/blink/renderer/core/paint/ng/ng_paint_fragment.h"
 #include "third_party/blink/renderer/core/paint/object_painter.h"
 #include "third_party/blink/renderer/core/paint/paint_info.h"
@@ -20,23 +21,23 @@
   auto paint_offset = paint_state.PaintOffset();
   const auto& local_paint_info = paint_state.GetPaintInfo();
 
-  if (RuntimeEnabledFeatures::LayoutNGEnabled()) {
-    // Inline box with self painting layer is painted in this code path.
-    if (auto* block_flow = layout_inline_.ContainingNGBlockFlow()) {
-      if (auto* block_flow_fragment = block_flow->PaintFragment()) {
-        block_flow_fragment->PaintInlineBoxForDescendants(
-            local_paint_info, paint_offset, &layout_inline_);
-        return;
-      }
-    }
-  }
-
   if (local_paint_info.phase == PaintPhase::kForeground &&
       local_paint_info.IsPrinting()) {
     ObjectPainter(layout_inline_)
         .AddPDFURLRectIfNeeded(local_paint_info, paint_offset);
   }
 
+  if (layout_inline_.IsInLayoutNGInlineFormattingContext()) {
+    for (const NGPaintFragment* fragment :
+         NGPaintFragment::InlineFragmentsFor(&layout_inline_)) {
+      NGInlineBoxFragmentPainter(*fragment).Paint(
+          paint_info, paint_offset + (fragment->InlineOffsetToContainerBox() -
+                                      fragment->Offset())
+                                         .ToLayoutPoint());
+    }
+    return;
+  }
+
   if (ShouldPaintSelfOutline(local_paint_info.phase) ||
       ShouldPaintDescendantOutlines(local_paint_info.phase)) {
     ObjectPainter painter(layout_inline_);
diff --git a/third_party/blink/renderer/core/paint/link_highlight_impl.cc b/third_party/blink/renderer/core/paint/link_highlight_impl.cc
index 7f91b721..7293c6d 100644
--- a/third_party/blink/renderer/core/paint/link_highlight_impl.cc
+++ b/third_party/blink/renderer/core/paint/link_highlight_impl.cc
@@ -116,6 +116,7 @@
   state.local_transform_space = &TransformPaintPropertyNode::Root();
   state.compositor_element_id = element_id_;
   state.direct_compositing_reasons = CompositingReason::kActiveOpacityAnimation;
+  state.is_running_opacity_animation_on_compositor = true;
   effect_ = EffectPaintPropertyNode::Create(EffectPaintPropertyNode::Root(),
                                             std::move(state));
 #if DCHECK_IS_ON()
diff --git a/third_party/blink/renderer/core/paint/link_highlight_impl_test.cc b/third_party/blink/renderer/core/paint/link_highlight_impl_test.cc
index 267c842..4270a73f 100644
--- a/third_party/blink/renderer/core/paint/link_highlight_impl_test.cc
+++ b/third_party/blink/renderer/core/paint/link_highlight_impl_test.cc
@@ -334,7 +334,7 @@
   // node.
   EXPECT_EQ(highlight->Effect().GetCompositorElementId(),
             highlight->ElementIdForTesting());
-  EXPECT_TRUE(highlight->Effect().RequiresCompositingForAnimation());
+  EXPECT_TRUE(highlight->Effect().IsRunningOpacityAnimationOnCompositor());
 
   touch_node->remove(IGNORE_EXCEPTION_FOR_TESTING);
   UpdateAllLifecyclePhases();
@@ -381,7 +381,7 @@
   // node.
   const auto& effect = highlight->Effect();
   EXPECT_EQ(effect.GetCompositorElementId(), highlight->ElementIdForTesting());
-  EXPECT_TRUE(effect.RequiresCompositingForAnimation());
+  EXPECT_TRUE(effect.IsRunningOpacityAnimationOnCompositor());
 
   const auto& first_fragment = touch_node->GetLayoutObject()->FirstFragment();
   const auto* second_fragment = first_fragment.NextFragment();
diff --git a/third_party/blink/renderer/core/paint/ng/ng_paint_fragment.cc b/third_party/blink/renderer/core/paint/ng/ng_paint_fragment.cc
index b23de40..5cfbc3b 100644
--- a/third_party/blink/renderer/core/paint/ng/ng_paint_fragment.cc
+++ b/third_party/blink/renderer/core/paint/ng/ng_paint_fragment.cc
@@ -29,7 +29,6 @@
 #include "third_party/blink/renderer/core/layout/ng/ng_physical_container_fragment.h"
 #include "third_party/blink/renderer/core/layout/ng/ng_physical_fragment.h"
 #include "third_party/blink/renderer/core/paint/ng/ng_box_fragment_painter.h"
-#include "third_party/blink/renderer/core/paint/ng/ng_inline_box_fragment_painter.h"
 #include "third_party/blink/renderer/core/paint/ng/ng_paint_fragment_traversal.h"
 
 namespace blink {
@@ -588,24 +587,6 @@
   }
 }
 
-void NGPaintFragment::PaintInlineBoxForDescendants(
-    const PaintInfo& paint_info,
-    const LayoutPoint& paint_offset,
-    const LayoutInline* layout_object,
-    NGPhysicalOffset offset) const {
-  DCHECK(layout_object);
-  for (const NGPaintFragment* child : Children()) {
-    if (child->GetLayoutObject() == layout_object) {
-      NGInlineBoxFragmentPainter(*child).Paint(
-          paint_info, paint_offset + offset.ToLayoutPoint() /*, paint_offset*/);
-      continue;
-    }
-
-    child->PaintInlineBoxForDescendants(paint_info, paint_offset, layout_object,
-                                        offset + child->Offset());
-  }
-}
-
 const NGPaintFragment* NGPaintFragment::ContainerLineBox() const {
   DCHECK(PhysicalFragment().IsInline());
   for (const NGPaintFragment* fragment :
diff --git a/third_party/blink/renderer/core/paint/ng/ng_paint_fragment.h b/third_party/blink/renderer/core/paint/ng/ng_paint_fragment.h
index 88f6450..4bf2e757 100644
--- a/third_party/blink/renderer/core/paint/ng/ng_paint_fragment.h
+++ b/third_party/blink/renderer/core/paint/ng/ng_paint_fragment.h
@@ -16,10 +16,8 @@
 
 namespace blink {
 
-class LayoutInline;
 class NGBlockBreakToken;
 struct LayoutSelectionStatus;
-struct PaintInfo;
 enum class NGOutlineType;
 
 // The NGPaintFragment contains a NGPhysicalFragment and geometry in the paint
@@ -189,13 +187,6 @@
   // this block-level fragment.
   void SetShouldDoFullPaintInvalidationForFirstLine();
 
-  // Paint all descendant inline box fragments that belong to the specified
-  // LayoutObject.
-  void PaintInlineBoxForDescendants(const PaintInfo&,
-                                    const LayoutPoint& paint_offset,
-                                    const LayoutInline*,
-                                    NGPhysicalOffset = {}) const;
-
   // DisplayItemClient methods.
   String DebugName() const override;
 
diff --git a/third_party/blink/renderer/core/paint/object_paint_properties.h b/third_party/blink/renderer/core/paint/object_paint_properties.h
index 616657ab..ecbcb70 100644
--- a/third_party/blink/renderer/core/paint/object_paint_properties.h
+++ b/third_party/blink/renderer/core/paint/object_paint_properties.h
@@ -44,21 +44,9 @@
     return base::WrapUnique(new ObjectPaintProperties());
   }
 
+#if DCHECK_IS_ON()
   ~ObjectPaintProperties() { DCHECK(!is_immutable_); }
-
-  class UpdateResult {
-    STACK_ALLOCATED();
-
-   public:
-    bool Unchanged() const { return result_ == kUnchanged; }
-    bool NewNodeCreated() const { return result_ == kNewNodeCreated; }
-
-   private:
-    friend class ObjectPaintProperties;
-    enum Result { kUnchanged, kValueChanged, kNewNodeCreated };
-    UpdateResult(Result r) : result_(r) {}
-    Result result_;
-  };
+#endif
 
 // The following defines 3 functions and one variable:
 // - Foo(): a getter for the property.
@@ -73,28 +61,30 @@
 #define ADD_NODE(type, function, variable)                                   \
  public:                                                                     \
   const type##PaintPropertyNode* function() const { return variable.get(); } \
-  UpdateResult Update##function(const type##PaintPropertyNode& parent,       \
-                                type##PaintPropertyNode::State&& state,      \
-                                bool is_parent_alias = false) {              \
-    auto result = is_parent_alias                                            \
-                      ? UpdateAlias(variable, parent)                        \
-                      : Update(variable, parent, std::move(state));          \
-    DCHECK(!is_immutable_ || result.Unchanged())                             \
-        << "Value changed while immutable. New state:\n"                     \
-        << *variable;                                                        \
-    return result;                                                           \
+  PaintPropertyChangeType Update##function(                                  \
+      const type##PaintPropertyNode& parent,                                 \
+      type##PaintPropertyNode::State&& state) {                              \
+    return Update(variable, parent, std::move(state));                       \
   }                                                                          \
-  bool Clear##function() {                                                   \
-    DCHECK(!is_immutable_ || !variable)                                      \
-        << "Value cleared while immutable. Old state:\n"                     \
-        << *variable;                                                        \
-    return Clear(variable);                                                  \
-  }                                                                          \
+  bool Clear##function() { return Clear(variable); }                         \
                                                                              \
  private:                                                                    \
   scoped_refptr<type##PaintPropertyNode> variable
   // (End of ADD_NODE definition)
 
+#define ADD_ALIAS_NODE(type, function, variable)                             \
+ public:                                                                     \
+  const type##PaintPropertyNode* function() const { return variable.get(); } \
+  PaintPropertyChangeType Update##function(                                  \
+      const type##PaintPropertyNode& parent) {                               \
+    return UpdateAlias(variable, parent);                                    \
+  }                                                                          \
+  bool Clear##function() { return Clear(variable); }                         \
+                                                                             \
+ private:                                                                    \
+  scoped_refptr<type##PaintPropertyNode> variable
+  // (End of ADD_ALIAS_NODE definition)
+
 #define ADD_TRANSFORM(function, variable) \
   ADD_NODE(Transform, function, variable)
 #define ADD_EFFECT(function, variable) ADD_NODE(Effect, function, variable)
@@ -138,7 +128,7 @@
   ADD_TRANSFORM(ReplacedContentTransform, replaced_content_transform_);
   ADD_TRANSFORM(ScrollTranslation, scroll_translation_);
   ADD_NODE(Scroll, Scroll, scroll_);
-  ADD_TRANSFORM(TransformIsolationNode, transform_isolation_node_);
+  ADD_ALIAS_NODE(Transform, TransformIsolationNode, transform_isolation_node_);
 
   // The hierarchy of the effect subtree created by a LayoutObject is as
   // follows:
@@ -169,7 +159,7 @@
   ADD_EFFECT(HorizontalScrollbarEffect, horizontal_scrollbar_effect_);
   ADD_EFFECT(Mask, mask_);
   ADD_EFFECT(ClipPath, clip_path_);
-  ADD_EFFECT(EffectIsolationNode, effect_isolation_node_);
+  ADD_ALIAS_NODE(Effect, EffectIsolationNode, effect_isolation_node_);
 
   // The hierarchy of the clip subtree created by a LayoutObject is as follows:
   // [ fragment clip ]
@@ -217,12 +207,13 @@
   ADD_CLIP(OverflowControlsClip, overflow_controls_clip_);
   ADD_CLIP(InnerBorderRadiusClip, inner_border_radius_clip_);
   ADD_CLIP(OverflowClip, overflow_clip_);
-  ADD_CLIP(ClipIsolationNode, clip_isolation_node_);
+  ADD_ALIAS_NODE(Clip, ClipIsolationNode, clip_isolation_node_);
 
 #undef ADD_CLIP
 #undef ADD_EFFECT
 #undef ADD_TRANSFORM
 #undef ADD_NODE
+#undef ADD_ALIAS_NODE
 
  public:
 #if DCHECK_IS_ON()
@@ -265,32 +256,56 @@
   // created), and false otherwise. See the class-level comment ("update & clear
   // implementation note") for details about why this is needed for efficiency.
   template <typename PaintPropertyNode>
-  UpdateResult Update(scoped_refptr<PaintPropertyNode>& field,
-                      const PaintPropertyNode& parent,
-                      typename PaintPropertyNode::State&& state) {
+  PaintPropertyChangeType Update(scoped_refptr<PaintPropertyNode>& field,
+                                 const PaintPropertyNode& parent,
+                                 typename PaintPropertyNode::State&& state) {
     if (field) {
-      return field->Update(parent, std::move(state))
-                 ? UpdateResult::kValueChanged
-                 : UpdateResult::kUnchanged;
+      auto changed = field->Update(parent, std::move(state));
+#if DCHECK_IS_ON()
+      DCHECK(!is_immutable_ || changed == PaintPropertyChangeType::kUnchanged ||
+             // TODO(crbug.com/937929): kChangedOnlyCompositedAnimationStatus is
+             // to workaround the situation that composited animation status
+             // changes without LayoutObject::SetStyle() being able to detect.
+             // Note that this may cause some false-negatives.
+             changed ==
+                 PaintPropertyChangeType::kChangedOnlyCompositedAnimationStatus)
+          << "Value changed while immutable. New state:\n"
+          << *field;
+#endif
+      return changed;
     }
     field = PaintPropertyNode::Create(parent, std::move(state));
-    return UpdateResult::kNewNodeCreated;
-  }
-  template <typename PaintPropertyNode>
-  UpdateResult UpdateAlias(scoped_refptr<PaintPropertyNode>& field,
-                           const PaintPropertyNode& parent) {
-    if (field) {
-      DCHECK(field->IsParentAlias());
-      return field->SetParent(&parent) ? UpdateResult::kValueChanged
-                                       : UpdateResult::kUnchanged;
-    }
-    field = PaintPropertyNode::CreateAlias(parent);
-    return UpdateResult::kNewNodeCreated;
+#if DCHECK_IS_ON()
+    DCHECK(!is_immutable_) << "Node added while immutable. New state:\n"
+                           << *field;
+#endif
+    return PaintPropertyChangeType::kNodeAddedOrRemoved;
   }
 
-  // This is used in DCHECKs only, but is not guarded by DCHECK_IS_ON() because
-  // we can't have a similar guard in a macro definition.
+  template <typename PaintPropertyNode>
+  PaintPropertyChangeType UpdateAlias(scoped_refptr<PaintPropertyNode>& field,
+                                      const PaintPropertyNode& parent) {
+    if (field) {
+      DCHECK(field->IsParentAlias());
+      auto changed = field->SetParent(&parent);
+#if DCHECK_IS_ON()
+      DCHECK(!is_immutable_ || changed == PaintPropertyChangeType::kUnchanged)
+          << "Parent changed while immutable. New state:\n"
+          << *field;
+#endif
+      return changed;
+    }
+    field = PaintPropertyNode::CreateAlias(parent);
+#if DCHECK_IS_ON()
+    DCHECK(!is_immutable_) << "Node added while immutable. New state:\n"
+                           << *field;
+#endif
+    return PaintPropertyChangeType::kNodeAddedOrRemoved;
+  }
+
+#if DCHECK_IS_ON()
   mutable bool is_immutable_ = false;
+#endif
 
   DISALLOW_COPY_AND_ASSIGN(ObjectPaintProperties);
 };
diff --git a/third_party/blink/renderer/core/paint/paint_property_tree_builder.cc b/third_party/blink/renderer/core/paint/paint_property_tree_builder.cc
index 149c753..2e062c0 100644
--- a/third_party/blink/renderer/core/paint/paint_property_tree_builder.cc
+++ b/third_party/blink/renderer/core/paint/paint_property_tree_builder.cc
@@ -137,7 +137,7 @@
         properties_(fragment_data.PaintProperties()) {}
 
   ~FragmentPaintPropertyTreeBuilder() {
-    if (property_changed_ >= PaintPropertyChangedState::kAddedOrRemoved) {
+    if (property_changed_ >= PaintPropertyChangeType::kNodeAddedOrRemoved) {
       // Tree topology changes are blocked by isolation.
       full_context_.force_subtree_update_reasons |=
           PaintPropertyTreeBuilderContext::kSubtreeUpdateIsolationBlocked;
@@ -151,9 +151,7 @@
   ALWAYS_INLINE void UpdateForSelf();
   ALWAYS_INLINE void UpdateForChildren();
 
-  PaintPropertyChangedState PropertyChanged() const {
-    return property_changed_;
-  }
+  PaintPropertyChangeType PropertyChanged() const { return property_changed_; }
   bool HasIsolationNodes() const {
     // All or nothing check on the isolation nodes.
     DCHECK(!properties_ ||
@@ -203,41 +201,30 @@
            full_context_.force_subtree_update_reasons;
   }
 
-  void OnUpdate(const ObjectPaintProperties::UpdateResult& result,
-                bool only_for_running_animation = false) {
-    if (!result.Unchanged()) {
-      if (result.NewNodeCreated()) {
-        property_changed_ = PaintPropertyChangedState::kAddedOrRemoved;
-      } else if (only_for_running_animation) {
-        property_changed_ =
-            std::max(property_changed_,
-                     PaintPropertyChangedState::kChangedOnlyDueToAnimations);
-      } else {
-        property_changed_ =
-            std::max(property_changed_, PaintPropertyChangedState::kChanged);
-      }
-    }
+  void OnUpdate(PaintPropertyChangeType change) {
+    property_changed_ = std::max(property_changed_, change);
   }
   // Like |OnUpdate| but sets |clip_changed| if the clip values change.
-  void OnUpdateClip(const ObjectPaintProperties::UpdateResult& result,
+  void OnUpdateClip(PaintPropertyChangeType change,
                     bool only_updated_hit_test_values = false) {
-    OnUpdate(result);
+    OnUpdate(change);
     full_context_.clip_changed |=
-        !(result.Unchanged() || only_updated_hit_test_values);
+        (change != PaintPropertyChangeType::kUnchanged &&
+         !only_updated_hit_test_values);
   }
   // Like |OnUpdate| but forces a piercing subtree update if the scroll tree
   // hierarchy changes because the scroll tree does not have isolation nodes
   // and non-piercing updates can fail to update scroll descendants.
-  void OnUpdateScroll(const ObjectPaintProperties::UpdateResult& result) {
-    OnUpdate(result);
-    if (result.NewNodeCreated()) {
+  void OnUpdateScroll(PaintPropertyChangeType change) {
+    OnUpdate(change);
+    if (change >= PaintPropertyChangeType::kNodeAddedOrRemoved) {
       full_context_.force_subtree_update_reasons |=
           PaintPropertyTreeBuilderContext::kSubtreeUpdateIsolationPiercing;
     }
   }
   void OnClear(bool cleared) {
     if (cleared) {
-      property_changed_ = PaintPropertyChangedState::kAddedOrRemoved;
+      property_changed_ = PaintPropertyChangeType::kNodeAddedOrRemoved;
     }
   }
   // See: |OnUpdateScroll|.
@@ -261,8 +248,8 @@
   PaintPropertyTreeBuilderFragmentContext& context_;
   FragmentData& fragment_data_;
   ObjectPaintProperties* properties_;
-  PaintPropertyChangedState property_changed_ =
-      PaintPropertyChangedState::kUnchanged;
+  PaintPropertyChangeType property_changed_ =
+      PaintPropertyChangeType::kUnchanged;
 };
 
 static bool IsRootScroller(const LayoutBox& box) {
@@ -714,21 +701,11 @@
             object_.UniqueId(),
             CompositorElementIdNamespace::kPrimaryTransform);
       }
+      state.is_running_animation_on_compositor =
+          style.IsRunningTransformAnimationOnCompositor();
 
-      // If nothing but the transform matrix changed, and animations are
-      // running, avoid setting property_changed.
-      bool other_properties_changed = true;
-      if (properties_->Transform()) {
-        other_properties_changed =
-            properties_->Transform()->HaveNonAnimatingPropertiesChanged(
-                *context_.current.transform, state);
-      }
-      bool running_animation =
-          style.IsRunningTransformAnimationOnCompositor() &&
-          !other_properties_changed;
       OnUpdate(properties_->UpdateTransform(*context_.current.transform,
-                                            std::move(state)),
-               running_animation);
+                                            std::move(state)));
     } else {
       OnClear(properties_->ClearTransform());
     }
@@ -975,22 +952,13 @@
               object_.UniqueId(), CompositorElementIdNamespace::kPrimary);
         }
       }
+      state.is_running_opacity_animation_on_compositor =
+          style.IsRunningOpacityAnimationOnCompositor();
+      state.is_running_backdrop_filter_animation_on_compositor =
+          style.IsRunningBackdropFilterAnimationOnCompositor();
 
-      // If nothing but the opacity and/or backdrop-filter changed, and
-      // animations are running, avoid setting property_changed.
-      bool other_properties_changed = true;
-      if (properties_->Effect()) {
-        other_properties_changed =
-            properties_->Effect()->HaveNonAnimatingPropertiesChanged(
-                *context_.current_effect, state);
-      }
-      bool running_animation =
-          (style.IsRunningOpacityAnimationOnCompositor() ||
-           style.IsRunningBackdropFilterAnimationOnCompositor()) &&
-          !other_properties_changed;
-      OnUpdate(
-          properties_->UpdateEffect(*context_.current_effect, std::move(state)),
-          running_animation);
+      OnUpdate(properties_->UpdateEffect(*context_.current_effect,
+                                         std::move(state)));
 
       if (mask_clip || has_spv1_composited_clip_path) {
         EffectPaintPropertyNode::State mask_state;
@@ -1134,20 +1102,11 @@
         state.compositor_element_id = CompositorElementIdFromUniqueObjectId(
             object_.UniqueId(), CompositorElementIdNamespace::kEffectFilter);
       }
+      state.is_running_filter_animation_on_compositor =
+          style.IsRunningFilterAnimationOnCompositor();
 
-      // If nothing but the filter changed, and animations are running, avoid
-      // setting property_changed.
-      bool other_properties_changed = true;
-      if (properties_->Filter()) {
-        other_properties_changed =
-            properties_->Filter()->HaveNonAnimatingPropertiesChanged(
-                *context_.current_effect, state);
-      }
-      bool running_animation = style.IsRunningFilterAnimationOnCompositor() &&
-                               !other_properties_changed;
-      OnUpdate(
-          properties_->UpdateFilter(*context_.current_effect, std::move(state)),
-          running_animation);
+      OnUpdate(properties_->UpdateFilter(*context_.current_effect,
+                                         std::move(state)));
     } else {
       OnClear(properties_->ClearFilter());
     }
@@ -1319,7 +1278,7 @@
 
     if (!fragment_data_.HasLocalBorderBoxProperties() ||
         local_border_box != fragment_data_.LocalBorderBoxProperties())
-      property_changed_ = PaintPropertyChangedState::kAddedOrRemoved;
+      property_changed_ = PaintPropertyChangeType::kNodeAddedOrRemoved;
 
     fragment_data_.SetLocalBorderBoxProperties(std::move(local_border_box));
   } else {
@@ -1891,8 +1850,7 @@
   if (NeedsPaintPropertyUpdate()) {
     if (NeedsIsolationNodes(object_)) {
       OnUpdate(properties_->UpdateTransformIsolationNode(
-          *context_.current.transform, TransformPaintPropertyNode::State{},
-          true /* is_parent_alias */));
+          *context_.current.transform));
     } else {
       OnClear(properties_->ClearTransformIsolationNode());
     }
@@ -1904,9 +1862,8 @@
 void FragmentPaintPropertyTreeBuilder::UpdateEffectIsolationNode() {
   if (NeedsPaintPropertyUpdate()) {
     if (NeedsIsolationNodes(object_)) {
-      OnUpdate(properties_->UpdateEffectIsolationNode(
-          *context_.current_effect, EffectPaintPropertyNode::State{},
-          true /* is_parent_alias */));
+      OnUpdate(
+          properties_->UpdateEffectIsolationNode(*context_.current_effect));
     } else {
       OnClear(properties_->ClearEffectIsolationNode());
     }
@@ -1918,9 +1875,7 @@
 void FragmentPaintPropertyTreeBuilder::UpdateClipIsolationNode() {
   if (NeedsPaintPropertyUpdate()) {
     if (NeedsIsolationNodes(object_)) {
-      OnUpdate(properties_->UpdateClipIsolationNode(
-          *context_.current.clip, ClipPaintPropertyNode::State{},
-          true /* is_parent_alias */));
+      OnUpdate(properties_->UpdateClipIsolationNode(*context_.current.clip));
     } else {
       OnClear(properties_->ClearClipIsolationNode());
     }
@@ -3104,18 +3059,18 @@
   DCHECK(context_.painting_layer == object_.PaintingLayer());
 }
 
-PaintPropertyChangedState PaintPropertyTreeBuilder::UpdateForSelf() {
+PaintPropertyChangeType PaintPropertyTreeBuilder::UpdateForSelf() {
   // This is not inherited from the parent context and we always recalculate it.
   context_.direct_compositing_reasons =
       CompositingReasonFinder::DirectReasonsForPaintProperties(object_);
 
   UpdatePaintingLayer();
 
-  PaintPropertyChangedState property_changed =
-      PaintPropertyChangedState::kUnchanged;
+  PaintPropertyChangeType property_changed =
+      PaintPropertyChangeType::kUnchanged;
   if (ObjectTypeMightNeedPaintProperties()) {
     if (UpdateFragments())
-      property_changed = PaintPropertyChangedState::kAddedOrRemoved;
+      property_changed = PaintPropertyChangeType::kNodeAddedOrRemoved;
   } else {
     DCHECK_EQ(context_.direct_compositing_reasons, CompositingReason::kNone);
     object_.GetMutableForPainting().FirstFragment().ClearNextFragment();
@@ -3132,16 +3087,16 @@
   DCHECK(!fragment_data);
 
   // We need to update property tree states of paint chunks.
-  if (property_changed >= PaintPropertyChangedState::kAddedOrRemoved) {
+  if (property_changed >= PaintPropertyChangeType::kNodeAddedOrRemoved) {
     context_.painting_layer->SetNeedsRepaint();
   }
 
   return property_changed;
 }
 
-PaintPropertyChangedState PaintPropertyTreeBuilder::UpdateForChildren() {
-  PaintPropertyChangedState property_changed =
-      PaintPropertyChangedState::kUnchanged;
+PaintPropertyChangeType PaintPropertyTreeBuilder::UpdateForChildren() {
+  PaintPropertyChangeType property_changed =
+      PaintPropertyChangeType::kUnchanged;
   if (!ObjectTypeMightNeedPaintProperties())
     return property_changed;
 
@@ -3187,7 +3142,7 @@
   }
 
   // We need to update property tree states of paint chunks.
-  if (property_changed >= PaintPropertyChangedState::kAddedOrRemoved)
+  if (property_changed >= PaintPropertyChangeType::kNodeAddedOrRemoved)
     context_.painting_layer->SetNeedsRepaint();
 
   return property_changed;
diff --git a/third_party/blink/renderer/core/paint/paint_property_tree_builder.h b/third_party/blink/renderer/core/paint/paint_property_tree_builder.h
index 35cf622..db959aa8 100644
--- a/third_party/blink/renderer/core/paint/paint_property_tree_builder.h
+++ b/third_party/blink/renderer/core/paint/paint_property_tree_builder.h
@@ -191,16 +191,6 @@
   static void Update(VisualViewport&, PaintPropertyTreeBuilderContext&);
 };
 
-// Used to report whether paint properties have changed, and if so, whether
-// it was only due to animations. The order is important - it must go from
-// no change to full change.
-enum class PaintPropertyChangedState {
-  kUnchanged,
-  kChangedOnlyDueToAnimations,
-  kChanged,
-  kAddedOrRemoved,
-};
-
 // Creates paint property tree nodes for non-local effects in the layout tree.
 // Non-local effects include but are not limited to: overflow clip, transform,
 // fixed-pos, animation, mask, filters, etc. It expects to be invoked for each
@@ -220,12 +210,12 @@
   // paint offset translation) and ensure the context is up to date. Also
   // handles updating the object's paintOffset.
   // Returns whether any paint property of the object has changed.
-  PaintPropertyChangedState UpdateForSelf();
+  PaintPropertyChangeType UpdateForSelf();
 
   // Update the paint properties that affect children of this object (e.g.,
   // scroll offset transform) and ensure the context is up to date.
   // Returns whether any paint property of the object has changed.
-  PaintPropertyChangedState UpdateForChildren();
+  PaintPropertyChangeType UpdateForChildren();
 
  private:
   ALWAYS_INLINE void InitFragmentPaintProperties(
diff --git a/third_party/blink/renderer/core/paint/paint_property_tree_builder_test.cc b/third_party/blink/renderer/core/paint/paint_property_tree_builder_test.cc
index c7ab3d85..8de79dd 100644
--- a/third_party/blink/renderer/core/paint/paint_property_tree_builder_test.cc
+++ b/third_party/blink/renderer/core/paint/paint_property_tree_builder_test.cc
@@ -4917,7 +4917,7 @@
   EXPECT_TRUE(properties->Transform());
   EXPECT_NE(CompositorElementId(),
             properties->Transform()->GetCompositorElementId());
-  EXPECT_TRUE(properties->Transform()->RequiresCompositingForAnimation());
+  EXPECT_TRUE(properties->Transform()->HasActiveTransformAnimation());
 }
 
 TEST_P(PaintPropertyTreeBuilderTest, EffectNodeAnimatedHasCompositorElementId) {
@@ -4929,7 +4929,7 @@
   EXPECT_TRUE(properties->Effect());
   EXPECT_NE(CompositorElementId(),
             properties->Effect()->GetCompositorElementId());
-  EXPECT_TRUE(properties->Effect()->RequiresCompositingForAnimation());
+  EXPECT_TRUE(properties->Effect()->HasActiveOpacityAnimation());
 }
 
 TEST_P(PaintPropertyTreeBuilderTest, FloatUnderInline) {
diff --git a/third_party/blink/renderer/core/paint/paint_property_tree_update_tests.cc b/third_party/blink/renderer/core/paint/paint_property_tree_update_tests.cc
index ac41904..1d2cdf0 100644
--- a/third_party/blink/renderer/core/paint/paint_property_tree_update_tests.cc
+++ b/third_party/blink/renderer/core/paint/paint_property_tree_update_tests.cc
@@ -9,6 +9,7 @@
 #include "third_party/blink/renderer/core/page/focus_controller.h"
 #include "third_party/blink/renderer/core/paint/paint_property_tree_builder_test.h"
 #include "third_party/blink/renderer/core/paint/paint_property_tree_printer.h"
+#include "third_party/blink/renderer/platform/graphics/compositing/paint_artifact_compositor.h"
 #include "third_party/blink/renderer/platform/graphics/paint/geometry_mapper.h"
 
 namespace blink {
@@ -1114,7 +1115,7 @@
   UpdateAllLifecyclePhasesForTest();
   if (RuntimeEnabledFeatures::CompositeAfterPaintEnabled()) {
     EXPECT_TRUE(transform->HasDirectCompositingReasons());
-    EXPECT_TRUE(transform->RequiresCompositingForAnimation());
+    EXPECT_TRUE(transform->HasActiveTransformAnimation());
   }
   // TODO(flackr): After https://crbug.com/900241 is fixed the filter effect
   // should no longer have direct compositing reasons due to the animation.
@@ -1126,11 +1127,11 @@
   // The transform animation still continues.
   if (RuntimeEnabledFeatures::CompositeAfterPaintEnabled()) {
     EXPECT_TRUE(transform->HasDirectCompositingReasons());
-    EXPECT_TRUE(transform->RequiresCompositingForAnimation());
+    EXPECT_TRUE(transform->HasActiveTransformAnimation());
     // The filter node should have correct direct compositing reasons, not
     // shadowed by the transform animation.
     EXPECT_TRUE(filter->HasDirectCompositingReasons());
-    EXPECT_TRUE(filter->RequiresCompositingForAnimation());
+    EXPECT_TRUE(transform->HasActiveTransformAnimation());
   }
 }
 
@@ -1619,4 +1620,76 @@
   }
 }
 
+TEST_P(PaintPropertyTreeUpdateTest, ChangeDuringAnimation) {
+  if (!RuntimeEnabledFeatures::CompositeAfterPaintEnabled() &&
+      !RuntimeEnabledFeatures::BlinkGenPropertyTreesEnabled())
+    return;
+
+  SetBodyInnerHTML("<div id='target' style='width: 100px; height: 100px'>");
+  auto* target = GetLayoutObjectByElementId("target");
+  auto style = ComputedStyle::Clone(target->StyleRef());
+  GetDocument().Lifecycle().AdvanceTo(DocumentLifecycle::kInStyleRecalc);
+  // Simulates starting a composite animation.
+  style->SetHasCurrentTransformAnimation(true);
+  style->SetIsRunningTransformAnimationOnCompositor(true);
+  target->SetStyle(std::move(style));
+  EXPECT_TRUE(target->NeedsPaintPropertyUpdate());
+  GetDocument().Lifecycle().AdvanceTo(DocumentLifecycle::kStyleClean);
+  GetDocument().View()->UpdateAllLifecyclePhasesExceptPaint();
+
+  const auto* transform_node =
+      target->FirstFragment().PaintProperties()->Transform();
+  ASSERT_TRUE(transform_node);
+  EXPECT_TRUE(transform_node->IsRunningAnimationOnCompositor());
+  EXPECT_EQ(TransformationMatrix(), transform_node->Matrix());
+  EXPECT_EQ(FloatPoint3D(50, 50, 0), transform_node->Origin());
+  // Change of animation status should update PaintArtifactCompositor.
+  auto* paint_artifact_compositor =
+      GetDocument().View()->GetPaintArtifactCompositorForTesting();
+  EXPECT_TRUE(paint_artifact_compositor->NeedsUpdate());
+  // PaintArtifactCompositor can't clear the NeedsUpdate flag by itself when
+  // there is no cc::LayerTreeHost.
+  paint_artifact_compositor->ClearNeedsUpdateForTesting();
+
+  // Simulates changing transform during animation.
+  GetDocument().Lifecycle().AdvanceTo(DocumentLifecycle::kInStyleRecalc);
+  style = ComputedStyle::Clone(target->StyleRef());
+  TransformOperations transform;
+  transform.Operations().push_back(
+      RotateTransformOperation::Create(10, TransformOperation::kRotate));
+  style->SetTransform(transform);
+  target->SetStyle(std::move(style));
+  EXPECT_TRUE(target->NeedsPaintPropertyUpdate());
+  GetDocument().Lifecycle().AdvanceTo(DocumentLifecycle::kStyleClean);
+  GetDocument().View()->UpdateAllLifecyclePhasesExceptPaint();
+
+  ASSERT_EQ(transform_node,
+            target->FirstFragment().PaintProperties()->Transform());
+  EXPECT_TRUE(transform_node->IsRunningAnimationOnCompositor());
+  EXPECT_EQ(TransformationMatrix().Rotate(10), transform_node->Matrix());
+  EXPECT_EQ(FloatPoint3D(50, 50, 0), transform_node->Origin());
+  // Only transform value change during composited animation should not schedule
+  // PaintArtifactCompositor update.
+  EXPECT_FALSE(paint_artifact_compositor->NeedsUpdate());
+
+  // Simulates changing transform origin during animation.
+  GetDocument().Lifecycle().AdvanceTo(DocumentLifecycle::kInStyleRecalc);
+  style = ComputedStyle::Clone(target->StyleRef());
+  style->SetTransformOrigin(TransformOrigin(Length(70, Length::kFixed),
+                                            Length(30, Length::kFixed), 0));
+  target->SetStyle(std::move(style));
+  EXPECT_TRUE(target->NeedsPaintPropertyUpdate());
+  GetDocument().Lifecycle().AdvanceTo(DocumentLifecycle::kStyleClean);
+  GetDocument().View()->UpdateAllLifecyclePhasesExceptPaint();
+
+  ASSERT_EQ(transform_node,
+            target->FirstFragment().PaintProperties()->Transform());
+  EXPECT_TRUE(transform_node->IsRunningAnimationOnCompositor());
+  EXPECT_EQ(TransformationMatrix().Rotate(10), transform_node->Matrix());
+  EXPECT_EQ(FloatPoint3D(70, 30, 0), transform_node->Origin());
+  // Only transform value change during composited animation should not schedule
+  // PaintArtifactCompositor update.
+  EXPECT_TRUE(paint_artifact_compositor->NeedsUpdate());
+}
+
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/paint/pre_paint_tree_walk.cc b/third_party/blink/renderer/core/paint/pre_paint_tree_walk.cc
index 89f9991..5ba5b20 100644
--- a/third_party/blink/renderer/core/paint/pre_paint_tree_walk.cc
+++ b/third_party/blink/renderer/core/paint/pre_paint_tree_walk.cc
@@ -299,14 +299,14 @@
   UpdateAuxiliaryObjectProperties(object, context);
 
   base::Optional<PaintPropertyTreeBuilder> property_tree_builder;
-  PaintPropertyChangedState property_changed =
-      PaintPropertyChangedState::kUnchanged;
+  PaintPropertyChangeType property_changed =
+      PaintPropertyChangeType::kUnchanged;
   if (context.tree_builder_context) {
     property_tree_builder.emplace(object, *context.tree_builder_context);
     property_changed =
         std::max(property_changed, property_tree_builder->UpdateForSelf());
 
-    if ((property_changed > PaintPropertyChangedState::kUnchanged) &&
+    if ((property_changed > PaintPropertyChangeType::kUnchanged) &&
         !context.tree_builder_context
              ->supports_composited_raster_invalidation) {
       paint_invalidator_context.subtree_flags |=
@@ -334,15 +334,15 @@
     if (context.tree_builder_context->clip_changed)
       context.clip_changed = true;
 
-    if (property_changed > PaintPropertyChangedState::kUnchanged) {
+    if (property_changed != PaintPropertyChangeType::kUnchanged) {
       if (property_changed >
-          PaintPropertyChangedState::kChangedOnlyDueToAnimations) {
+          PaintPropertyChangeType::kChangedOnlyCompositedAnimationValues) {
         object.GetFrameView()->SetPaintArtifactCompositorNeedsUpdate();
       }
 
       if (!RuntimeEnabledFeatures::CompositeAfterPaintEnabled()) {
         if (property_changed >
-            PaintPropertyChangedState::kChangedOnlyDueToAnimations) {
+            PaintPropertyChangeType::kChangedOnlyCompositedAnimationValues) {
           const auto* paint_invalidation_layer =
               paint_invalidator_context.paint_invalidation_container->Layer();
           if (!paint_invalidation_layer->NeedsRepaint()) {
diff --git a/third_party/blink/renderer/core/paint/theme_painter.cc b/third_party/blink/renderer/core/paint/theme_painter.cc
index e4cd32a8..2aed9cf 100644
--- a/third_party/blink/renderer/core/paint/theme_painter.cc
+++ b/third_party/blink/renderer/core/paint/theme_painter.cc
@@ -21,6 +21,7 @@
 
 #include "third_party/blink/renderer/core/paint/theme_painter.h"
 
+#include "build/build_config.h"
 #include "third_party/blink/public/platform/platform.h"
 #include "third_party/blink/public/platform/web_rect.h"
 #include "third_party/blink/renderer/core/frame/local_frame_view.h"
@@ -61,6 +62,33 @@
   return ui::NativeTheme::kNormal;
 }
 
+bool IsTemporalInput(const AtomicString& type) {
+  return type == input_type_names::kDate ||
+         type == input_type_names::kDatetimeLocal ||
+         type == input_type_names::kMonth || type == input_type_names::kTime ||
+         type == input_type_names::kWeek;
+}
+
+bool IsMenulistInput(const Node* node) {
+  if (auto* input = ToHTMLInputElement(node)) {
+#if defined(OS_ANDROID)
+    if (IsTemporalInput(input->type()))
+      return true;
+#endif
+    return input->type() == input_type_names::kColor &&
+           input->FastHasAttribute(html_names::kListAttr);
+  }
+  return false;
+}
+
+bool IsMultipleFieldsTemporalInput(const AtomicString& type) {
+#if !defined(OS_ANDROID)
+  return IsTemporalInput(type);
+#else
+  return false;
+#endif
+}
+
 }  // anonymous namespace
 
 ThemePainter::ThemePainter() = default;
@@ -140,7 +168,7 @@
     }
     case kMenulistPart:
       COUNT_APPEARANCE(doc, MenuList);
-      if (!IsHTMLSelectElement(node))
+      if (!IsHTMLSelectElement(node) && !IsMenulistInput(node))
         COUNT_APPEARANCE(doc, MenuListForOthers);
       return PaintMenuList(node, o.GetDocument(), style, paint_info, r);
     case kMeterPart:
@@ -235,17 +263,20 @@
       if (node) {
         UseCounter::Count(node->GetDocument(),
                           WebFeature::kCSSValueAppearanceTextFieldRendered);
+        WebFeature feature =
+            WebFeature::kCSSValueAppearanceTextFieldForOthersRendered;
         if (auto* input = ToHTMLInputElementOrNull(node)) {
-          if (input->type() == input_type_names::kSearch) {
-            UseCounter::Count(
-                node->GetDocument(),
-                WebFeature::kCSSValueAppearanceTextFieldForSearch);
+          const AtomicString& type = input->type();
+          if (type == input_type_names::kSearch) {
+            feature = WebFeature::kCSSValueAppearanceTextFieldForSearch;
           } else if (input->IsTextField()) {
-            UseCounter::Count(
-                node->GetDocument(),
-                WebFeature::kCSSValueAppearanceTextFieldForTextField);
+            feature = WebFeature::kCSSValueAppearanceTextFieldForTextField;
+          } else if (IsMultipleFieldsTemporalInput(type)) {
+            feature =
+                WebFeature::kCSSValueAppearanceTextFieldForTemporalRendered;
           }
         }
+        UseCounter::Count(node->GetDocument(), feature);
       }
       return PaintTextField(node, style, paint_info, r);
     case kTextAreaPart:
@@ -298,7 +329,7 @@
   switch (style.Appearance()) {
     case kMenulistButtonPart:
       COUNT_APPEARANCE(document, MenuListButton);
-      if (!IsHTMLSelectElement(node))
+      if (!IsHTMLSelectElement(node) && !IsMenulistInput(node))
         COUNT_APPEARANCE(document, MenuListButtonForOthers);
       return PaintMenuListButton(node, document, style, paint_info, r);
     case kTextFieldPart:
diff --git a/third_party/blink/renderer/core/scheduler/frame_throttling_test.cc b/third_party/blink/renderer/core/scheduler/frame_throttling_test.cc
index 2c9079c..bb1910a 100644
--- a/third_party/blink/renderer/core/scheduler/frame_throttling_test.cc
+++ b/third_party/blink/renderer/core/scheduler/frame_throttling_test.cc
@@ -1041,57 +1041,21 @@
       "<iframe id=child-frame sandbox src=child-iframe.html></iframe>");
   child_frame_resource.Complete("");
 
-  // Move both frames offscreen, but don't run the intersection observers yet.
+  // Move both frames offscreen. IntersectionObservers will run during
+  // post-lifecycle steps and synchronously update throttling status.
   auto* frame_element =
       ToHTMLIFrameElement(GetDocument().getElementById("frame"));
   auto* child_frame_element = ToHTMLIFrameElement(
       frame_element->contentDocument()->getElementById("child-frame"));
   frame_element->setAttribute(kStyleAttr, "transform: translateY(480px)");
   Compositor().BeginFrame();
-  EXPECT_FALSE(
-      frame_element->contentDocument()->View()->CanThrottleRendering());
-  EXPECT_FALSE(
-      child_frame_element->contentDocument()->View()->CanThrottleRendering());
-
-  // Only run the intersection observer for the parent frame. Both frames
-  // should immediately become throttled. This simulates the case where a task
-  // such as BeginMainFrame runs in the middle of dispatching intersection
-  // observer notifications.
-  frame_element->contentDocument()
-      ->View()
-      ->UpdateRenderThrottlingStatusForTesting();
   EXPECT_TRUE(frame_element->contentDocument()->View()->CanThrottleRendering());
   EXPECT_TRUE(
       child_frame_element->contentDocument()->View()->CanThrottleRendering());
 
-  // Both frames should still be throttled after the second notification.
-  child_frame_element->contentDocument()
-      ->View()
-      ->UpdateRenderThrottlingStatusForTesting();
-  EXPECT_TRUE(frame_element->contentDocument()->View()->CanThrottleRendering());
-  EXPECT_TRUE(
-      child_frame_element->contentDocument()->View()->CanThrottleRendering());
-
-  // Move the frame back on screen but don't update throttling yet.
+  // Move the frame back on screen.
   frame_element->setAttribute(kStyleAttr, "transform: translateY(0px)");
   Compositor().BeginFrame();
-  EXPECT_TRUE(frame_element->contentDocument()->View()->CanThrottleRendering());
-  EXPECT_TRUE(
-      child_frame_element->contentDocument()->View()->CanThrottleRendering());
-
-  // Update throttling for the child. It should remain throttled because the
-  // parent is still throttled.
-  child_frame_element->contentDocument()
-      ->View()
-      ->UpdateRenderThrottlingStatusForTesting();
-  EXPECT_TRUE(frame_element->contentDocument()->View()->CanThrottleRendering());
-  EXPECT_TRUE(
-      child_frame_element->contentDocument()->View()->CanThrottleRendering());
-
-  // Updating throttling on the parent should unthrottle both frames.
-  frame_element->contentDocument()
-      ->View()
-      ->UpdateRenderThrottlingStatusForTesting();
   EXPECT_FALSE(
       frame_element->contentDocument()->View()->CanThrottleRendering());
   EXPECT_FALSE(
diff --git a/third_party/blink/renderer/core/script/BUILD.gn b/third_party/blink/renderer/core/script/BUILD.gn
index 3eecb063c..2ac894f 100644
--- a/third_party/blink/renderer/core/script/BUILD.gn
+++ b/third_party/blink/renderer/core/script/BUILD.gn
@@ -28,6 +28,7 @@
     "import_map.h",
     "layered_api.cc",
     "layered_api.h",
+    "layered_api_module.h",
     "layered_api_resources.h",
     "modulator.cc",
     "modulator.h",
diff --git a/third_party/blink/renderer/core/script/generate_lapi_grdp.py b/third_party/blink/renderer/core/script/generate_lapi_grdp.py
index 99eff1f6..a05172b 100755
--- a/third_party/blink/renderer/core/script/generate_lapi_grdp.py
+++ b/third_party/blink/renderer/core/script/generate_lapi_grdp.py
@@ -22,10 +22,14 @@
         os.path.join(core_script_path, 'resources/layered_api/resources.grdp'),
         'w')
 
-    # Output .h file.
+    # Output layered_api_resources.h file.
     output_header_file = open(
         os.path.join(core_script_path, 'layered_api_resources.h'), 'w')
 
+    # Output layered_api_module.h file.
+    output_module_header_file = open(
+        os.path.join(core_script_path, 'layered_api_module.h'), 'w')
+
     print >> output_grdp_file, '''<?xml version="1.0" encoding="utf-8"?>
 <grit-part>
   <!-- Layered API scripts. This file is generated by
@@ -35,12 +39,76 @@
        third_party/blink/public/blink_resources.grd.
     -->'''
 
+    resource_list_in_header_file = ''
+    modules = set()
+
+    for root, _, filenames in sorted(os.walk(input_path)):
+        relroot = os.path.relpath(root, input_path)
+        if relroot == '.':
+            # We don't include top-level files under resources/layered_api,
+            # including generated resources.grdp.
+            continue
+
+        # Get e.g. "kKvStorage" for kv-storage.
+        module_name = relroot.split('/')[0]
+        module_name = "k" + module_name.title().replace('-', '')
+        modules.add(module_name)
+
+        for filename in sorted(filenames):
+            if filename.startswith('.') or filename.startswith(
+                    'README') or filename.startswith('OWNERS'):
+                continue
+            relpath = os.path.relpath(os.path.join(root, filename), input_path)
+            relpath = relpath.replace('\\', '/')
+            resource_id = relpath
+            resource_id = resource_id.replace('/', '_')
+            resource_id = resource_id.replace('-', '_')
+            resource_id = resource_id.replace('.', '_')
+            resource_id = resource_id.upper()
+            resource_id = "IDR_LAYERED_API_" + resource_id
+            resource_list_in_header_file += \
+                '    {"%s",\n     %s,\n     Module::%s},\n' % (relpath, resource_id, module_name)
+            print >> output_grdp_file, (
+                '  <include name="%s" file="%s/%s" type="BINDATA" skip_minify="true" compress="gzip"/>'
+                % (resource_id, input_relative_path, relpath))
+        resource_list_in_header_file += '\n'
+    print >> output_grdp_file, '</grit-part>'
+
+    module_list_in_header_file = ''
+    for module in modules:
+        module_list_in_header_file += ('  %s,\n' % module)
+
+    print >> output_module_header_file, '''// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_CORE_SCRIPT_LAYERED_API_MODULE_H_
+#define THIRD_PARTY_BLINK_RENDERER_CORE_SCRIPT_LAYERED_API_MODULE_H_
+
+// This file is generated by
+// core/script/generate_lapi_grdp.py and shouldn't modified manually.
+// A corresponding grdp file (layered_api_resources.grdp) is also generated.
+
+namespace blink {
+
+namespace layered_api {
+
+enum class Module {
+%s};
+
+}  // namespace layered_api
+
+}  // namespace blink
+
+#endif  // THIRD_PARTY_BLINK_RENDERER_CORE_SCRIPT_LAYERED_API_MODULE_H_''' % \
+        module_list_in_header_file
+
     print >> output_header_file, '''// 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/resources/grit/blink_resources.h"
-#include "third_party/blink/renderer/core/script/layered_api.h"
+#include "third_party/blink/renderer/core/script/layered_api_module.h"
 
 #ifndef THIRD_PARTY_BLINK_RENDERER_CORE_SCRIPT_LAYERED_API_RESOURCES_H_
 #define THIRD_PARTY_BLINK_RENDERER_CORE_SCRIPT_LAYERED_API_RESOURCES_H_
@@ -57,34 +125,14 @@
 
 namespace {
 
-const LayeredAPIResource kLayeredAPIResources[] = {'''
+struct LayeredAPIResource {
+  const char* path;
+  int resource_id;
+  Module module;
+};
 
-    for root, _, filenames in sorted(os.walk(input_path)):
-        if root == 'resources/layered_api':
-            # We don't include top-level files under resources/layered_api,
-            # including generated resources.grdp.
-            continue
-        for filename in sorted(filenames):
-            if filename.startswith('.') or filename.startswith(
-                    'README') or filename.startswith('OWNERS'):
-                continue
-            relpath = os.path.relpath(os.path.join(root, filename), input_path)
-            relpath = relpath.replace('\\', '/')
-            resource_id = relpath
-            resource_id = resource_id.replace('/', '_')
-            resource_id = resource_id.replace('-', '_')
-            resource_id = resource_id.replace('.', '_')
-            resource_id = resource_id.upper()
-            resource_id = "IDR_LAYERED_API_" + resource_id
-            print >> output_header_file, (
-                '    {"%s",\n     %s},' % (relpath, resource_id))
-            print >> output_grdp_file, (
-                '  <include name="%s" file="%s/%s" type="BINDATA" skip_minify="true" compress="gzip"/>'
-                % (resource_id, input_relative_path, relpath))
-        print >> output_header_file, ''
-    print >> output_grdp_file, '</grit-part>'
-
-    print >> output_header_file, '''};
+const LayeredAPIResource kLayeredAPIResources[] = {
+%s};
 
 }  // namespace
 
@@ -92,9 +140,12 @@
 
 }  // namespace blink
 
-#endif  // THIRD_PARTY_BLINK_RENDERER_CORE_SCRIPT_LAYERED_API_RESOURCES_H_'''
+#endif  // THIRD_PARTY_BLINK_RENDERER_CORE_SCRIPT_LAYERED_API_RESOURCES_H_''' % \
+        resource_list_in_header_file
+
     output_grdp_file.close()
     output_header_file.close()
+    output_module_header_file.close()
 
 
 if __name__ == '__main__':
diff --git a/third_party/blink/renderer/core/script/layered_api.h b/third_party/blink/renderer/core/script/layered_api.h
index 6667730..c7d21a8 100644
--- a/third_party/blink/renderer/core/script/layered_api.h
+++ b/third_party/blink/renderer/core/script/layered_api.h
@@ -39,11 +39,6 @@
 // Gets source text for std-internal://x/index.js.
 CORE_EXPORT String GetSourceText(const KURL&);
 
-struct LayeredAPIResource {
-  const char* path;
-  int resource_id;
-};
-
 }  // namespace layered_api
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/script/layered_api_module.h b/third_party/blink/renderer/core/script/layered_api_module.h
new file mode 100644
index 0000000..28809a9
--- /dev/null
+++ b/third_party/blink/renderer/core/script/layered_api_module.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_RENDERER_CORE_SCRIPT_LAYERED_API_MODULE_H_
+#define THIRD_PARTY_BLINK_RENDERER_CORE_SCRIPT_LAYERED_API_MODULE_H_
+
+// This file is generated by
+// core/script/generate_lapi_grdp.py and shouldn't modified manually.
+// A corresponding grdp file (layered_api_resources.grdp) is also generated.
+
+namespace blink {
+
+namespace layered_api {
+
+enum class Module {
+  kBlank,
+  kVirtualScroller,
+  kKvStorage,
+};
+
+}  // namespace layered_api
+
+}  // namespace blink
+
+#endif  // THIRD_PARTY_BLINK_RENDERER_CORE_SCRIPT_LAYERED_API_MODULE_H_
diff --git a/third_party/blink/renderer/core/script/layered_api_resources.h b/third_party/blink/renderer/core/script/layered_api_resources.h
index cfd04355..65214d2 100644
--- a/third_party/blink/renderer/core/script/layered_api_resources.h
+++ b/third_party/blink/renderer/core/script/layered_api_resources.h
@@ -3,7 +3,7 @@
 // found in the LICENSE file.
 
 #include "third_party/blink/public/resources/grit/blink_resources.h"
-#include "third_party/blink/renderer/core/script/layered_api.h"
+#include "third_party/blink/renderer/core/script/layered_api_module.h"
 
 #ifndef THIRD_PARTY_BLINK_RENDERER_CORE_SCRIPT_LAYERED_API_RESOURCES_H_
 #define THIRD_PARTY_BLINK_RENDERER_CORE_SCRIPT_LAYERED_API_RESOURCES_H_
@@ -20,28 +20,42 @@
 
 namespace {
 
+struct LayeredAPIResource {
+  const char* path;
+  int resource_id;
+  Module module;
+};
+
 const LayeredAPIResource kLayeredAPIResources[] = {
-    {"blank/index.js", IDR_LAYERED_API_BLANK_INDEX_JS},
+    {"blank/index.js", IDR_LAYERED_API_BLANK_INDEX_JS, Module::kBlank},
 
     {"kv-storage/async_iterator.js",
-     IDR_LAYERED_API_KV_STORAGE_ASYNC_ITERATOR_JS},
-    {"kv-storage/idb_utils.js", IDR_LAYERED_API_KV_STORAGE_IDB_UTILS_JS},
-    {"kv-storage/index.js", IDR_LAYERED_API_KV_STORAGE_INDEX_JS},
+     IDR_LAYERED_API_KV_STORAGE_ASYNC_ITERATOR_JS, Module::kKvStorage},
+    {"kv-storage/idb_utils.js", IDR_LAYERED_API_KV_STORAGE_IDB_UTILS_JS,
+     Module::kKvStorage},
+    {"kv-storage/index.js", IDR_LAYERED_API_KV_STORAGE_INDEX_JS,
+     Module::kKvStorage},
 
-    {"virtual-scroller/index.js", IDR_LAYERED_API_VIRTUAL_SCROLLER_INDEX_JS},
+    {"virtual-scroller/index.js", IDR_LAYERED_API_VIRTUAL_SCROLLER_INDEX_JS,
+     Module::kVirtualScroller},
     {"virtual-scroller/item-source.js",
-     IDR_LAYERED_API_VIRTUAL_SCROLLER_ITEM_SOURCE_JS},
+     IDR_LAYERED_API_VIRTUAL_SCROLLER_ITEM_SOURCE_JS, Module::kVirtualScroller},
     {"virtual-scroller/virtual-repeater.js",
-     IDR_LAYERED_API_VIRTUAL_SCROLLER_VIRTUAL_REPEATER_JS},
+     IDR_LAYERED_API_VIRTUAL_SCROLLER_VIRTUAL_REPEATER_JS,
+     Module::kVirtualScroller},
     {"virtual-scroller/virtual-scroller.js",
-     IDR_LAYERED_API_VIRTUAL_SCROLLER_VIRTUAL_SCROLLER_JS},
+     IDR_LAYERED_API_VIRTUAL_SCROLLER_VIRTUAL_SCROLLER_JS,
+     Module::kVirtualScroller},
 
     {"virtual-scroller/layouts/layout-1d-base.js",
-     IDR_LAYERED_API_VIRTUAL_SCROLLER_LAYOUTS_LAYOUT_1D_BASE_JS},
+     IDR_LAYERED_API_VIRTUAL_SCROLLER_LAYOUTS_LAYOUT_1D_BASE_JS,
+     Module::kVirtualScroller},
     {"virtual-scroller/layouts/layout-1d-grid.js",
-     IDR_LAYERED_API_VIRTUAL_SCROLLER_LAYOUTS_LAYOUT_1D_GRID_JS},
+     IDR_LAYERED_API_VIRTUAL_SCROLLER_LAYOUTS_LAYOUT_1D_GRID_JS,
+     Module::kVirtualScroller},
     {"virtual-scroller/layouts/layout-1d.js",
-     IDR_LAYERED_API_VIRTUAL_SCROLLER_LAYOUTS_LAYOUT_1D_JS},
+     IDR_LAYERED_API_VIRTUAL_SCROLLER_LAYOUTS_LAYOUT_1D_JS,
+     Module::kVirtualScroller},
 
 };
 
diff --git a/third_party/blink/renderer/core/script/modulator_impl_base.cc b/third_party/blink/renderer/core/script/modulator_impl_base.cc
index 7f5f017..156c48a 100644
--- a/third_party/blink/renderer/core/script/modulator_impl_base.cc
+++ b/third_party/blink/renderer/core/script/modulator_impl_base.cc
@@ -161,7 +161,7 @@
 
     case ParsedSpecifier::Type::kBare:
       // Allow |@std/x| specifiers if Layered API is enabled.
-      if (RuntimeEnabledFeatures::LayeredAPIEnabled()) {
+      if (RuntimeEnabledFeatures::BuiltInModuleInfraEnabled()) {
         if (parsed_specifier.GetImportMapKeyString().StartsWith("@std/")) {
           return KURL("import:" + parsed_specifier.GetImportMapKeyString());
         }
@@ -190,10 +190,10 @@
     return;
   }
 
-  if (!RuntimeEnabledFeatures::LayeredAPIEnabled()) {
+  if (!RuntimeEnabledFeatures::BuiltInModuleInfraEnabled()) {
     GetExecutionContext()->AddErrorMessage(
         ConsoleLogger::Source::kOther,
-        "Import maps are disabled when LayeredAPI is disabled.");
+        "Import maps are disabled when Layered API Infra is disabled.");
     return;
   }
   import_map_ = import_map;
diff --git a/third_party/blink/renderer/core/script/script_loader.cc b/third_party/blink/renderer/core/script/script_loader.cc
index b6a1320..2fd42f0 100644
--- a/third_party/blink/renderer/core/script/script_loader.cc
+++ b/third_party/blink/renderer/core/script/script_loader.cc
@@ -165,8 +165,12 @@
   return false;
 }
 
-// Returns true on success.
-bool ParseAndRegisterImportMap(ScriptElementBase& element) {
+enum class ShouldFireErrorEvent {
+  kDoNotFire,
+  kShouldFire,
+};
+
+ShouldFireErrorEvent ParseAndRegisterImportMap(ScriptElementBase& element) {
   Document& element_document = element.GetDocument();
   Document* context_document = element_document.ContextDocument();
   DCHECK(context_document);
@@ -174,11 +178,16 @@
       Modulator::From(ToScriptStateForMainWorld(context_document->GetFrame()));
   DCHECK(modulator);
 
+  // If import maps are not enabled, we do nothing and return here, and also
+  // do not fire error events.
+  if (!RuntimeEnabledFeatures::BuiltInModuleInfraEnabled())
+    return ShouldFireErrorEvent::kDoNotFire;
+
   if (!modulator->IsAcquiringImportMaps()) {
     element_document.AddConsoleMessage(ConsoleMessage::Create(
         kJSMessageSource, kErrorMessageLevel,
         "An import map is added after module script load was triggered."));
-    return false;
+    return ShouldFireErrorEvent::kShouldFire;
   }
 
   // TODO(crbug.com/922212): Implemenet external import maps.
@@ -186,7 +195,7 @@
     element_document.AddConsoleMessage(
         ConsoleMessage::Create(kJSMessageSource, kErrorMessageLevel,
                                "External import maps are not yet supported."));
-    return false;
+    return ShouldFireErrorEvent::kShouldFire;
   }
 
   KURL base_url = element_document.BaseURL();
@@ -194,10 +203,10 @@
       ImportMap::Create(element.TextFromChildren(), base_url, element_document);
 
   if (!import_map)
-    return false;
+    return ShouldFireErrorEvent::kShouldFire;
 
   modulator->RegisterImportMap(import_map);
-  return true;
+  return ShouldFireErrorEvent::kDoNotFire;
 }
 
 }  // namespace
@@ -233,7 +242,7 @@
     return true;
   }
 
-  if (RuntimeEnabledFeatures::LayeredAPIEnabled() && type == "importmap") {
+  if (type == "importmap") {
     if (out_is_import_map)
       *out_is_import_map = true;
     return true;
@@ -385,7 +394,8 @@
 
   // Process the import map.
   if (is_import_map) {
-    if (!ParseAndRegisterImportMap(*element_)) {
+    if (ParseAndRegisterImportMap(*element_) ==
+        ShouldFireErrorEvent::kShouldFire) {
       element_document.GetTaskRunner(TaskType::kDOMManipulation)
           ->PostTask(FROM_HERE,
                      WTF::Bind(&ScriptElementBase::DispatchErrorEvent,
diff --git a/third_party/blink/renderer/core/streams/BUILD.gn b/third_party/blink/renderer/core/streams/BUILD.gn
index 0353f0e..20862900 100644
--- a/third_party/blink/renderer/core/streams/BUILD.gn
+++ b/third_party/blink/renderer/core/streams/BUILD.gn
@@ -19,6 +19,8 @@
     "readable_stream_default_reader.h",
     "readable_stream_operations.cc",
     "readable_stream_operations.h",
+    "readable_stream_wrapper.cc",
+    "readable_stream_wrapper.h",
     "retain_wrapper_during_construction.cc",
     "retain_wrapper_during_construction.h",
     "stream_algorithms.h",
diff --git a/third_party/blink/renderer/core/streams/readable_stream.cc b/third_party/blink/renderer/core/streams/readable_stream.cc
index 8a4dac7..04c31ce6 100644
--- a/third_party/blink/renderer/core/streams/readable_stream.cc
+++ b/third_party/blink/renderer/core/streams/readable_stream.cc
@@ -4,82 +4,12 @@
 
 #include "third_party/blink/renderer/core/streams/readable_stream.h"
 
-#include "third_party/blink/renderer/bindings/core/v8/script_function.h"
-#include "third_party/blink/renderer/bindings/core/v8/v8_readable_stream.h"
-#include "third_party/blink/renderer/bindings/core/v8/v8_writable_stream.h"
-#include "third_party/blink/renderer/core/streams/readable_stream_operations.h"
-#include "third_party/blink/renderer/core/streams/retain_wrapper_during_construction.h"
-#include "third_party/blink/renderer/core/streams/writable_stream_wrapper.h"
+#include "third_party/blink/renderer/core/streams/readable_stream_wrapper.h"
 #include "third_party/blink/renderer/platform/bindings/exception_state.h"
-#include "third_party/blink/renderer/platform/bindings/v8_binding.h"
+#include "third_party/blink/renderer/platform/runtime_enabled_features.h"
 
 namespace blink {
 
-class ReadableStream::NoopFunction : public ScriptFunction {
- public:
-  static v8::Local<v8::Function> CreateFunction(ScriptState* script_state) {
-    auto* self = MakeGarbageCollected<NoopFunction>(script_state);
-    return self->BindToV8Function();
-  }
-  explicit NoopFunction(ScriptState* script_state)
-      : ScriptFunction(script_state) {}
-  ScriptValue Call(ScriptValue value) override { return value; }
-};
-
-void ReadableStream::Init(ScriptState* script_state,
-                          ScriptValue underlying_source,
-                          ScriptValue strategy,
-                          ExceptionState& exception_state) {
-  ScriptValue value = ReadableStreamOperations::CreateReadableStream(
-      script_state, underlying_source, strategy, exception_state);
-  if (exception_state.HadException())
-    return;
-
-  DCHECK(value.IsObject());
-  InitWithInternalStream(script_state, value.V8Value().As<v8::Object>(),
-                         exception_state);
-}
-
-void ReadableStream::InitWithInternalStream(ScriptState* script_state,
-                                            v8::Local<v8::Object> object,
-                                            ExceptionState& exception_state) {
-  DCHECK(ReadableStreamOperations::IsReadableStreamForDCheck(
-      script_state, ScriptValue(script_state, object)));
-  object_.Set(script_state->GetIsolate(), object);
-
-  v8::Isolate* isolate = script_state->GetIsolate();
-  v8::TryCatch block(isolate);
-  v8::Local<v8::Value> wrapper = ToV8(this, script_state);
-  if (wrapper.IsEmpty()) {
-    exception_state.RethrowV8Exception(block.Exception());
-    return;
-  }
-
-  v8::Local<v8::Context> context = script_state->GetContext();
-  v8::Local<v8::Object> bindings =
-      context->GetExtrasBindingObject().As<v8::Object>();
-  v8::Local<v8::Value> symbol_value;
-  if (!bindings->Get(context, V8String(isolate, "internalReadableStreamSymbol"))
-           .ToLocal(&symbol_value)) {
-    exception_state.RethrowV8Exception(block.Exception());
-    return;
-  }
-
-  if (wrapper.As<v8::Object>()
-          ->Set(context, symbol_value.As<v8::Symbol>(), object)
-          .IsNothing()) {
-    exception_state.RethrowV8Exception(block.Exception());
-    return;
-  }
-
-  // This is needed because sometimes a ReadableStream can be detached from
-  // the owner object such as Response.
-  if (!RetainWrapperDuringConstruction(this, script_state)) {
-    exception_state.ThrowDOMException(DOMExceptionCode::kInvalidStateError,
-                                      "Cannot queue task to retain wrapper");
-  }
-}
-
 ReadableStream* ReadableStream::Create(ScriptState* script_state,
                                        ExceptionState& exception_state) {
   return Create(
@@ -102,485 +32,33 @@
                                        ScriptValue underlying_source,
                                        ScriptValue strategy,
                                        ExceptionState& exception_state) {
-  auto* stream = MakeGarbageCollected<ReadableStream>();
-  stream->Init(script_state, underlying_source, strategy, exception_state);
-  if (exception_state.HadException())
-    return nullptr;
-  return stream;
-}
-
-ReadableStream* ReadableStream::CreateFromInternalStream(
-    ScriptState* script_state,
-    ScriptValue object,
-    ExceptionState& exception_state) {
-  DCHECK(object.IsObject());
-  return CreateFromInternalStream(
-      script_state, object.V8Value().As<v8::Object>(), exception_state);
-}
-
-ReadableStream* ReadableStream::CreateFromInternalStream(
-    ScriptState* script_state,
-    v8::Local<v8::Object> object,
-    ExceptionState& exception_state) {
-  auto* stream = MakeGarbageCollected<ReadableStream>();
-  stream->InitWithInternalStream(script_state, object, exception_state);
-  if (exception_state.HadException())
-    return nullptr;
-  return stream;
+  // TODO(ricea): Select implementation based on StreamsNative feature here.
+  return ReadableStreamWrapper::Create(script_state, underlying_source,
+                                       strategy, exception_state);
 }
 
 ReadableStream* ReadableStream::CreateWithCountQueueingStrategy(
     ScriptState* script_state,
     UnderlyingSourceBase* underlying_source,
     size_t high_water_mark) {
-  v8::TryCatch block(script_state->GetIsolate());
-  ScriptValue strategy =
-      ReadableStreamOperations::CreateCountQueuingStrategy(script_state, 0);
-  if (strategy.IsEmpty())
-    return nullptr;
-
-  ScriptValue value = ReadableStreamOperations::CreateReadableStream(
-      script_state, underlying_source, strategy);
-  if (value.IsEmpty())
-    return nullptr;
-
-  ExceptionState exception_state(script_state->GetIsolate(),
-                                 ExceptionState::kConstructionContext,
-                                 "ReadableStream");
-  DCHECK(value.V8Value()->IsObject());
-  auto* stream = CreateFromInternalStream(script_state, value, exception_state);
-  if (exception_state.HadException())
-    exception_state.ClearException();
-  return stream;
-}
-
-void ReadableStream::Trace(Visitor* visitor) {
-  visitor->Trace(object_);
-  ScriptWrappable::Trace(visitor);
-}
-
-ScriptPromise ReadableStream::cancel(ScriptState* script_state,
-                                     ExceptionState& exception_state) {
-  return cancel(
-      script_state,
-      ScriptValue(script_state, v8::Undefined(script_state->GetIsolate())),
-      exception_state);
-}
-
-bool ReadableStream::locked(ScriptState* script_state,
-                            ExceptionState& exception_state) const {
-  auto result = IsLocked(script_state, exception_state);
-
-  return !result || *result;
-}
-
-ScriptPromise ReadableStream::cancel(ScriptState* script_state,
-                                     ScriptValue reason,
-                                     ExceptionState& exception_state) {
-  if (locked(script_state, exception_state) &&
-      !exception_state.HadException()) {
-    exception_state.ThrowTypeError("Cannot cancel a locked stream");
-  }
-
-  if (exception_state.HadException())
-    return ScriptPromise();
-
-  return ReadableStreamOperations::Cancel(
-      script_state, GetInternalStream(script_state), reason, exception_state);
-}
-
-ScriptValue ReadableStream::getReader(ScriptState* script_state,
-                                      ExceptionState& exception_state) {
-  return ReadableStreamOperations::GetReader(
-      script_state, GetInternalStream(script_state), exception_state);
-}
-
-ScriptValue ReadableStream::getReader(ScriptState* script_state,
-                                      ScriptValue options,
-                                      ExceptionState& exception_state) {
-  v8::TryCatch block(script_state->GetIsolate());
-  v8::Local<v8::Value> mode;
-  v8::Local<v8::String> mode_string;
-  v8::Local<v8::Context> context = script_state->GetContext();
-  if (options.V8Value()->IsUndefined()) {
-    mode = v8::Undefined(script_state->GetIsolate());
-  } else {
-    v8::Local<v8::Object> v8_options;
-    if (!options.V8Value()->ToObject(context).ToLocal(&v8_options)) {
-      exception_state.RethrowV8Exception(block.Exception());
-      return ScriptValue();
-    }
-    if (!v8_options->Get(context, V8String(script_state->GetIsolate(), "mode"))
-             .ToLocal(&mode)) {
-      exception_state.RethrowV8Exception(block.Exception());
-      return ScriptValue();
-    }
-  }
-
-  if (!mode->ToString(context).ToLocal(&mode_string)) {
-    exception_state.RethrowV8Exception(block.Exception());
-    return ScriptValue();
-  }
-  if (ToCoreString(mode_string) == "byob") {
-    exception_state.ThrowTypeError("invalid mode");
-    return ScriptValue();
-  }
-  if (!mode->IsUndefined()) {
-    exception_state.ThrowRangeError("invalid mode");
-    return ScriptValue();
-  }
-  return ReadableStreamOperations::GetReader(
-      script_state, GetInternalStream(script_state), exception_state);
-}
-
-ScriptValue ReadableStream::pipeThrough(ScriptState* script_state,
-                                        ScriptValue transform_stream,
-                                        ExceptionState& exception_state) {
-  return pipeThrough(
-      script_state, transform_stream,
-      ScriptValue(script_state, v8::Undefined(script_state->GetIsolate())),
-      exception_state);
-}
-
-// https://streams.spec.whatwg.org/#rs-pipe-through
-ScriptValue ReadableStream::pipeThrough(ScriptState* script_state,
-                                        ScriptValue transform_stream,
-                                        ScriptValue options,
-                                        ExceptionState& exception_state) {
-  v8::Local<v8::Value> pair_value = transform_stream.V8Value();
-  v8::Local<v8::Context> context = script_state->GetContext();
-
-  constexpr char kWritableIsNotWritableStream[] =
-      "parameter 1's 'writable' property is not a WritableStream.";
-  constexpr char kReadableIsNotReadableStream[] =
-      "parameter 1's 'readable' property is not a ReadableStream.";
-  constexpr char kWritableIsLocked[] = "parameter 1's 'writable' is locked.";
-
-  v8::Local<v8::Object> pair;
-  if (!pair_value->ToObject(context).ToLocal(&pair)) {
-    exception_state.ThrowTypeError(kWritableIsNotWritableStream);
-    return ScriptValue();
-  }
-
-  v8::Isolate* isolate = script_state->GetIsolate();
-  v8::Local<v8::Value> writable, readable;
-  {
-    v8::TryCatch block(isolate);
-    if (!pair->Get(context, V8String(isolate, "writable")).ToLocal(&writable)) {
-      exception_state.RethrowV8Exception(block.Exception());
-      return ScriptValue();
-    }
-    DCHECK(!block.HasCaught());
-
-    if (!pair->Get(context, V8String(isolate, "readable")).ToLocal(&readable)) {
-      exception_state.RethrowV8Exception(block.Exception());
-      return ScriptValue();
-    }
-    DCHECK(!block.HasCaught());
-  }
-
-  // 2. If ! IsWritableStream(_writable_) is *false*, throw a *TypeError*
-  //    exception.
-  WritableStream* dom_writable =
-      V8WritableStream::ToImplWithTypeCheck(isolate, writable);
-  if (!dom_writable) {
-    exception_state.ThrowTypeError(kWritableIsNotWritableStream);
-    return ScriptValue();
-  }
-
-  // 3. If ! IsReadableStream(_readable_) is *false*, throw a *TypeError*
-  //    exception.
-  if (!V8ReadableStream::HasInstance(readable, isolate)) {
-    exception_state.ThrowTypeError(kReadableIsNotReadableStream);
-    return ScriptValue();
-  }
-
-  // TODO(ricea): When aborting pipes is supported, implement step 5:
-  // 5. If _signal_ is not *undefined*, and _signal_ is not an instance of the
-  //    `AbortSignal` interface, throw a *TypeError* exception.
-
-  // 6. If ! IsReadableStreamLocked(*this*) is *true*, throw a *TypeError*
-  //    exception.
-  if (IsLocked(script_state, exception_state).value_or(false)) {
-    exception_state.ThrowTypeError("Cannot pipe a locked stream");
-    return ScriptValue();
-  }
-  if (exception_state.HadException()) {
-    return ScriptValue();
-  }
-
-  // 7. If ! IsWritableStreamLocked(_writable_) is *true*, throw a *TypeError*
-  //    exception.
-  if (dom_writable->IsLocked(script_state, exception_state).value_or(false)) {
-    exception_state.ThrowTypeError(kWritableIsLocked);
-    return ScriptValue();
-  }
-  if (exception_state.HadException()) {
-    return ScriptValue();
-  }
-
-  if (RuntimeEnabledFeatures::StreamsNativeEnabled()) {
-    // TODO(ricea): Replace this with a DCHECK once ReadableStreamNative is
-    // implemented.
-    exception_state.ThrowTypeError(
-        "pipeThrough disabled because StreamsNative feature is enabled");
-    return ScriptValue();
-  }
-
-  // This cast is safe because the following code will only be run when the
-  // native version of WritableStream is not in use.
-  WritableStreamWrapper* writable_wrapper =
-      static_cast<WritableStreamWrapper*>(dom_writable);
-
-  // 8. Let _promise_ be ! ReadableStreamPipeTo(*this*, _writable_,
-  //    _preventClose_, _preventAbort_, _preventCancel_,
-  //   _signal_).
-
-  // TODO(ricea): Maybe change the parameters to
-  // ReadableStreamOperations::PipeTo to match ReadableStreamPipeTo() in the
-  // standard?
-  ScriptPromise promise = ReadableStreamOperations::PipeTo(
-      script_state, GetInternalStream(script_state),
-      writable_wrapper->GetInternalStream(script_state), options,
-      exception_state);
-  if (exception_state.HadException()) {
-    return ScriptValue();
-  }
-
-  // 9. Set _promise_.[[PromiseIsHandled]] to *true*.
-  promise.MarkAsHandled();
-
-  // 10. Return _readable_.
-  return ScriptValue(script_state, readable);
-}
-
-ScriptPromise ReadableStream::pipeTo(ScriptState* script_state,
-                                     ScriptValue destination,
-                                     ExceptionState& exception_state) {
-  return pipeTo(
-      script_state, destination,
-      ScriptValue(script_state, v8::Undefined(script_state->GetIsolate())),
-      exception_state);
-}
-
-ScriptPromise ReadableStream::pipeTo(ScriptState* script_state,
-                                     ScriptValue destination_value,
-                                     ScriptValue options,
-                                     ExceptionState& exception_state) {
-  WritableStream* destination = V8WritableStream::ToImplWithTypeCheck(
-      script_state->GetIsolate(), destination_value.V8Value());
-
-  if (!destination) {
-    exception_state.ThrowTypeError("Illegal invocation");
-    return ScriptPromise();
-  }
-  if (locked(script_state, exception_state) &&
-      !exception_state.HadException()) {
-    exception_state.ThrowTypeError("Cannot pipe a locked stream");
-    return ScriptPromise();
-  }
-  if (exception_state.HadException())
-    return ScriptPromise();
-  if (destination->locked(script_state, exception_state) &&
-      !exception_state.HadException()) {
-    exception_state.ThrowTypeError("Cannot pipe to a locked stream");
-    return ScriptPromise();
-  }
-  if (exception_state.HadException())
-    return ScriptPromise();
-
-  if (RuntimeEnabledFeatures::StreamsNativeEnabled()) {
-    // TODO(ricea): Replace this with a DCHECK once ReadableStreamNative is
-    // implemented.
-    exception_state.ThrowTypeError(
-        "pipeTo disabled because StreamsNative feature is enabled");
-    return ScriptPromise();
-  }
-
-  // This cast is safe because the following code will only be run when the
-  // native version of WritableStream is not in use.
-  WritableStreamWrapper* destination_wrapper =
-      static_cast<WritableStreamWrapper*>(destination);
-
-  return ReadableStreamOperations::PipeTo(
-      script_state, GetInternalStream(script_state),
-      destination_wrapper->GetInternalStream(script_state), options,
-      exception_state);
-}
-
-ScriptValue ReadableStream::tee(ScriptState* script_state,
-                                ExceptionState& exception_state) {
-  v8::Isolate* isolate = script_state->GetIsolate();
-  ReadableStream* branch1 = nullptr;
-  ReadableStream* branch2 = nullptr;
-
-  Tee(script_state, &branch1, &branch2, exception_state);
-
-  if (!branch1 || !branch2)
-    return ScriptValue();
-
-  DCHECK(!exception_state.HadException());
-
-  v8::TryCatch block(isolate);
-  v8::Local<v8::Context> context = script_state->GetContext();
-  v8::Local<v8::Array> array = v8::Array::New(isolate, 2);
-  v8::Local<v8::Object> global = context->Global();
-
-  v8::Local<v8::Value> v8_branch1 = ToV8(branch1, global, isolate);
-  if (v8_branch1.IsEmpty()) {
-    exception_state.RethrowV8Exception(block.Exception());
-    return ScriptValue();
-  }
-  v8::Local<v8::Value> v8_branch2 = ToV8(branch2, global, isolate);
-  if (v8_branch1.IsEmpty()) {
-    exception_state.RethrowV8Exception(block.Exception());
-    return ScriptValue();
-  }
-  if (array->Set(context, V8String(isolate, "0"), v8_branch1).IsNothing()) {
-    exception_state.RethrowV8Exception(block.Exception());
-    return ScriptValue();
-  }
-  if (array->Set(context, V8String(isolate, "1"), v8_branch2).IsNothing()) {
-    exception_state.RethrowV8Exception(block.Exception());
-    return ScriptValue();
-  }
-  return ScriptValue(script_state, array);
-}
-
-void ReadableStream::Tee(ScriptState* script_state,
-                         ReadableStream** branch1,
-                         ReadableStream** branch2,
-                         ExceptionState& exception_state) {
-  v8::Local<v8::Context> context = script_state->GetContext();
-
-  if (locked(script_state, exception_state) &&
-      !exception_state.HadException()) {
-    exception_state.ThrowTypeError("The stream is already locked.");
-  }
-
-  if (exception_state.HadException())
-    return;
-
-  ScriptValue tee_result = ReadableStreamOperations::Tee(
-      script_state, GetInternalStream(script_state), exception_state);
-  if (tee_result.IsEmpty())
-    return;
-
-  DCHECK(!exception_state.HadException());
-  DCHECK(tee_result.V8Value()->IsArray());
-
-  v8::Local<v8::Array> branches = tee_result.V8Value().As<v8::Array>();
-  v8::Local<v8::Value> v8_branch1, v8_branch2;
-  v8::TryCatch block(script_state->GetIsolate());
-
-  if (!branches->Get(context, 0).ToLocal(&v8_branch1)) {
-    exception_state.RethrowV8Exception(block.Exception());
-    return;
-  }
-  if (!branches->Get(context, 1).ToLocal(&v8_branch2)) {
-    exception_state.RethrowV8Exception(block.Exception());
-    return;
-  }
-
-  DCHECK(v8_branch1->IsObject());
-  DCHECK(v8_branch2->IsObject());
-
-  ReadableStream* temp_branch1 = MakeGarbageCollected<ReadableStream>();
-  ReadableStream* temp_branch2 = MakeGarbageCollected<ReadableStream>();
-
-  temp_branch1->InitWithInternalStream(
-      script_state, v8_branch1.As<v8::Object>(), exception_state);
-  if (exception_state.HadException())
-    return;
-
-  temp_branch2->InitWithInternalStream(
-      script_state, v8_branch2.As<v8::Object>(), exception_state);
-  if (exception_state.HadException())
-    return;
-
-  *branch1 = temp_branch1;
-  *branch2 = temp_branch2;
-}
-
-base::Optional<bool> ReadableStream::IsLocked(
-    ScriptState* script_state,
-    ExceptionState& exception_state) const {
-  return ReadableStreamOperations::IsLocked(
-      script_state, GetInternalStream(script_state), exception_state);
-}
-
-base::Optional<bool> ReadableStream::IsDisturbed(
-    ScriptState* script_state,
-    ExceptionState& exception_state) const {
-  return ReadableStreamOperations::IsDisturbed(
-      script_state, GetInternalStream(script_state), exception_state);
-}
-
-base::Optional<bool> ReadableStream::IsReadable(
-    ScriptState* script_state,
-    ExceptionState& exception_state) const {
-  return ReadableStreamOperations::IsReadable(
-      script_state, GetInternalStream(script_state), exception_state);
-}
-
-base::Optional<bool> ReadableStream::IsClosed(
-    ScriptState* script_state,
-    ExceptionState& exception_state) const {
-  return ReadableStreamOperations::IsClosed(
-      script_state, GetInternalStream(script_state), exception_state);
-}
-
-base::Optional<bool> ReadableStream::IsErrored(
-    ScriptState* script_state,
-    ExceptionState& exception_state) const {
-  return ReadableStreamOperations::IsErrored(
-      script_state, GetInternalStream(script_state), exception_state);
-}
-
-void ReadableStream::LockAndDisturb(ScriptState* script_state,
-                                    ExceptionState& exception_state) {
-  ScriptState::Scope scope(script_state);
-
-  const base::Optional<bool> is_locked =
-      IsLocked(script_state, exception_state);
-  if (!is_locked || is_locked.value())
-    return;
-
-  ScriptValue reader = getReader(script_state, exception_state);
-  if (reader.IsEmpty())
-    return;
-
-  ScriptPromise promise =
-      ReadableStreamOperations::DefaultReaderRead(script_state, reader);
-  promise.MarkAsHandled();
-}
-
-void ReadableStream::Serialize(ScriptState* script_state,
-                               MessagePort* port,
-                               ExceptionState& exception_state) {
-  ReadableStreamOperations::Serialize(
-      script_state, GetInternalStream(script_state), port, exception_state);
+  // TODO(ricea): Select implementation based on StreamsNative feature here.
+  return ReadableStreamWrapper::CreateWithCountQueueingStrategy(
+      script_state, underlying_source, high_water_mark);
 }
 
 // static
 ReadableStream* ReadableStream::Deserialize(ScriptState* script_state,
                                             MessagePort* port,
                                             ExceptionState& exception_state) {
-  // We need to execute V8 Extras JavaScript to create the new ReadableStream.
-  // We will not run author code.
-  v8::Isolate::AllowJavascriptExecutionScope allow_js(
-      script_state->GetIsolate());
-  ScriptValue internal_stream = ReadableStreamOperations::Deserialize(
-      script_state, port, exception_state);
-  if (exception_state.HadException())
+  // TODO(ricea): Implementation serialization for the native implementation.
+  if (RuntimeEnabledFeatures::StreamsNativeEnabled()) {
+    exception_state.ThrowTypeError(
+        "serialization disabled because StreamsNative feature is enabled");
     return nullptr;
-  return CreateFromInternalStream(script_state, internal_stream,
-                                  exception_state);
-}
+  }
 
-ScriptValue ReadableStream::GetInternalStream(ScriptState* script_state) const {
-  return ScriptValue(script_state,
-                     object_.NewLocal(script_state->GetIsolate()));
+  return ReadableStreamWrapper::Deserialize(script_state, port,
+                                            exception_state);
 }
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/streams/readable_stream.h b/third_party/blink/renderer/core/streams/readable_stream.h
index 7ba0452..3b9c1ad1 100644
--- a/third_party/blink/renderer/core/streams/readable_stream.h
+++ b/third_party/blink/renderer/core/streams/readable_stream.h
@@ -8,7 +8,6 @@
 #include "base/optional.h"
 #include "third_party/blink/renderer/bindings/core/v8/script_value.h"
 #include "third_party/blink/renderer/platform/bindings/script_wrappable.h"
-#include "third_party/blink/renderer/platform/bindings/trace_wrapper_v8_reference.h"
 #include "v8/include/v8.h"
 
 namespace blink {
@@ -25,22 +24,32 @@
   DEFINE_WRAPPERTYPEINFO();
 
  public:
-  // Call one of Init functions before using the instance.
-  ReadableStream() = default;
-  ~ReadableStream() override = default;
+  // ReadHandle is used to read from a stream. Each call to Read() corresponds
+  // to a call to ReadableStreamDefaultReaderRead on the underlying stream.
+  //
+  // It has awkward garbage collection semantics: either it must be kept in a
+  // TraceWrapperMember, or the ReadableStream must outlive it.
+  //
+  // This is a transitional interface while the streams C++ port is in progress.
+  // Eventually callers will just use ReadableStreamDefaultReader objects
+  // directly.
+  //
+  // TODO(ricea): Remove this when the V8 Extras implementation is removed.
+  class ReadHandle : public GarbageCollectedFinalized<ReadHandle> {
+   public:
+    ReadHandle() = default;
+    virtual ~ReadHandle() = default;
 
-  // If an error happens, |exception_state.HadException()| will be true, and
-  // |this| will not be usable after that.
-  void Init(ScriptState*,
-            ScriptValue underlying_source,
-            ScriptValue strategy,
-            ExceptionState& exception_state);
-  void InitWithInternalStream(ScriptState*,
-                              v8::Local<v8::Object> object,
-                              ExceptionState& exception_state);
+    virtual ScriptPromise Read(ScriptState*) = 0;
 
-  // Create* functions call Init* internally and returns null when an error
-  // happens.
+    virtual void Trace(Visitor*) {}
+
+   private:
+    DISALLOW_COPY_AND_ASSIGN(ReadHandle);
+  };
+
+  // Create* functions create an appropriate subclass depending on which
+  // implementation is selected by blink features.
   static ReadableStream* Create(ScriptState*, ExceptionState&);
   static ReadableStream* Create(ScriptState*,
                                 ScriptValue underlying_source,
@@ -49,12 +58,7 @@
                                 ScriptValue underlying_source,
                                 ScriptValue strategy,
                                 ExceptionState&);
-  static ReadableStream* CreateFromInternalStream(ScriptState*,
-                                                  v8::Local<v8::Object> object,
-                                                  ExceptionState&);
-  static ReadableStream* CreateFromInternalStream(ScriptState*,
-                                                  ScriptValue object,
-                                                  ExceptionState&);
+
   // This function doesn't take ExceptionState because the caller cannot have
   // one. Returns null when an error happens.
   static ReadableStream* CreateWithCountQueueingStrategy(
@@ -62,45 +66,60 @@
       UnderlyingSourceBase* underlying_source,
       size_t high_water_mark);
 
-  void Trace(Visitor* visitor) override;
-
   // IDL defined functions
-  bool locked(ScriptState*, ExceptionState&) const;
-  ScriptPromise cancel(ScriptState*, ExceptionState&);
-  ScriptPromise cancel(ScriptState*, ScriptValue reason, ExceptionState&);
-  ScriptValue getReader(ScriptState*, ExceptionState&);
-  ScriptValue getReader(ScriptState*, ScriptValue options, ExceptionState&);
-  ScriptValue pipeThrough(ScriptState*,
-                          ScriptValue transform_stream,
-                          ExceptionState&);
-  ScriptValue pipeThrough(ScriptState*,
-                          ScriptValue transform_stream,
-                          ScriptValue options,
-                          ExceptionState&);
-  ScriptPromise pipeTo(ScriptState*, ScriptValue destination, ExceptionState&);
-  ScriptPromise pipeTo(ScriptState*,
-                       ScriptValue destination,
-                       ScriptValue options,
-                       ExceptionState&);
-  ScriptValue tee(ScriptState*, ExceptionState&);
+  virtual bool locked(ScriptState*, ExceptionState&) const = 0;
+  virtual ScriptPromise cancel(ScriptState*, ExceptionState&) = 0;
+  virtual ScriptPromise cancel(ScriptState*,
+                               ScriptValue reason,
+                               ExceptionState&) = 0;
+  virtual ScriptValue getReader(ScriptState*, ExceptionState&) = 0;
+  virtual ScriptValue getReader(ScriptState*,
+                                ScriptValue options,
+                                ExceptionState&) = 0;
+  virtual ScriptValue pipeThrough(ScriptState*,
+                                  ScriptValue transform_stream,
+                                  ExceptionState&) = 0;
+  virtual ScriptValue pipeThrough(ScriptState*,
+                                  ScriptValue transform_stream,
+                                  ScriptValue options,
+                                  ExceptionState&) = 0;
+  virtual ScriptPromise pipeTo(ScriptState*,
+                               ScriptValue destination,
+                               ExceptionState&) = 0;
+  virtual ScriptPromise pipeTo(ScriptState*,
+                               ScriptValue destination,
+                               ScriptValue options,
+                               ExceptionState&) = 0;
+  virtual ScriptValue tee(ScriptState*, ExceptionState&) = 0;
 
-  void Tee(ScriptState*,
-           ReadableStream** branch1,
-           ReadableStream** branch2,
-           ExceptionState&);
+  virtual void Tee(ScriptState*,
+                   ReadableStream** branch1,
+                   ReadableStream** branch2,
+                   ExceptionState&) = 0;
 
-  base::Optional<bool> IsLocked(ScriptState*, ExceptionState&) const;
-  base::Optional<bool> IsDisturbed(ScriptState*, ExceptionState&) const;
-  base::Optional<bool> IsReadable(ScriptState*, ExceptionState&) const;
-  base::Optional<bool> IsClosed(ScriptState*, ExceptionState&) const;
-  base::Optional<bool> IsErrored(ScriptState*, ExceptionState&) const;
+  // Lock the stream and return a handle that permits reading from the stream.
+  // This is a temporary API to abstract away the difference between the two
+  // implementations. It is not possible to unlock the stream again after
+  // calling this.
+  virtual ReadHandle* GetReadHandle(ScriptState*, ExceptionState&) = 0;
+
+  virtual base::Optional<bool> IsLocked(ScriptState*,
+                                        ExceptionState&) const = 0;
+  virtual base::Optional<bool> IsDisturbed(ScriptState*,
+                                           ExceptionState&) const = 0;
+  virtual base::Optional<bool> IsReadable(ScriptState*,
+                                          ExceptionState&) const = 0;
+  virtual base::Optional<bool> IsClosed(ScriptState*,
+                                        ExceptionState&) const = 0;
+  virtual base::Optional<bool> IsErrored(ScriptState*,
+                                         ExceptionState&) const = 0;
 
   // Makes this stream locked and disturbed.
-  void LockAndDisturb(ScriptState*, ExceptionState&);
+  virtual void LockAndDisturb(ScriptState*, ExceptionState&) = 0;
 
   // Serialize this stream to |port|. The stream will be locked by this
   // operation.
-  void Serialize(ScriptState*, MessagePort* port, ExceptionState&);
+  virtual void Serialize(ScriptState*, MessagePort* port, ExceptionState&) = 0;
 
   // Given a |port| which is entangled with a MessagePort that was previously
   // passed to Serialize(), returns a new ReadableStream which behaves like it
@@ -109,18 +128,12 @@
                                      MessagePort* port,
                                      ExceptionState&);
 
-  ScriptValue GetInternalStream(ScriptState* script_state) const;
-
   // In some cases we are known to fail to trace the stream correctly. In such
-  // cases |object_| will be silently gone. This function is for detecting the
-  // issue. Use this function at places where an actual crash happens. Do not
-  // use this function to write "just in case" code.
-  bool IsInternalStreamMissing() const { return object_.IsEmpty(); }
-
- private:
-  class NoopFunction;
-
-  TraceWrapperV8Reference<v8::Object> object_;
+  // cases internal references will be silently lost. This function is for
+  // detecting the issue. Use this function at places where an actual crash
+  // happens. Do not use this function to write "just in case" code.
+  // TODO(ricea): Remove this after switching to the new implementation.
+  virtual bool IsBroken() const = 0;
 };
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/streams/readable_stream_operations.h b/third_party/blink/renderer/core/streams/readable_stream_operations.h
index 7803eb7..92ed0ec6 100644
--- a/third_party/blink/renderer/core/streams/readable_stream_operations.h
+++ b/third_party/blink/renderer/core/streams/readable_stream_operations.h
@@ -19,6 +19,11 @@
 
 // This class has various methods for ReadableStream[Reader] implemented with
 // V8 Extras.
+//
+//     DEPRECATED: None of these functions work correctly with the new C++
+//     implementation of ReadableStream. Use the ReadableStream API directly
+//     instead.
+//
 // All methods should be called in an appropriate V8 context. All ScriptValue
 // arguments must not be empty.
 //
diff --git a/third_party/blink/renderer/core/streams/readable_stream_operations_test.cc b/third_party/blink/renderer/core/streams/readable_stream_operations_test.cc
index 840c87f1..a7989cf 100644
--- a/third_party/blink/renderer/core/streams/readable_stream_operations_test.cc
+++ b/third_party/blink/renderer/core/streams/readable_stream_operations_test.cc
@@ -16,6 +16,7 @@
 #include "third_party/blink/renderer/core/messaging/message_channel.h"
 #include "third_party/blink/renderer/core/streams/readable_stream.h"
 #include "third_party/blink/renderer/core/streams/readable_stream_default_controller_wrapper.h"
+#include "third_party/blink/renderer/core/streams/readable_stream_wrapper.h"
 #include "third_party/blink/renderer/core/streams/test_underlying_source.h"
 #include "third_party/blink/renderer/platform/bindings/exception_state.h"
 #include "third_party/blink/renderer/platform/bindings/script_state.h"
@@ -112,6 +113,23 @@
   Member<Iteration> iteration_;
 };
 
+// Returns the internal V8 Extras implementation of a ReadableStream object.
+// Requires StreamsNative feature to be off.
+ScriptValue CheckedGetInternalStream(ScriptState* script_state,
+                                     ReadableStream* readable_stream) {
+  CHECK(!RuntimeEnabledFeatures::StreamsNativeEnabled());
+  ReadableStreamWrapper* readable_stream_wrapper =
+      static_cast<ReadableStreamWrapper*>(readable_stream);
+  return readable_stream_wrapper->GetInternalStream(script_state);
+}
+
+ScriptValue CheckedGetInternalStream(ScriptState* script_state,
+                                     ScriptValue stream) {
+  ReadableStream* readable_stream =
+      V8ReadableStream::ToImpl(stream.V8Value().As<v8::Object>());
+  return CheckedGetInternalStream(script_state, readable_stream);
+}
+
 TEST(ReadableStreamOperationsTest, IsReadableStream) {
   V8TestingScope scope;
   TryCatchScope try_catch_scope(scope.GetIsolate());
@@ -141,8 +159,7 @@
                                             scope.GetIsolate()));
 
   ScriptValue internal_stream =
-      V8ReadableStream::ToImpl(stream.V8Value().As<v8::Object>())
-          ->GetInternalStream(scope.GetScriptState());
+      CheckedGetInternalStream(scope.GetScriptState(), stream);
   ASSERT_FALSE(internal_stream.IsEmpty());
   EXPECT_TRUE(ReadableStreamOperations::IsReadableStream(
                   scope.GetScriptState(), internal_stream, ASSERT_NO_EXCEPTION)
@@ -185,7 +202,7 @@
   ASSERT_TRUE(stream);
 
   ScriptValue internal_stream =
-      stream->GetInternalStream(scope.GetScriptState());
+      CheckedGetInternalStream(scope.GetScriptState(), stream);
   ASSERT_FALSE(internal_stream.IsEmpty());
 
   EXPECT_EQ(ReadableStreamOperations::IsLocked(
@@ -222,7 +239,7 @@
   ASSERT_TRUE(stream);
 
   ScriptValue internal_stream =
-      stream->GetInternalStream(scope.GetScriptState());
+      CheckedGetInternalStream(scope.GetScriptState(), stream);
   EXPECT_EQ(ReadableStreamOperations::IsDisturbed(
                 scope.GetScriptState(), internal_stream, ASSERT_NO_EXCEPTION),
             base::make_optional(false));
@@ -365,7 +382,8 @@
       scope.GetScriptState(), underlying_source, strategy);
   ASSERT_FALSE(internal_stream.IsEmpty());
 
-  auto* stream = ReadableStream::CreateFromInternalStream(
+  CHECK(!RuntimeEnabledFeatures::StreamsNativeEnabled());
+  auto* stream = ReadableStreamWrapper::CreateFromInternalStream(
       scope.GetScriptState(), internal_stream, ASSERT_NO_EXCEPTION);
   ASSERT_TRUE(stream);
 
@@ -414,17 +432,17 @@
 
   EXPECT_EQ(ReadableStreamOperations::IsReadable(
                 scope.GetScriptState(),
-                readable->GetInternalStream(scope.GetScriptState()),
+                CheckedGetInternalStream(scope.GetScriptState(), readable),
                 ASSERT_NO_EXCEPTION),
             base::make_optional(true));
   EXPECT_EQ(ReadableStreamOperations::IsReadable(
                 scope.GetScriptState(),
-                closed->GetInternalStream(scope.GetScriptState()),
+                CheckedGetInternalStream(scope.GetScriptState(), closed),
                 ASSERT_NO_EXCEPTION),
             base::make_optional(false));
   EXPECT_EQ(ReadableStreamOperations::IsReadable(
                 scope.GetScriptState(),
-                errored->GetInternalStream(scope.GetScriptState()),
+                CheckedGetInternalStream(scope.GetScriptState(), errored),
                 ASSERT_NO_EXCEPTION),
             base::make_optional(false));
 }
@@ -454,17 +472,17 @@
 
   EXPECT_EQ(ReadableStreamOperations::IsClosed(
                 scope.GetScriptState(),
-                readable->GetInternalStream(scope.GetScriptState()),
+                CheckedGetInternalStream(scope.GetScriptState(), readable),
                 ASSERT_NO_EXCEPTION),
             base::make_optional(false));
   EXPECT_EQ(ReadableStreamOperations::IsClosed(
                 scope.GetScriptState(),
-                closed->GetInternalStream(scope.GetScriptState()),
+                CheckedGetInternalStream(scope.GetScriptState(), closed),
                 ASSERT_NO_EXCEPTION),
             base::make_optional(true));
   EXPECT_EQ(ReadableStreamOperations::IsClosed(
                 scope.GetScriptState(),
-                errored->GetInternalStream(scope.GetScriptState()),
+                CheckedGetInternalStream(scope.GetScriptState(), errored),
                 ASSERT_NO_EXCEPTION),
             base::make_optional(false));
 }
@@ -494,17 +512,17 @@
 
   EXPECT_EQ(ReadableStreamOperations::IsErrored(
                 scope.GetScriptState(),
-                readable->GetInternalStream(scope.GetScriptState()),
+                CheckedGetInternalStream(scope.GetScriptState(), readable),
                 ASSERT_NO_EXCEPTION),
             base::make_optional(false));
   EXPECT_EQ(ReadableStreamOperations::IsErrored(
                 scope.GetScriptState(),
-                closed->GetInternalStream(scope.GetScriptState()),
+                CheckedGetInternalStream(scope.GetScriptState(), closed),
                 ASSERT_NO_EXCEPTION),
             base::make_optional(false));
   EXPECT_EQ(ReadableStreamOperations::IsErrored(
                 scope.GetScriptState(),
-                errored->GetInternalStream(scope.GetScriptState()),
+                CheckedGetInternalStream(scope.GetScriptState(), errored),
                 ASSERT_NO_EXCEPTION),
             base::make_optional(true));
 }
@@ -521,7 +539,8 @@
   ASSERT_TRUE(stream);
 
   ScriptValue result = ReadableStreamOperations::Tee(
-      scope.GetScriptState(), stream->GetInternalStream(scope.GetScriptState()),
+      scope.GetScriptState(),
+      CheckedGetInternalStream(scope.GetScriptState(), stream),
       exception_state);
   ASSERT_FALSE(result.IsEmpty());
   ASSERT_TRUE(result.IsObject());
@@ -593,7 +612,7 @@
   source->Enqueue(ScriptValue(scope.GetScriptState(),
                               V8String(scope.GetIsolate(), "hello")));
   ScriptValue internal_stream =
-      stream->GetInternalStream(scope.GetScriptState());
+      CheckedGetInternalStream(scope.GetScriptState(), stream);
   MessageChannel* channel = MessageChannel::Create(scope.GetExecutionContext());
   ReadableStreamOperations::Serialize(scope.GetScriptState(), internal_stream,
                                       channel->port1(), ASSERT_NO_EXCEPTION);
diff --git a/third_party/blink/renderer/core/streams/readable_stream_test.cc b/third_party/blink/renderer/core/streams/readable_stream_test.cc
index 6147c96..8d9bc61 100644
--- a/third_party/blink/renderer/core/streams/readable_stream_test.cc
+++ b/third_party/blink/renderer/core/streams/readable_stream_test.cc
@@ -4,17 +4,21 @@
 
 #include "third_party/blink/renderer/core/streams/readable_stream.h"
 
+#include "base/optional.h"
 #include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/renderer/bindings/core/v8/script_function.h"
 #include "third_party/blink/renderer/bindings/core/v8/script_value.h"
 #include "third_party/blink/renderer/bindings/core/v8/v8_binding_for_core.h"
 #include "third_party/blink/renderer/bindings/core/v8/v8_binding_for_testing.h"
 #include "third_party/blink/renderer/bindings/core/v8/v8_extras_test_utils.h"
+#include "third_party/blink/renderer/bindings/core/v8/v8_iterator_result_value.h"
 #include "third_party/blink/renderer/core/messaging/message_channel.h"
 #include "third_party/blink/renderer/core/streams/readable_stream_default_controller_wrapper.h"
 #include "third_party/blink/renderer/core/streams/test_underlying_source.h"
 #include "third_party/blink/renderer/core/streams/underlying_source_base.h"
 #include "third_party/blink/renderer/platform/bindings/exception_state.h"
 #include "third_party/blink/renderer/platform/bindings/script_state.h"
+#include "third_party/blink/renderer/platform/bindings/string_resource.h"
 #include "third_party/blink/renderer/platform/heap/handle.h"
 #include "third_party/blink/renderer/platform/testing/unit_test_helpers.h"
 #include "v8/include/v8.h"
@@ -430,6 +434,86 @@
             base::make_optional<String>("hello, bye"));
 }
 
+TEST_F(ReadableStreamTest, GetReadHandle) {
+  V8TestingScope scope;
+  ScriptState* script_state = scope.GetScriptState();
+  auto* isolate = scope.GetIsolate();
+
+  auto* underlying_source =
+      MakeGarbageCollected<TestUnderlyingSource>(script_state);
+
+  ScriptValue js_underlying_source = ScriptValue(
+      script_state,
+      ToV8(underlying_source, script_state->GetContext()->Global(), isolate));
+  ReadableStream* stream = ReadableStream::Create(
+      script_state, js_underlying_source, ASSERT_NO_EXCEPTION);
+  ASSERT_TRUE(stream);
+
+  ScriptValue abc = ScriptValue(script_state, V8String(isolate, "abc"));
+  underlying_source->Enqueue(abc);
+  underlying_source->Close();
+
+  ReadableStream::ReadHandle* read_handle =
+      stream->GetReadHandle(script_state, ASSERT_NO_EXCEPTION);
+
+  class CaptureFunction : public ScriptFunction {
+   public:
+    static v8::Local<v8::Function> CreateFunction(
+        ScriptState* script_state,
+        base::Optional<ScriptValue>* out_value) {
+      return MakeGarbageCollected<CaptureFunction>(script_state, out_value)
+          ->BindToV8Function();
+    }
+
+    CaptureFunction(ScriptState* script_state,
+                    base::Optional<ScriptValue>* out_value)
+        : ScriptFunction(script_state), out_value_(out_value) {}
+
+    ScriptValue Call(ScriptValue value) override {
+      *out_value_ = value;
+      return value;
+    }
+
+   private:
+    base::Optional<ScriptValue>* out_value_;
+  };
+
+  base::Optional<ScriptValue> iterator;
+  base::Optional<ScriptValue> not_reached;
+
+  read_handle->Read(script_state)
+      .Then(CaptureFunction::CreateFunction(script_state, &iterator),
+            CaptureFunction::CreateFunction(script_state, &not_reached));
+  // Resolve promise and run callbacks.
+  v8::MicrotasksScope::PerformCheckpoint(isolate);
+
+  ASSERT_TRUE(iterator.has_value());
+  EXPECT_FALSE(not_reached.has_value());
+  ASSERT_FALSE(iterator->IsEmpty());
+
+  bool done = false;
+  auto maybe_value = V8UnpackIteratorResult(
+      script_state, iterator->V8Value().As<v8::Object>(), &done);
+  EXPECT_FALSE(done);
+  auto value_as_string = ToBlinkString<String>(
+      maybe_value.ToLocalChecked().As<v8::String>(), kDoNotExternalize);
+  EXPECT_EQ(value_as_string, "abc");
+
+  iterator.reset();
+  read_handle->Read(script_state)
+      .Then(CaptureFunction::CreateFunction(script_state, &iterator),
+            CaptureFunction::CreateFunction(script_state, &not_reached));
+  v8::MicrotasksScope::PerformCheckpoint(isolate);
+
+  ASSERT_TRUE(iterator.has_value());
+  EXPECT_FALSE(not_reached.has_value());
+  ASSERT_FALSE(iterator->IsEmpty());
+
+  maybe_value = V8UnpackIteratorResult(
+      script_state, iterator->V8Value().As<v8::Object>(), &done);
+  EXPECT_TRUE(done);
+}
+
 }  // namespace
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/streams/readable_stream_wrapper.cc b/third_party/blink/renderer/core/streams/readable_stream_wrapper.cc
new file mode 100644
index 0000000..e855410
--- /dev/null
+++ b/third_party/blink/renderer/core/streams/readable_stream_wrapper.cc
@@ -0,0 +1,601 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/core/streams/readable_stream_wrapper.h"
+
+#include "third_party/blink/renderer/bindings/core/v8/script_function.h"
+#include "third_party/blink/renderer/bindings/core/v8/v8_readable_stream.h"
+#include "third_party/blink/renderer/bindings/core/v8/v8_writable_stream.h"
+#include "third_party/blink/renderer/core/streams/readable_stream_operations.h"
+#include "third_party/blink/renderer/core/streams/retain_wrapper_during_construction.h"
+#include "third_party/blink/renderer/core/streams/writable_stream_wrapper.h"
+#include "third_party/blink/renderer/platform/bindings/exception_state.h"
+#include "third_party/blink/renderer/platform/bindings/v8_binding.h"
+
+namespace blink {
+
+class ReadableStreamWrapper::ReadHandleImpl final
+    : public ReadableStream::ReadHandle {
+ public:
+  ReadHandleImpl(v8::Isolate* isolate, v8::Local<v8::Value> reader)
+      : reader_(isolate, reader) {}
+
+  ~ReadHandleImpl() override = default;
+
+  ScriptPromise Read(ScriptState* script_state) override {
+    return ReadableStreamOperations::DefaultReaderRead(
+        script_state,
+        ScriptValue(script_state,
+                    reader_.NewLocal(script_state->GetIsolate())));
+  }
+
+  void Trace(Visitor* visitor) override {
+    visitor->Trace(reader_);
+    ReadHandle::Trace(visitor);
+  }
+
+ private:
+  TraceWrapperV8Reference<v8::Value> reader_;
+};
+
+void ReadableStreamWrapper::Init(ScriptState* script_state,
+                                 ScriptValue underlying_source,
+                                 ScriptValue strategy,
+                                 ExceptionState& exception_state) {
+  ScriptValue value = ReadableStreamOperations::CreateReadableStream(
+      script_state, underlying_source, strategy, exception_state);
+  if (exception_state.HadException())
+    return;
+
+  DCHECK(value.IsObject());
+  InitWithInternalStream(script_state, value.V8Value().As<v8::Object>(),
+                         exception_state);
+}
+
+void ReadableStreamWrapper::InitWithInternalStream(
+    ScriptState* script_state,
+    v8::Local<v8::Object> object,
+    ExceptionState& exception_state) {
+  DCHECK(ReadableStreamOperations::IsReadableStreamForDCheck(
+      script_state, ScriptValue(script_state, object)));
+  object_.Set(script_state->GetIsolate(), object);
+
+  v8::Isolate* isolate = script_state->GetIsolate();
+  v8::TryCatch block(isolate);
+  v8::Local<v8::Value> wrapper = ToV8(this, script_state);
+  if (wrapper.IsEmpty()) {
+    exception_state.RethrowV8Exception(block.Exception());
+    return;
+  }
+
+  v8::Local<v8::Context> context = script_state->GetContext();
+  v8::Local<v8::Object> bindings =
+      context->GetExtrasBindingObject().As<v8::Object>();
+  v8::Local<v8::Value> symbol_value;
+  if (!bindings->Get(context, V8String(isolate, "internalReadableStreamSymbol"))
+           .ToLocal(&symbol_value)) {
+    exception_state.RethrowV8Exception(block.Exception());
+    return;
+  }
+
+  if (wrapper.As<v8::Object>()
+          ->Set(context, symbol_value.As<v8::Symbol>(), object)
+          .IsNothing()) {
+    exception_state.RethrowV8Exception(block.Exception());
+    return;
+  }
+
+  // This is needed because sometimes a ReadableStream can be detached from
+  // the owner object such as Response.
+  if (!RetainWrapperDuringConstruction(this, script_state)) {
+    exception_state.ThrowDOMException(DOMExceptionCode::kInvalidStateError,
+                                      "Cannot queue task to retain wrapper");
+  }
+}
+
+ReadableStreamWrapper* ReadableStreamWrapper::Create(
+    ScriptState* script_state,
+    ScriptValue underlying_source,
+    ScriptValue strategy,
+    ExceptionState& exception_state) {
+  auto* stream = MakeGarbageCollected<ReadableStreamWrapper>();
+  stream->Init(script_state, underlying_source, strategy, exception_state);
+  if (exception_state.HadException())
+    return nullptr;
+  return stream;
+}
+
+ReadableStreamWrapper* ReadableStreamWrapper::CreateFromInternalStream(
+    ScriptState* script_state,
+    ScriptValue object,
+    ExceptionState& exception_state) {
+  DCHECK(object.IsObject());
+  return CreateFromInternalStream(
+      script_state, object.V8Value().As<v8::Object>(), exception_state);
+}
+
+ReadableStreamWrapper* ReadableStreamWrapper::CreateFromInternalStream(
+    ScriptState* script_state,
+    v8::Local<v8::Object> object,
+    ExceptionState& exception_state) {
+  auto* stream = MakeGarbageCollected<ReadableStreamWrapper>();
+  stream->InitWithInternalStream(script_state, object, exception_state);
+  if (exception_state.HadException())
+    return nullptr;
+  return stream;
+}
+
+ReadableStreamWrapper* ReadableStreamWrapper::CreateWithCountQueueingStrategy(
+    ScriptState* script_state,
+    UnderlyingSourceBase* underlying_source,
+    size_t high_water_mark) {
+  v8::TryCatch block(script_state->GetIsolate());
+  ScriptValue strategy =
+      ReadableStreamOperations::CreateCountQueuingStrategy(script_state, 0);
+  if (strategy.IsEmpty())
+    return nullptr;
+
+  ScriptValue value = ReadableStreamOperations::CreateReadableStream(
+      script_state, underlying_source, strategy);
+  if (value.IsEmpty())
+    return nullptr;
+
+  ExceptionState exception_state(script_state->GetIsolate(),
+                                 ExceptionState::kConstructionContext,
+                                 "ReadableStream");
+  DCHECK(value.V8Value()->IsObject());
+  auto* stream = CreateFromInternalStream(script_state, value, exception_state);
+  if (exception_state.HadException())
+    exception_state.ClearException();
+  return stream;
+}
+
+void ReadableStreamWrapper::Trace(Visitor* visitor) {
+  visitor->Trace(object_);
+  ReadableStream::Trace(visitor);
+}
+
+ScriptPromise ReadableStreamWrapper::cancel(ScriptState* script_state,
+                                            ExceptionState& exception_state) {
+  return cancel(
+      script_state,
+      ScriptValue(script_state, v8::Undefined(script_state->GetIsolate())),
+      exception_state);
+}
+
+bool ReadableStreamWrapper::locked(ScriptState* script_state,
+                                   ExceptionState& exception_state) const {
+  auto result = IsLocked(script_state, exception_state);
+
+  return !result || *result;
+}
+
+ScriptPromise ReadableStreamWrapper::cancel(ScriptState* script_state,
+                                            ScriptValue reason,
+                                            ExceptionState& exception_state) {
+  if (locked(script_state, exception_state) &&
+      !exception_state.HadException()) {
+    exception_state.ThrowTypeError("Cannot cancel a locked stream");
+  }
+
+  if (exception_state.HadException())
+    return ScriptPromise();
+
+  return ReadableStreamOperations::Cancel(
+      script_state, GetInternalStream(script_state), reason, exception_state);
+}
+
+ScriptValue ReadableStreamWrapper::getReader(ScriptState* script_state,
+                                             ExceptionState& exception_state) {
+  return ReadableStreamOperations::GetReader(
+      script_state, GetInternalStream(script_state), exception_state);
+}
+
+ScriptValue ReadableStreamWrapper::getReader(ScriptState* script_state,
+                                             ScriptValue options,
+                                             ExceptionState& exception_state) {
+  v8::TryCatch block(script_state->GetIsolate());
+  v8::Local<v8::Value> mode;
+  v8::Local<v8::String> mode_string;
+  v8::Local<v8::Context> context = script_state->GetContext();
+  if (options.V8Value()->IsUndefined()) {
+    mode = v8::Undefined(script_state->GetIsolate());
+  } else {
+    v8::Local<v8::Object> v8_options;
+    if (!options.V8Value()->ToObject(context).ToLocal(&v8_options)) {
+      exception_state.RethrowV8Exception(block.Exception());
+      return ScriptValue();
+    }
+    if (!v8_options->Get(context, V8String(script_state->GetIsolate(), "mode"))
+             .ToLocal(&mode)) {
+      exception_state.RethrowV8Exception(block.Exception());
+      return ScriptValue();
+    }
+  }
+
+  if (!mode->ToString(context).ToLocal(&mode_string)) {
+    exception_state.RethrowV8Exception(block.Exception());
+    return ScriptValue();
+  }
+  if (ToCoreString(mode_string) == "byob") {
+    exception_state.ThrowTypeError("invalid mode");
+    return ScriptValue();
+  }
+  if (!mode->IsUndefined()) {
+    exception_state.ThrowRangeError("invalid mode");
+    return ScriptValue();
+  }
+  return ReadableStreamOperations::GetReader(
+      script_state, GetInternalStream(script_state), exception_state);
+}
+
+ScriptValue ReadableStreamWrapper::pipeThrough(
+    ScriptState* script_state,
+    ScriptValue transform_stream,
+    ExceptionState& exception_state) {
+  return pipeThrough(
+      script_state, transform_stream,
+      ScriptValue(script_state, v8::Undefined(script_state->GetIsolate())),
+      exception_state);
+}
+
+// https://streams.spec.whatwg.org/#rs-pipe-through
+ScriptValue ReadableStreamWrapper::pipeThrough(
+    ScriptState* script_state,
+    ScriptValue transform_stream,
+    ScriptValue options,
+    ExceptionState& exception_state) {
+  v8::Local<v8::Value> pair_value = transform_stream.V8Value();
+  v8::Local<v8::Context> context = script_state->GetContext();
+
+  constexpr char kWritableIsNotWritableStream[] =
+      "parameter 1's 'writable' property is not a WritableStream.";
+  constexpr char kReadableIsNotReadableStream[] =
+      "parameter 1's 'readable' property is not a ReadableStream.";
+  constexpr char kWritableIsLocked[] = "parameter 1's 'writable' is locked.";
+
+  v8::Local<v8::Object> pair;
+  if (!pair_value->ToObject(context).ToLocal(&pair)) {
+    exception_state.ThrowTypeError(kWritableIsNotWritableStream);
+    return ScriptValue();
+  }
+
+  v8::Isolate* isolate = script_state->GetIsolate();
+  v8::Local<v8::Value> writable, readable;
+  {
+    v8::TryCatch block(isolate);
+    if (!pair->Get(context, V8String(isolate, "writable")).ToLocal(&writable)) {
+      exception_state.RethrowV8Exception(block.Exception());
+      return ScriptValue();
+    }
+    DCHECK(!block.HasCaught());
+
+    if (!pair->Get(context, V8String(isolate, "readable")).ToLocal(&readable)) {
+      exception_state.RethrowV8Exception(block.Exception());
+      return ScriptValue();
+    }
+    DCHECK(!block.HasCaught());
+  }
+
+  // 2. If ! IsWritableStream(_writable_) is *false*, throw a *TypeError*
+  //    exception.
+  WritableStream* dom_writable =
+      V8WritableStream::ToImplWithTypeCheck(isolate, writable);
+  if (!dom_writable) {
+    exception_state.ThrowTypeError(kWritableIsNotWritableStream);
+    return ScriptValue();
+  }
+
+  // 3. If ! IsReadableStream(_readable_) is *false*, throw a *TypeError*
+  //    exception.
+  if (!V8ReadableStream::HasInstance(readable, isolate)) {
+    exception_state.ThrowTypeError(kReadableIsNotReadableStream);
+    return ScriptValue();
+  }
+
+  // TODO(ricea): When aborting pipes is supported, implement step 5:
+  // 5. If _signal_ is not *undefined*, and _signal_ is not an instance of the
+  //    `AbortSignal` interface, throw a *TypeError* exception.
+
+  // 6. If ! IsReadableStreamLocked(*this*) is *true*, throw a *TypeError*
+  //    exception.
+  if (IsLocked(script_state, exception_state).value_or(false)) {
+    exception_state.ThrowTypeError("Cannot pipe a locked stream");
+    return ScriptValue();
+  }
+  if (exception_state.HadException()) {
+    return ScriptValue();
+  }
+
+  // 7. If ! IsWritableStreamLocked(_writable_) is *true*, throw a *TypeError*
+  //    exception.
+  if (dom_writable->IsLocked(script_state, exception_state).value_or(false)) {
+    exception_state.ThrowTypeError(kWritableIsLocked);
+    return ScriptValue();
+  }
+  if (exception_state.HadException()) {
+    return ScriptValue();
+  }
+
+  if (RuntimeEnabledFeatures::StreamsNativeEnabled()) {
+    // TODO(ricea): Replace this with a DCHECK once ReadableStreamNative is
+    // implemented.
+    exception_state.ThrowTypeError(
+        "pipeThrough disabled because StreamsNative feature is enabled");
+    return ScriptValue();
+  }
+
+  // This cast is safe because the following code will only be run when the
+  // native version of WritableStream is not in use.
+  WritableStreamWrapper* writable_wrapper =
+      static_cast<WritableStreamWrapper*>(dom_writable);
+
+  // 8. Let _promise_ be ! ReadableStreamPipeTo(*this*, _writable_,
+  //    _preventClose_, _preventAbort_, _preventCancel_,
+  //   _signal_).
+
+  // TODO(ricea): Maybe change the parameters to
+  // ReadableStreamOperations::PipeTo to match ReadableStreamPipeTo() in the
+  // standard?
+  ScriptPromise promise = ReadableStreamOperations::PipeTo(
+      script_state, GetInternalStream(script_state),
+      writable_wrapper->GetInternalStream(script_state), options,
+      exception_state);
+  if (exception_state.HadException()) {
+    return ScriptValue();
+  }
+
+  // 9. Set _promise_.[[PromiseIsHandled]] to *true*.
+  promise.MarkAsHandled();
+
+  // 10. Return _readable_.
+  return ScriptValue(script_state, readable);
+}
+
+ScriptPromise ReadableStreamWrapper::pipeTo(ScriptState* script_state,
+                                            ScriptValue destination,
+                                            ExceptionState& exception_state) {
+  return pipeTo(
+      script_state, destination,
+      ScriptValue(script_state, v8::Undefined(script_state->GetIsolate())),
+      exception_state);
+}
+
+ScriptPromise ReadableStreamWrapper::pipeTo(ScriptState* script_state,
+                                            ScriptValue destination_value,
+                                            ScriptValue options,
+                                            ExceptionState& exception_state) {
+  WritableStream* destination = V8WritableStream::ToImplWithTypeCheck(
+      script_state->GetIsolate(), destination_value.V8Value());
+
+  if (!destination) {
+    exception_state.ThrowTypeError("Illegal invocation");
+    return ScriptPromise();
+  }
+  if (locked(script_state, exception_state) &&
+      !exception_state.HadException()) {
+    exception_state.ThrowTypeError("Cannot pipe a locked stream");
+    return ScriptPromise();
+  }
+  if (exception_state.HadException())
+    return ScriptPromise();
+  if (destination->locked(script_state, exception_state) &&
+      !exception_state.HadException()) {
+    exception_state.ThrowTypeError("Cannot pipe to a locked stream");
+    return ScriptPromise();
+  }
+  if (exception_state.HadException())
+    return ScriptPromise();
+
+  if (RuntimeEnabledFeatures::StreamsNativeEnabled()) {
+    // TODO(ricea): Replace this with a DCHECK once ReadableStreamNative is
+    // implemented.
+    exception_state.ThrowTypeError(
+        "pipeTo disabled because StreamsNative feature is enabled");
+    return ScriptPromise();
+  }
+
+  // This cast is safe because the following code will only be run when the
+  // native version of WritableStream is not in use.
+  WritableStreamWrapper* destination_wrapper =
+      static_cast<WritableStreamWrapper*>(destination);
+
+  return ReadableStreamOperations::PipeTo(
+      script_state, GetInternalStream(script_state),
+      destination_wrapper->GetInternalStream(script_state), options,
+      exception_state);
+}
+
+ScriptValue ReadableStreamWrapper::tee(ScriptState* script_state,
+                                       ExceptionState& exception_state) {
+  v8::Isolate* isolate = script_state->GetIsolate();
+  ReadableStream* branch1 = nullptr;
+  ReadableStream* branch2 = nullptr;
+
+  Tee(script_state, &branch1, &branch2, exception_state);
+
+  if (!branch1 || !branch2)
+    return ScriptValue();
+
+  DCHECK(!exception_state.HadException());
+
+  v8::TryCatch block(isolate);
+  v8::Local<v8::Context> context = script_state->GetContext();
+  v8::Local<v8::Array> array = v8::Array::New(isolate, 2);
+  v8::Local<v8::Object> global = context->Global();
+
+  v8::Local<v8::Value> v8_branch1 = ToV8(branch1, global, isolate);
+  if (v8_branch1.IsEmpty()) {
+    exception_state.RethrowV8Exception(block.Exception());
+    return ScriptValue();
+  }
+  v8::Local<v8::Value> v8_branch2 = ToV8(branch2, global, isolate);
+  if (v8_branch1.IsEmpty()) {
+    exception_state.RethrowV8Exception(block.Exception());
+    return ScriptValue();
+  }
+  if (array->Set(context, V8String(isolate, "0"), v8_branch1).IsNothing()) {
+    exception_state.RethrowV8Exception(block.Exception());
+    return ScriptValue();
+  }
+  if (array->Set(context, V8String(isolate, "1"), v8_branch2).IsNothing()) {
+    exception_state.RethrowV8Exception(block.Exception());
+    return ScriptValue();
+  }
+  return ScriptValue(script_state, array);
+}
+
+void ReadableStreamWrapper::Tee(ScriptState* script_state,
+                                ReadableStream** branch1,
+                                ReadableStream** branch2,
+                                ExceptionState& exception_state) {
+  v8::Local<v8::Context> context = script_state->GetContext();
+
+  if (locked(script_state, exception_state) &&
+      !exception_state.HadException()) {
+    exception_state.ThrowTypeError("The stream is already locked.");
+  }
+
+  if (exception_state.HadException())
+    return;
+
+  ScriptValue tee_result = ReadableStreamOperations::Tee(
+      script_state, GetInternalStream(script_state), exception_state);
+  if (tee_result.IsEmpty())
+    return;
+
+  DCHECK(!exception_state.HadException());
+  DCHECK(tee_result.V8Value()->IsArray());
+
+  v8::Local<v8::Array> branches = tee_result.V8Value().As<v8::Array>();
+  v8::Local<v8::Value> v8_branch1, v8_branch2;
+  v8::TryCatch block(script_state->GetIsolate());
+
+  if (!branches->Get(context, 0).ToLocal(&v8_branch1)) {
+    exception_state.RethrowV8Exception(block.Exception());
+    return;
+  }
+  if (!branches->Get(context, 1).ToLocal(&v8_branch2)) {
+    exception_state.RethrowV8Exception(block.Exception());
+    return;
+  }
+
+  DCHECK(v8_branch1->IsObject());
+  DCHECK(v8_branch2->IsObject());
+
+  ReadableStreamWrapper* temp_branch1 =
+      MakeGarbageCollected<ReadableStreamWrapper>();
+  ReadableStreamWrapper* temp_branch2 =
+      MakeGarbageCollected<ReadableStreamWrapper>();
+
+  temp_branch1->InitWithInternalStream(
+      script_state, v8_branch1.As<v8::Object>(), exception_state);
+  if (exception_state.HadException())
+    return;
+
+  temp_branch2->InitWithInternalStream(
+      script_state, v8_branch2.As<v8::Object>(), exception_state);
+  if (exception_state.HadException())
+    return;
+
+  *branch1 = temp_branch1;
+  *branch2 = temp_branch2;
+}
+
+ReadableStream::ReadHandle* ReadableStreamWrapper::GetReadHandle(
+    ScriptState* script_state,
+    ExceptionState& exception_state) {
+  ScriptValue reader = ReadableStreamOperations::GetReader(
+      script_state, GetInternalStream(script_state), exception_state);
+  if (exception_state.HadException()) {
+    return nullptr;
+  }
+  return MakeGarbageCollected<ReadHandleImpl>(script_state->GetIsolate(),
+                                              reader.V8Value());
+}
+
+base::Optional<bool> ReadableStreamWrapper::IsLocked(
+    ScriptState* script_state,
+    ExceptionState& exception_state) const {
+  return ReadableStreamOperations::IsLocked(
+      script_state, GetInternalStream(script_state), exception_state);
+}
+
+base::Optional<bool> ReadableStreamWrapper::IsDisturbed(
+    ScriptState* script_state,
+    ExceptionState& exception_state) const {
+  return ReadableStreamOperations::IsDisturbed(
+      script_state, GetInternalStream(script_state), exception_state);
+}
+
+base::Optional<bool> ReadableStreamWrapper::IsReadable(
+    ScriptState* script_state,
+    ExceptionState& exception_state) const {
+  return ReadableStreamOperations::IsReadable(
+      script_state, GetInternalStream(script_state), exception_state);
+}
+
+base::Optional<bool> ReadableStreamWrapper::IsClosed(
+    ScriptState* script_state,
+    ExceptionState& exception_state) const {
+  return ReadableStreamOperations::IsClosed(
+      script_state, GetInternalStream(script_state), exception_state);
+}
+
+base::Optional<bool> ReadableStreamWrapper::IsErrored(
+    ScriptState* script_state,
+    ExceptionState& exception_state) const {
+  return ReadableStreamOperations::IsErrored(
+      script_state, GetInternalStream(script_state), exception_state);
+}
+
+void ReadableStreamWrapper::LockAndDisturb(ScriptState* script_state,
+                                           ExceptionState& exception_state) {
+  ScriptState::Scope scope(script_state);
+
+  const base::Optional<bool> is_locked =
+      IsLocked(script_state, exception_state);
+  if (!is_locked || is_locked.value())
+    return;
+
+  ScriptValue reader = getReader(script_state, exception_state);
+  if (reader.IsEmpty())
+    return;
+
+  ScriptPromise promise =
+      ReadableStreamOperations::DefaultReaderRead(script_state, reader);
+  promise.MarkAsHandled();
+}
+
+void ReadableStreamWrapper::Serialize(ScriptState* script_state,
+                                      MessagePort* port,
+                                      ExceptionState& exception_state) {
+  ReadableStreamOperations::Serialize(
+      script_state, GetInternalStream(script_state), port, exception_state);
+}
+
+// static
+ReadableStreamWrapper* ReadableStreamWrapper::Deserialize(
+    ScriptState* script_state,
+    MessagePort* port,
+    ExceptionState& exception_state) {
+  // We need to execute V8 Extras JavaScript to create the new ReadableStream.
+  // We will not run author code.
+  v8::Isolate::AllowJavascriptExecutionScope allow_js(
+      script_state->GetIsolate());
+  ScriptValue internal_stream = ReadableStreamOperations::Deserialize(
+      script_state, port, exception_state);
+  if (exception_state.HadException())
+    return nullptr;
+  return CreateFromInternalStream(script_state, internal_stream,
+                                  exception_state);
+}
+
+ScriptValue ReadableStreamWrapper::GetInternalStream(
+    ScriptState* script_state) const {
+  return ScriptValue(script_state,
+                     object_.NewLocal(script_state->GetIsolate()));
+}
+
+}  // namespace blink
diff --git a/third_party/blink/renderer/core/streams/readable_stream_wrapper.h b/third_party/blink/renderer/core/streams/readable_stream_wrapper.h
new file mode 100644
index 0000000..108e261
--- /dev/null
+++ b/third_party/blink/renderer/core/streams/readable_stream_wrapper.h
@@ -0,0 +1,126 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_CORE_STREAMS_READABLE_STREAM_WRAPPER_H_
+#define THIRD_PARTY_BLINK_RENDERER_CORE_STREAMS_READABLE_STREAM_WRAPPER_H_
+
+#include "base/optional.h"
+#include "third_party/blink/renderer/bindings/core/v8/script_value.h"
+#include "third_party/blink/renderer/core/streams/readable_stream.h"
+#include "third_party/blink/renderer/platform/bindings/trace_wrapper_v8_reference.h"
+#include "v8/include/v8.h"
+
+namespace blink {
+
+// This is an implementation of the corresponding IDL interface.
+// Use TraceWrapperMember to hold a reference to an instance of this class.
+class CORE_EXPORT ReadableStreamWrapper : public ReadableStream {
+ public:
+  // Call one of Init functions before using the instance.
+  ReadableStreamWrapper() = default;
+  ~ReadableStreamWrapper() override = default;
+
+  // If an error happens, |exception_state.HadException()| will be true, and
+  // |this| will not be usable after that.
+  void Init(ScriptState*,
+            ScriptValue underlying_source,
+            ScriptValue strategy,
+            ExceptionState& exception_state);
+  void InitWithInternalStream(ScriptState*,
+                              v8::Local<v8::Object> object,
+                              ExceptionState& exception_state);
+
+  // Create* functions call Init* internally and returns null when an error
+  // happens.
+  static ReadableStreamWrapper* Create(ScriptState*,
+                                       ScriptValue underlying_source,
+                                       ScriptValue strategy,
+                                       ExceptionState&);
+  static ReadableStreamWrapper* CreateFromInternalStream(
+      ScriptState*,
+      v8::Local<v8::Object> object,
+      ExceptionState&);
+  static ReadableStreamWrapper* CreateFromInternalStream(ScriptState*,
+                                                         ScriptValue object,
+                                                         ExceptionState&);
+  // This function doesn't take ExceptionState because the caller cannot have
+  // one. Returns null when an error happens.
+  static ReadableStreamWrapper* CreateWithCountQueueingStrategy(
+      ScriptState*,
+      UnderlyingSourceBase* underlying_source,
+      size_t high_water_mark);
+
+  void Trace(Visitor* visitor) override;
+
+  // IDL defined functions
+  bool locked(ScriptState*, ExceptionState&) const override;
+  ScriptPromise cancel(ScriptState*, ExceptionState&) override;
+  ScriptPromise cancel(ScriptState*,
+                       ScriptValue reason,
+                       ExceptionState&) override;
+  ScriptValue getReader(ScriptState*, ExceptionState&) override;
+  ScriptValue getReader(ScriptState*,
+                        ScriptValue options,
+                        ExceptionState&) override;
+  ScriptValue pipeThrough(ScriptState*,
+                          ScriptValue transform_stream,
+                          ExceptionState&) override;
+  ScriptValue pipeThrough(ScriptState*,
+                          ScriptValue transform_stream,
+                          ScriptValue options,
+                          ExceptionState&) override;
+  ScriptPromise pipeTo(ScriptState*,
+                       ScriptValue destination,
+                       ExceptionState&) override;
+  ScriptPromise pipeTo(ScriptState*,
+                       ScriptValue destination,
+                       ScriptValue options,
+                       ExceptionState&) override;
+  ScriptValue tee(ScriptState*, ExceptionState&) override;
+
+  void Tee(ScriptState*,
+           ReadableStream** branch1,
+           ReadableStream** branch2,
+           ExceptionState&) override;
+
+  ReadHandle* GetReadHandle(ScriptState*, ExceptionState&) override;
+
+  base::Optional<bool> IsLocked(ScriptState*, ExceptionState&) const override;
+  base::Optional<bool> IsDisturbed(ScriptState*,
+                                   ExceptionState&) const override;
+  base::Optional<bool> IsReadable(ScriptState*, ExceptionState&) const override;
+  base::Optional<bool> IsClosed(ScriptState*, ExceptionState&) const override;
+  base::Optional<bool> IsErrored(ScriptState*, ExceptionState&) const override;
+
+  // Makes this stream locked and disturbed.
+  void LockAndDisturb(ScriptState*, ExceptionState&) override;
+
+  // Serialize this stream to |port|. The stream will be locked by this
+  // operation.
+  void Serialize(ScriptState*, MessagePort* port, ExceptionState&) override;
+
+  // Given a |port| which is entangled with a MessagePort that was previously
+  // passed to Serialize(), returns a new ReadableStreamWrapper which behaves
+  // like it was the original.
+  static ReadableStreamWrapper* Deserialize(ScriptState*,
+                                            MessagePort* port,
+                                            ExceptionState&);
+
+  ScriptValue GetInternalStream(ScriptState* script_state) const;
+
+  // In some cases we are known to fail to trace the stream correctly. In such
+  // cases |object_| will be silently gone. This function is for detecting the
+  // issue. Use this function at places where an actual crash happens. Do not
+  // use this function to write "just in case" code.
+  bool IsBroken() const override { return object_.IsEmpty(); }
+
+ private:
+  class ReadHandleImpl;
+
+  TraceWrapperV8Reference<v8::Object> object_;
+};
+
+}  // namespace blink
+
+#endif  // THIRD_PARTY_BLINK_RENDERER_CORE_STREAMS_READABLE_STREAM_WRAPPER_H_
diff --git a/third_party/blink/renderer/core/streams/transform_stream.cc b/third_party/blink/renderer/core/streams/transform_stream.cc
index 77f74ea..b016805d2 100644
--- a/third_party/blink/renderer/core/streams/transform_stream.cc
+++ b/third_party/blink/renderer/core/streams/transform_stream.cc
@@ -10,6 +10,7 @@
 #include "third_party/blink/renderer/bindings/core/v8/script_value.h"
 #include "third_party/blink/renderer/bindings/core/v8/v8_script_runner.h"
 #include "third_party/blink/renderer/core/streams/readable_stream.h"
+#include "third_party/blink/renderer/core/streams/readable_stream_wrapper.h"
 #include "third_party/blink/renderer/core/streams/transform_stream_default_controller.h"
 #include "third_party/blink/renderer/core/streams/transform_stream_transformer.h"
 #include "third_party/blink/renderer/core/streams/writable_stream_wrapper.h"
@@ -233,7 +234,7 @@
   }
 
   DCHECK(readable->IsObject());
-  readable_ = ReadableStream::CreateFromInternalStream(
+  readable_ = ReadableStreamWrapper::CreateFromInternalStream(
       script_state, readable.As<v8::Object>(), exception_state);
 
   if (!readable_)
diff --git a/third_party/blink/renderer/core/testing/sim/sim_compositor.cc b/third_party/blink/renderer/core/testing/sim/sim_compositor.cc
index 0d6a1dd..f797e92 100644
--- a/third_party/blink/renderer/core/testing/sim/sim_compositor.cc
+++ b/third_party/blink/renderer/core/testing/sim/sim_compositor.cc
@@ -57,9 +57,14 @@
 
   last_frame_time_ += base::TimeDelta::FromSecondsD(time_delta_in_seconds);
 
+  SimCanvas::Commands commands;
+  paint_commands_ = &commands;
+
   layer_tree_view_->layer_tree_host()->Composite(last_frame_time_,
                                                  /*raster=*/false);
-  return PaintFrame();
+
+  paint_commands_ = nullptr;
+  return commands;
 }
 
 SimCanvas::Commands SimCompositor::PaintFrame() {
@@ -96,6 +101,7 @@
   web_view_->MainFrameWidget()->BeginFrame(last_frame_time_, false);
   web_view_->MainFrameWidget()->UpdateAllLifecyclePhases(
       WebWidget::LifecycleUpdateReason::kTest);
+  *paint_commands_ = PaintFrame();
 }
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/testing/sim/sim_compositor.h b/third_party/blink/renderer/core/testing/sim/sim_compositor.h
index 259e28e..6809f3a 100644
--- a/third_party/blink/renderer/core/testing/sim/sim_compositor.h
+++ b/third_party/blink/renderer/core/testing/sim/sim_compositor.h
@@ -107,6 +107,8 @@
 
   base::TimeTicks last_frame_time_;
 
+  SimCanvas::Commands* paint_commands_;
+
   std::unique_ptr<cc::ScopedDeferMainFrameUpdate>
       scoped_defer_main_frame_update_;
 };
diff --git a/third_party/blink/renderer/core/testing/sim/sim_network.cc b/third_party/blink/renderer/core/testing/sim/sim_network.cc
index e76da21..12119ee4 100644
--- a/third_party/blink/renderer/core/testing/sim/sim_network.cc
+++ b/third_party/blink/renderer/core/testing/sim/sim_network.cc
@@ -97,9 +97,9 @@
   response.SetMIMEType(request.mime_type_);
 
   if (request.redirect_url_.IsEmpty()) {
-    response.SetHTTPStatusCode(200);
+    response.SetHttpStatusCode(200);
   } else {
-    response.SetHTTPStatusCode(302);
+    response.SetHttpStatusCode(302);
     response.AddHTTPHeaderField("Location", request.redirect_url_);
   }
 
@@ -117,7 +117,7 @@
   SimRequestBase* request = it->value;
   params->response = WebURLResponse(params->url);
   params->response.SetMIMEType(request->mime_type_);
-  params->response.SetHTTPStatusCode(200);
+  params->response.SetHttpStatusCode(200);
   auto body_loader = std::make_unique<StaticDataNavigationBodyLoader>();
   request->UsedForNavigation(body_loader.get());
   params->body_loader = std::move(body_loader);
diff --git a/third_party/blink/renderer/core/timing/performance.cc b/third_party/blink/renderer/core/timing/performance.cc
index 393d92fc..5c850ab 100644
--- a/third_party/blink/renderer/core/timing/performance.cc
+++ b/third_party/blink/renderer/core/timing/performance.cc
@@ -38,8 +38,10 @@
 #include "third_party/blink/renderer/core/dom/document.h"
 #include "third_party/blink/renderer/core/dom/document_timing.h"
 #include "third_party/blink/renderer/core/dom/events/event.h"
+#include "third_party/blink/renderer/core/execution_context/execution_context.h"
 #include "third_party/blink/renderer/core/frame/local_frame.h"
 #include "third_party/blink/renderer/core/frame/use_counter.h"
+#include "third_party/blink/renderer/core/inspector/console_message.h"
 #include "third_party/blink/renderer/core/loader/document_load_timing.h"
 #include "third_party/blink/renderer/core/loader/document_loader.h"
 #include "third_party/blink/renderer/core/timing/performance_element_timing.h"
@@ -235,12 +237,30 @@
   return entries;
 }
 
-PerformanceEntryVector Performance::getEntriesByType(
+PerformanceEntryVector Performance::getBufferedEntriesByType(
     const AtomicString& entry_type) {
-  PerformanceEntryVector entries;
   PerformanceEntry::EntryType type =
       PerformanceEntry::ToEntryTypeEnum(entry_type);
+  return getEntriesByTypeInternal(type);
+}
 
+PerformanceEntryVector Performance::getEntriesByType(
+    const AtomicString& entry_type) {
+  PerformanceEntry::EntryType type =
+      PerformanceEntry::ToEntryTypeEnum(entry_type);
+  if (!PerformanceEntry::IsValidTimelineEntryType(type)) {
+    PerformanceEntryVector empty_entries;
+    String message = "Deprecated API for given entry type.";
+    GetExecutionContext()->AddConsoleMessage(ConsoleMessage::Create(
+        kJSMessageSource, kWarningMessageLevel, message));
+    return empty_entries;
+  }
+  return getEntriesByTypeInternal(type);
+}
+
+PerformanceEntryVector Performance::getEntriesByTypeInternal(
+    PerformanceEntry::EntryType type) {
+  PerformanceEntryVector entries;
   switch (type) {
     case PerformanceEntry::kResource:
       for (const auto& resource : resource_timing_buffer_)
diff --git a/third_party/blink/renderer/core/timing/performance.h b/third_party/blink/renderer/core/timing/performance.h
index c5d352bd..aad8b37d 100644
--- a/third_party/blink/renderer/core/timing/performance.h
+++ b/third_party/blink/renderer/core/timing/performance.h
@@ -118,6 +118,12 @@
   }
 
   PerformanceEntryVector getEntries();
+  // Get BufferedEntriesByType will return all entries in the buffer regardless
+  // of whether they are exposed in the Performance Timeline. getEntriesByType
+  // will only return all entries for existing types in
+  // PerformanceEntry.IsValidTimelineEntryType.
+  PerformanceEntryVector getBufferedEntriesByType(
+      const AtomicString& entry_type);
   PerformanceEntryVector getEntriesByType(const AtomicString& entry_type);
   PerformanceEntryVector getEntriesByName(const AtomicString& name,
                                           const AtomicString& entry_type);
@@ -283,6 +289,8 @@
                                         ExceptionState&);
 
   void CopySecondaryBuffer();
+  PerformanceEntryVector getEntriesByTypeInternal(
+      PerformanceEntry::EntryType type);
 
  protected:
   Performance(TimeTicks time_origin,
diff --git a/third_party/blink/renderer/core/timing/performance_entry.h b/third_party/blink/renderer/core/timing/performance_entry.h
index ff96175f..a6a1420 100644
--- a/third_party/blink/renderer/core/timing/performance_entry.h
+++ b/third_party/blink/renderer/core/timing/performance_entry.h
@@ -94,6 +94,22 @@
   static PerformanceEntry::EntryType ToEntryTypeEnum(
       const AtomicString& entry_type);
 
+  // Entries of the types listed here will be accessible from the
+  // PerformanceTimeline or the PerformanceObserver.  Those not listed will
+  // only be available via the PerformanceObserver.
+  // Note: Currently buffered flags don't support long task entries
+  // so leaving it out of this list for now.
+  static bool IsValidTimelineEntryType(const PerformanceEntryType& entry_type) {
+    if (entry_type == kInvalid) {
+      return true;
+    }
+    DEFINE_THREAD_SAFE_STATIC_LOCAL(
+        HashSet<PerformanceEntryType>, valid_timeline_entry_types,
+        ({kNavigation, kMark, kMeasure, kResource, kTaskAttribution, kPaint,
+          kEvent, kFirstInput, kElement, kLayoutJank}));
+    return valid_timeline_entry_types.Contains(entry_type);
+  }
+
  protected:
   PerformanceEntry(const AtomicString& name,
                    double start_time,
diff --git a/third_party/blink/renderer/core/timing/performance_observer.cc b/third_party/blink/renderer/core/timing/performance_observer.cc
index 1cb5429..9e124dc 100644
--- a/third_party/blink/renderer/core/timing/performance_observer.cc
+++ b/third_party/blink/renderer/core/timing/performance_observer.cc
@@ -23,6 +23,7 @@
 #include "third_party/blink/renderer/core/workers/worker_global_scope.h"
 #include "third_party/blink/renderer/platform/bindings/exception_messages.h"
 #include "third_party/blink/renderer/platform/bindings/exception_state.h"
+#include "third_party/blink/renderer/platform/runtime_enabled_features.h"
 #include "third_party/blink/renderer/platform/timer.h"
 
 namespace blink {
@@ -99,6 +100,7 @@
     return;
   }
 
+  bool is_buffered = false;
   if (observer_init->hasEntryTypes()) {
     if (observer_init->hasType()) {
       exception_state.ThrowDOMException(
@@ -129,6 +131,14 @@
           kJSMessageSource, kWarningMessageLevel, message));
       return;
     }
+    if (RuntimeEnabledFeatures::PerformanceObserverBufferedFlagEnabled() &&
+        observer_init->buffered()) {
+      String message =
+          "The Performance Observer does not support buffered flag with "
+          "entryTypes. ";
+      GetExecutionContext()->AddConsoleMessage(ConsoleMessage::Create(
+          kJSMessageSource, kWarningMessageLevel, message));
+    }
     filter_options_ = entry_types;
   } else {
     if (!observer_init->hasType()) {
@@ -155,6 +165,32 @@
           kJSMessageSource, kWarningMessageLevel, message));
       return;
     }
+    if (filter_options_ & entry_type) {
+      String message =
+          "The Performance Observer has already been called with this "
+          "entryType";
+      GetExecutionContext()->AddConsoleMessage(ConsoleMessage::Create(
+          kJSMessageSource, kWarningMessageLevel, message));
+      return;
+    }
+    if (RuntimeEnabledFeatures::PerformanceObserverBufferedFlagEnabled() &&
+        observer_init->buffered()) {
+      if (entry_type == PerformanceEntry::kLongTask) {
+        String message =
+            "Buffered flag does not support the long task entry type ";
+        GetExecutionContext()->AddConsoleMessage(ConsoleMessage::Create(
+            kJSMessageSource, kWarningMessageLevel, message));
+      } else {
+        // Append all entries of this type to the current performance_entries_
+        // to be returned on the next callback.
+        performance_entries_.AppendVector(
+            performance_->getBufferedEntriesByType(
+                AtomicString(observer_init->type())));
+        std::sort(performance_entries_.begin(), performance_entries_.end(),
+                  PerformanceEntry::StartTimeCompareLessThan);
+        is_buffered = true;
+      }
+    }
     filter_options_ |= entry_type;
   }
   if (filter_options_ & PerformanceEntry::kLayoutJank) {
@@ -166,6 +202,9 @@
   else
     performance_->RegisterPerformanceObserver(*this);
   is_registered_ = true;
+  if (is_buffered) {
+    performance_->ActivateObserver(*this);
+  }
 }
 
 void PerformanceObserver::disconnect() {
diff --git a/third_party/blink/renderer/core/timing/performance_observer_init.idl b/third_party/blink/renderer/core/timing/performance_observer_init.idl
index 08e0440..2feff6d 100644
--- a/third_party/blink/renderer/core/timing/performance_observer_init.idl
+++ b/third_party/blink/renderer/core/timing/performance_observer_init.idl
@@ -7,4 +7,5 @@
 dictionary PerformanceObserverInit {
     sequence<DOMString> entryTypes;
     DOMString type;
+    [RuntimeEnabled=PerformanceObserverBufferedFlag] boolean buffered = false;
 };
diff --git a/third_party/blink/renderer/core/timing/performance_observer_test.cc b/third_party/blink/renderer/core/timing/performance_observer_test.cc
index d4e9fcc4..c307df6 100644
--- a/third_party/blink/renderer/core/timing/performance_observer_test.cc
+++ b/third_party/blink/renderer/core/timing/performance_observer_test.cc
@@ -10,6 +10,7 @@
 #include "third_party/blink/renderer/bindings/core/v8/v8_performance_observer_callback.h"
 #include "third_party/blink/renderer/core/execution_context/execution_context.h"
 #include "third_party/blink/renderer/core/timing/performance.h"
+#include "third_party/blink/renderer/core/timing/performance_layout_jank.h"
 #include "third_party/blink/renderer/core/timing/performance_mark.h"
 #include "third_party/blink/renderer/core/timing/performance_observer_init.h"
 #include "third_party/blink/renderer/core/timing/window_performance.h"
@@ -62,6 +63,27 @@
   EXPECT_TRUE(IsRegistered());
 }
 
+TEST_F(PerformanceObserverTest, ObserveWithBufferedFlag) {
+  V8TestingScope scope;
+  Initialize(scope.GetScriptState());
+
+  NonThrowableExceptionState exception_state;
+  PerformanceObserverInit* options = PerformanceObserverInit::Create();
+  options->setType("layoutJank");
+  options->setBuffered(true);
+  EXPECT_EQ(0, NumPerformanceEntries());
+
+  // add a layoutjank to performance so getEntries() returns it
+  PerformanceLayoutJank* entry = PerformanceLayoutJank::Create(1234);
+  base_->AddLayoutJankBuffer(*entry);
+
+  // call observe with the buffered flag
+  observer_->observe(options, exception_state);
+  EXPECT_TRUE(IsRegistered());
+  // Verify that the entry was added to the performance entries
+  EXPECT_EQ(1, NumPerformanceEntries());
+}
+
 TEST_F(PerformanceObserverTest, Enqueue) {
   V8TestingScope scope;
   Initialize(scope.GetScriptState());
diff --git a/third_party/blink/renderer/core/trustedtypes/trusted_type_policy_factory.idl b/third_party/blink/renderer/core/trustedtypes/trusted_type_policy_factory.idl
index 29c5c167..a34cc910 100644
--- a/third_party/blink/renderer/core/trustedtypes/trusted_type_policy_factory.idl
+++ b/third_party/blink/renderer/core/trustedtypes/trusted_type_policy_factory.idl
@@ -12,8 +12,8 @@
     [Unforgeable] TrustedTypePolicy getExposedPolicy(DOMString policyName);
     // All the policy object names that have been created
     [Affects=Nothing, Unforgeable] sequence<DOMString> getPolicyNames();
-    [CallWith=ScriptState, Unforgeable] boolean isHTML(object? checkedObject);
-    [CallWith=ScriptState, Unforgeable] boolean isScript(object? checkedObject);
-    [CallWith=ScriptState, Unforgeable] boolean isScriptURL(object? checkedObject);
-    [CallWith=ScriptState, Unforgeable] boolean isURL(object? checkedObject);
+    [CallWith=ScriptState, Unforgeable] boolean isHTML(any checkedObject);
+    [CallWith=ScriptState, Unforgeable] boolean isScript(any checkedObject);
+    [CallWith=ScriptState, Unforgeable] boolean isScriptURL(any checkedObject);
+    [CallWith=ScriptState, Unforgeable] boolean isURL(any checkedObject);
 };
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 da75f930..1a8721d 100644
--- a/third_party/blink/renderer/core/workers/worker_global_scope.cc
+++ b/third_party/blink/renderer/core/workers/worker_global_scope.cc
@@ -173,14 +173,13 @@
                             : stringOrUrl.GetAsTrustedScriptURL()->toString();
     string_urls.push_back(string_url);
   }
-  importScriptsFromStrings(string_urls, exception_state);
+  ImportScriptsInternal(string_urls, exception_state);
 }
 
-// Implementation of the "importScripts()" algorithm:
-// https://html.spec.whatwg.org/C/#dom-workerglobalscope-importscripts
-void WorkerGlobalScope::importScriptsFromStrings(
-    const Vector<String>& urls,
-    ExceptionState& exception_state) {
+// Implementation of the "import scripts into worker global scope" algorithm:
+// https://html.spec.whatwg.org/C/#import-scripts-into-worker-global-scope
+void WorkerGlobalScope::ImportScriptsInternal(const Vector<String>& urls,
+                                              ExceptionState& exception_state) {
   DCHECK(GetContentSecurityPolicy());
   DCHECK(GetExecutionContext());
 
@@ -192,10 +191,18 @@
     return;
   }
 
-  ExecutionContext& execution_context = *this->GetExecutionContext();
+  // Step 2: "Let settings object be the current settings object."
+  // |this| roughly corresponds to the current settings object.
+
+  // Step 3: "If urls is empty, return."
+  if (urls.IsEmpty())
+    return;
+
+  // Step 4: "Parse each value in urls relative to settings object. If any fail,
+  // throw a "SyntaxError" DOMException."
   Vector<KURL> completed_urls;
   for (const String& url_string : urls) {
-    const KURL& url = execution_context.CompleteURL(url_string);
+    const KURL& url = CompleteURL(url_string);
     if (!url.IsValid()) {
       exception_state.ThrowDOMException(
           DOMExceptionCode::kSyntaxError,
@@ -212,22 +219,18 @@
     completed_urls.push_back(url);
   }
 
+  // Step 5: "For each url in the resulting URL records, run these substeps:"
   for (const KURL& complete_url : completed_urls) {
     KURL response_url;
     String source_code;
     std::unique_ptr<Vector<uint8_t>> cached_meta_data;
-    LoadResult result = LoadResult::kNotHandled;
-    result = LoadScriptFromInstalledScriptsManager(
-        complete_url, &response_url, &source_code, &cached_meta_data);
 
-    // If the script wasn't provided by the InstalledScriptsManager, load from
-    // ResourceLoader.
-    if (result == LoadResult::kNotHandled) {
-      result = LoadScriptFromClassicScriptLoader(
-          complete_url, &response_url, &source_code, &cached_meta_data);
-    }
-
-    if (result != LoadResult::kSuccess) {
+    // Step 5.1: "Fetch a classic worker-imported script given url and settings
+    // object, passing along any custom perform the fetch steps provided. If
+    // this succeeds, let script be the result. Otherwise, rethrow the
+    // exception."
+    if (!FetchClassicImportedScript(complete_url, &response_url, &source_code,
+                                    &cached_meta_data)) {
       // TODO(vogelheim): In case of certain types of failure - e.g. 'nosniff'
       // block - this ought to be a DOMExceptionCode::kSecurityError, but that
       // information presently gets lost on the way.
@@ -242,10 +245,12 @@
     // enough.
     // TODO(yhirano): Remove this ad-hoc logic and use the response type.
     const SanitizeScriptErrors sanitize_script_errors =
-        execution_context.GetSecurityOrigin()->CanReadContent(response_url)
+        GetSecurityOrigin()->CanReadContent(response_url)
             ? SanitizeScriptErrors::kDoNotSanitize
             : SanitizeScriptErrors::kSanitize;
 
+    // Step 5.2: "Run the classic script script, with the rethrow errors
+    // argument set to true."
     ErrorEvent* error_event = nullptr;
     SingleCachedMetadataHandler* handler(
         CreateWorkerScriptCachedMetadataHandler(complete_url,
@@ -264,34 +269,34 @@
   }
 }
 
-WorkerGlobalScope::LoadResult
-WorkerGlobalScope::LoadScriptFromInstalledScriptsManager(
+// Implementation of the "fetch a classic worker-imported script" algorithm.
+// https://html.spec.whatwg.org/C/#fetch-a-classic-worker-imported-script
+bool WorkerGlobalScope::FetchClassicImportedScript(
     const KURL& script_url,
     KURL* out_response_url,
     String* out_source_code,
     std::unique_ptr<Vector<uint8_t>>* out_cached_meta_data) {
-  if (!GetThread()->GetInstalledScriptsManager() ||
-      !GetThread()->GetInstalledScriptsManager()->IsScriptInstalled(
-          script_url)) {
-    return LoadResult::kNotHandled;
+  // InstalledScriptsManager is now used only for starting installed service
+  // workers.
+  // TODO(nhiroki): Consider moving this into ServiceWorkerGlobalScope.
+  InstalledScriptsManager* installed_scripts_manager =
+      GetThread()->GetInstalledScriptsManager();
+  if (installed_scripts_manager &&
+      installed_scripts_manager->IsScriptInstalled(script_url)) {
+    DCHECK(IsServiceWorkerGlobalScope());
+    std::unique_ptr<InstalledScriptsManager::ScriptData> script_data =
+        installed_scripts_manager->GetScriptData(script_url);
+    if (!script_data)
+      return false;
+    *out_response_url = script_url;
+    *out_source_code = script_data->TakeSourceText();
+    *out_cached_meta_data = script_data->TakeMetaData();
+    // TODO(shimazu): Add appropriate probes for inspector.
+    return true;
   }
-  std::unique_ptr<InstalledScriptsManager::ScriptData> script_data =
-      GetThread()->GetInstalledScriptsManager()->GetScriptData(script_url);
-  if (!script_data)
-    return LoadResult::kFailed;
-  *out_response_url = script_url;
-  *out_source_code = script_data->TakeSourceText();
-  *out_cached_meta_data = script_data->TakeMetaData();
-  // TODO(shimazu): Add appropriate probes for inspector.
-  return LoadResult::kSuccess;
-}
 
-WorkerGlobalScope::LoadResult
-WorkerGlobalScope::LoadScriptFromClassicScriptLoader(
-    const KURL& script_url,
-    KURL* out_response_url,
-    String* out_source_code,
-    std::unique_ptr<Vector<uint8_t>>* out_cached_meta_data) {
+  // If the script wasn't provided by the InstalledScriptsManager, load from
+  // ResourceLoader.
   ExecutionContext* execution_context = GetExecutionContext();
   WorkerClassicScriptLoader* classic_script_loader =
       MakeGarbageCollected<WorkerClassicScriptLoader>();
@@ -300,18 +305,14 @@
       *execution_context, Fetcher(), script_url,
       mojom::RequestContextType::SCRIPT,
       execution_context->GetSecurityContext().AddressSpace());
-
-  // If the fetching attempt failed, throw a NetworkError exception and
-  // abort all these steps.
   if (classic_script_loader->Failed())
-    return LoadResult::kFailed;
-
+    return false;
   *out_response_url = classic_script_loader->ResponseURL();
   *out_source_code = classic_script_loader->SourceText();
   *out_cached_meta_data = classic_script_loader->ReleaseCachedMetadata();
   probe::ScriptImported(execution_context, classic_script_loader->Identifier(),
                         classic_script_loader->SourceText());
-  return LoadResult::kSuccess;
+  return true;
 }
 
 bool WorkerGlobalScope::IsContextThread() const {
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 a581a4d0..c5777272f 100644
--- a/third_party/blink/renderer/core/workers/worker_global_scope.h
+++ b/third_party/blink/renderer/core/workers/worker_global_scope.h
@@ -201,9 +201,6 @@
   mojom::ScriptType GetScriptType() const { return script_type_; }
 
  private:
-  virtual void importScriptsFromStrings(const Vector<String>& urls,
-                                        ExceptionState&);
-
   void SetWorkerSettings(std::unique_ptr<WorkerSettings>);
 
   void DidReceiveResponseForClassicScript(
@@ -211,28 +208,9 @@
   void DidImportClassicScript(WorkerClassicScriptLoader* classic_script_loader,
                               const v8_inspector::V8StackTraceId& stack_id);
 
-  // |kNotHandled| is used when the script was not in
-  // InstalledScriptsManager, which means it was not an installed script.
-  enum class LoadResult { kSuccess, kFailed, kNotHandled };
-
-  // Tries to load the script synchronously from the
-  // InstalledScriptsManager, which holds scripts that are sent from the browser
-  // upon starting an installed worker. This blocks until the script is
-  // received. If the script load could not be handled by the
-  // InstalledScriptsManager, e.g. when the script was not an installed script,
-  // returns LoadResult::kNotHandled.
-  // TODO(crbug.com/753350): Factor out LoadScriptFrom* into a new class which
-  // provides the worker's scripts.
-  LoadResult LoadScriptFromInstalledScriptsManager(
-      const KURL& script_url,
-      KURL* out_response_url,
-      String* out_source_code,
-      std::unique_ptr<Vector<uint8_t>>* out_cached_meta_data);
-
-  // Tries to load the script synchronously from the WorkerClassicScriptLoader,
-  // which requests the script from the browser. This blocks until the script is
-  // received.
-  LoadResult LoadScriptFromClassicScriptLoader(
+  // Used for importScripts().
+  void ImportScriptsInternal(const Vector<String>& urls, ExceptionState&);
+  bool FetchClassicImportedScript(
       const KURL& script_url,
       KURL* out_response_url,
       String* out_source_code,
diff --git a/third_party/blink/renderer/devtools/front_end/javascript_metadata/NativeFunctions.js b/third_party/blink/renderer/devtools/front_end/javascript_metadata/NativeFunctions.js
index 81bff084..587b9a9 100644
--- a/third_party/blink/renderer/devtools/front_end/javascript_metadata/NativeFunctions.js
+++ b/third_party/blink/renderer/devtools/front_end/javascript_metadata/NativeFunctions.js
@@ -1,2 +1,2 @@
 // Generated from scripts/javascript_natives/index.js
-JavaScriptMetadata.NativeFunctions = [{"name":"find","signatures":[["predicate","?thisArg"]],"receiver":"Array"},{"name":"find","signatures":[["predicate","?thisArg"]],"receiver":"ReadonlyArray"},{"name":"find","signatures":[["predicate","?thisArg"]],"receiver":"Int8Array"},{"name":"find","signatures":[["predicate","?thisArg"]],"receiver":"Uint8Array"},{"name":"find","signatures":[["predicate","?thisArg"]],"receiver":"Uint8ClampedArray"},{"name":"find","signatures":[["predicate","?thisArg"]],"receiver":"Int16Array"},{"name":"find","signatures":[["predicate","?thisArg"]],"receiver":"Uint16Array"},{"name":"find","signatures":[["predicate","?thisArg"]],"receiver":"Int32Array"},{"name":"find","signatures":[["predicate","?thisArg"]],"receiver":"Uint32Array"},{"name":"find","signatures":[["predicate","?thisArg"]],"receiver":"Float32Array"},{"name":"find","signatures":[["predicate","?thisArg"]],"receiver":"Float64Array"},{"name":"find","signatures":[["?string","?caseSensitive","?backwards","?wrap","?wholeWord","?searchInFrames","?showDialog"]],"receiver":"Window"},{"name":"findIndex","signatures":[["predicate","?thisArg"]]},{"name":"fill","signatures":[["value","?start","?end"]],"receiver":"Array"},{"name":"fill","signatures":[["value","?start","?end"]],"receiver":"Int8Array"},{"name":"fill","signatures":[["value","?start","?end"]],"receiver":"Uint8Array"},{"name":"fill","signatures":[["value","?start","?end"]],"receiver":"Uint8ClampedArray"},{"name":"fill","signatures":[["value","?start","?end"]],"receiver":"Int16Array"},{"name":"fill","signatures":[["value","?start","?end"]],"receiver":"Uint16Array"},{"name":"fill","signatures":[["value","?start","?end"]],"receiver":"Int32Array"},{"name":"fill","signatures":[["value","?start","?end"]],"receiver":"Uint32Array"},{"name":"fill","signatures":[["value","?start","?end"]],"receiver":"Float32Array"},{"name":"fill","signatures":[["value","?start","?end"]],"receiver":"Float64Array"},{"name":"fill","signatures":[["?winding"],["path","?winding"]],"receiver":"CanvasRenderingContext2D"},{"name":"fill","signatures":[["?winding"],["path","?winding"]],"receiver":"OffscreenCanvasRenderingContext2D"},{"name":"fill","signatures":[["?winding"],["path","?winding"]],"receiver":"PaintRenderingContext2D"},{"name":"copyWithin","signatures":[["target","start","?end"]]},{"name":"from","signatures":[["arrayLike","?mapfn","?thisArg"],["iterable","?mapfn","?thisArg"]],"static":true},{"name":"from","signatures":[["arrayLike","?mapfn","?thisArg"]],"static":true},{"name":"from","signatures":[["arrayLike","?mapfn","?thisArg"]],"static":true},{"name":"from","signatures":[["arrayLike","?mapfn","?thisArg"]],"static":true},{"name":"from","signatures":[["arrayLike","?mapfn","?thisArg"]],"static":true},{"name":"from","signatures":[["arrayLike","?mapfn","?thisArg"]],"static":true},{"name":"from","signatures":[["arrayLike","?mapfn","?thisArg"]],"static":true},{"name":"from","signatures":[["arrayLike","?mapfn","?thisArg"]],"static":true},{"name":"from","signatures":[["arrayLike","?mapfn","?thisArg"]],"static":true},{"name":"from","signatures":[["arrayLike","?mapfn","?thisArg"]],"static":true},{"name":"of","signatures":[["...items"]]},{"name":"clz32","signatures":[["x"]]},{"name":"imul","signatures":[["x","y"]]},{"name":"sign","signatures":[["x"]],"receiver":"Math"},{"name":"sign","signatures":[["algorithm","key","data"]],"receiver":"SubtleCrypto"},{"name":"log10","signatures":[["x"]]},{"name":"log2","signatures":[["x"]]},{"name":"log1p","signatures":[["x"]]},{"name":"expm1","signatures":[["x"]]},{"name":"cosh","signatures":[["x"]]},{"name":"sinh","signatures":[["x"]]},{"name":"tanh","signatures":[["x"]]},{"name":"acosh","signatures":[["x"]]},{"name":"asinh","signatures":[["x"]]},{"name":"atanh","signatures":[["x"]]},{"name":"hypot","signatures":[["...values"]]},{"name":"trunc","signatures":[["x"]]},{"name":"fround","signatures":[["x"]]},{"name":"cbrt","signatures":[["x"]]},{"name":"isFinite","signatures":[["number"]]},{"name":"isInteger","signatures":[["number"]]},{"name":"isNaN","signatures":[["number"]]},{"name":"isSafeInteger","signatures":[["number"]]},{"name":"parseFloat","signatures":[["string"]]},{"name":"parseInt","signatures":[["string","?radix"]],"static":true},{"name":"parseInt","signatures":[["s","?radix"]],"receiver":"Window"},{"name":"hasOwnProperty","signatures":[["v"]]},{"name":"propertyIsEnumerable","signatures":[["v"]]},{"name":"assign","signatures":[["target","source"],["target","...sources"],["target","source1","source2","?source3"]],"static":true},{"name":"assign","signatures":[["url"]],"receiver":"Location"},{"name":"getOwnPropertySymbols","signatures":[["o"]]},{"name":"is","signatures":[["value1","value2"]]},{"name":"setPrototypeOf","signatures":[["o","proto"]],"static":true},{"name":"setPrototypeOf","signatures":[["target","v"]],"receiver":"ProxyHandler"},{"name":"getOwnPropertyDescriptor","signatures":[["o","propertyKey"],["o","p"]],"static":true},{"name":"getOwnPropertyDescriptor","signatures":[["target","p"]],"receiver":"ProxyHandler"},{"name":"defineProperty","signatures":[["o","propertyKey","attributes"],["o","p","attributes"]],"static":true},{"name":"defineProperty","signatures":[["target","p","attributes"]],"receiver":"ProxyHandler"},{"name":"codePointAt","signatures":[["pos"]]},{"name":"includes","signatures":[["searchString","?position"]],"receiver":"String"},{"name":"includes","signatures":[["searchElement","?fromIndex"]],"receiver":"Array"},{"name":"includes","signatures":[["searchElement","?fromIndex"]],"receiver":"ReadonlyArray"},{"name":"includes","signatures":[["searchElement","?fromIndex"]],"receiver":"Int8Array"},{"name":"includes","signatures":[["searchElement","?fromIndex"]],"receiver":"Uint8Array"},{"name":"includes","signatures":[["searchElement","?fromIndex"]],"receiver":"Uint8ClampedArray"},{"name":"includes","signatures":[["searchElement","?fromIndex"]],"receiver":"Int16Array"},{"name":"includes","signatures":[["searchElement","?fromIndex"]],"receiver":"Uint16Array"},{"name":"includes","signatures":[["searchElement","?fromIndex"]],"receiver":"Int32Array"},{"name":"includes","signatures":[["searchElement","?fromIndex"]],"receiver":"Uint32Array"},{"name":"includes","signatures":[["searchElement","?fromIndex"]],"receiver":"Float32Array"},{"name":"includes","signatures":[["searchElement","?fromIndex"]],"receiver":"Float64Array"},{"name":"includes","signatures":[["key"]],"receiver":"IDBKeyRange"},{"name":"endsWith","signatures":[["searchString","?endPosition"]]},{"name":"normalize","signatures":[["?form"]],"receiver":"String"},{"name":"repeat","signatures":[["count"]]},{"name":"startsWith","signatures":[["searchString","?position"]]},{"name":"anchor","signatures":[["name"]]},{"name":"fontcolor","signatures":[["color"]]},{"name":"fontsize","signatures":[["size"]]},{"name":"link","signatures":[["url"]]},{"name":"sub","signatures":[["typedArray","index","value"]],"receiver":"Atomics"},{"name":"sub","signatures":[["...values"]],"receiver":"CSSNumericValue"},{"name":"fromCodePoint","signatures":[["...codePoints"]]},{"name":"raw","signatures":[["template","...substitutions"]]},{"name":"clear","signatures":[["mask"]],"receiver":"WebGLRenderingContextBase"},{"name":"delete","signatures":[["key"]],"receiver":"Map"},{"name":"delete","signatures":[["key"]],"receiver":"WeakMap"},{"name":"delete","signatures":[["value"]],"receiver":"Set"},{"name":"delete","signatures":[["value"]],"receiver":"WeakSet"},{"name":"delete","signatures":[["property"]],"receiver":"StylePropertyMap"},{"name":"delete","signatures":[["key"]],"receiver":"Headers"},{"name":"delete","signatures":[["name"]],"receiver":"FormData"},{"name":"delete","signatures":[["name"]],"receiver":"URLSearchParams"},{"name":"delete","signatures":[["cacheName"]],"receiver":"CacheStorage"},{"name":"delete","signatures":[["request","?options"]],"receiver":"Cache"},{"name":"delete","signatures":[["options"],["name","?options"]],"receiver":"CookieStore"},{"name":"delete","signatures":[["key"]],"receiver":"IDBObjectStore"},{"name":"delete","signatures":[["instrumentKey"]],"receiver":"PaymentInstruments"},{"name":"forEach","signatures":[["callbackfn","?thisArg"]]},{"name":"get","signatures":[["key"]],"receiver":"Map"},{"name":"get","signatures":[["key"]],"receiver":"ReadonlyMap"},{"name":"get","signatures":[["key"]],"receiver":"WeakMap"},{"name":"get","signatures":[["target","p","receiver"]],"receiver":"ProxyHandler"},{"name":"get","signatures":[["property"]],"receiver":"StylePropertyMapReadOnly"},{"name":"get","signatures":[["key"]],"receiver":"Headers"},{"name":"get","signatures":[["name"]],"receiver":"CustomElementRegistry"},{"name":"get","signatures":[["name"]],"receiver":"FormData"},{"name":"get","signatures":[["name"]],"receiver":"URLSearchParams"},{"name":"get","signatures":[["id"]],"receiver":"BackgroundFetchManager"},{"name":"get","signatures":[["?options"],["name","?options"]],"receiver":"CookieStore"},{"name":"get","signatures":[["?options"]],"receiver":"CredentialsContainer"},{"name":"get","signatures":[["keyId"]],"receiver":"MediaKeyStatusMap"},{"name":"get","signatures":[["key"]],"receiver":"IDBIndex"},{"name":"get","signatures":[["key"]],"receiver":"IDBObjectStore"},{"name":"get","signatures":[["instrumentKey"]],"receiver":"PaymentInstruments"},{"name":"get","signatures":[["id"]],"receiver":"Clients"},{"name":"has","signatures":[["key"]],"receiver":"Map"},{"name":"has","signatures":[["key"]],"receiver":"ReadonlyMap"},{"name":"has","signatures":[["key"]],"receiver":"WeakMap"},{"name":"has","signatures":[["value"]],"receiver":"Set"},{"name":"has","signatures":[["value"]],"receiver":"ReadonlySet"},{"name":"has","signatures":[["value"]],"receiver":"WeakSet"},{"name":"has","signatures":[["target","p"]],"receiver":"ProxyHandler"},{"name":"has","signatures":[["property"]],"receiver":"StylePropertyMapReadOnly"},{"name":"has","signatures":[["key"]],"receiver":"Headers"},{"name":"has","signatures":[["name"]],"receiver":"FormData"},{"name":"has","signatures":[["name"]],"receiver":"URLSearchParams"},{"name":"has","signatures":[["cacheName"]],"receiver":"CacheStorage"},{"name":"has","signatures":[["?options"],["name","?options"]],"receiver":"CookieStore"},{"name":"has","signatures":[["keyId"]],"receiver":"MediaKeyStatusMap"},{"name":"has","signatures":[["instrumentKey"]],"receiver":"PaymentInstruments"},{"name":"set","signatures":[["key","value"]],"receiver":"Map"},{"name":"set","signatures":[["key","value"]],"receiver":"WeakMap"},{"name":"set","signatures":[["target","p","value","receiver"]],"receiver":"ProxyHandler"},{"name":"set","signatures":[["v"]],"receiver":"PropertyDescriptor"},{"name":"set","signatures":[["array","?offset"]],"receiver":"Int8Array"},{"name":"set","signatures":[["array","?offset"]],"receiver":"Uint8Array"},{"name":"set","signatures":[["array","?offset"]],"receiver":"Uint8ClampedArray"},{"name":"set","signatures":[["array","?offset"]],"receiver":"Int16Array"},{"name":"set","signatures":[["array","?offset"]],"receiver":"Uint16Array"},{"name":"set","signatures":[["array","?offset"]],"receiver":"Int32Array"},{"name":"set","signatures":[["array","?offset"]],"receiver":"Uint32Array"},{"name":"set","signatures":[["array","?offset"]],"receiver":"Float32Array"},{"name":"set","signatures":[["array","?offset"]],"receiver":"Float64Array"},{"name":"set","signatures":[["property","...values"]],"receiver":"StylePropertyMap"},{"name":"set","signatures":[["key","value"]],"receiver":"Headers"},{"name":"set","signatures":[["name","value","?filename"]],"receiver":"FormData"},{"name":"set","signatures":[["name","value"]],"receiver":"URLSearchParams"},{"name":"set","signatures":[["options"],["name","value","?options"]],"receiver":"CookieStore"},{"name":"set","signatures":[["instrumentKey","details"]],"receiver":"PaymentInstruments"},{"name":"add","signatures":[["value"]],"receiver":"Set"},{"name":"add","signatures":[["value"]],"receiver":"WeakSet"},{"name":"add","signatures":[["typedArray","index","value"]],"receiver":"Atomics"},{"name":"add","signatures":[["file"],["data","type"]],"receiver":"DataTransferItemList"},{"name":"add","signatures":[["...values"]],"receiver":"CSSNumericValue"},{"name":"add","signatures":[["node","?before"]],"receiver":"AccessibleNodeList"},{"name":"add","signatures":[["...tokens"]],"receiver":"DOMTokenList"},{"name":"add","signatures":[["element","?before"]],"receiver":"HTMLOptionsCollection"},{"name":"add","signatures":[["element","?before"]],"receiver":"HTMLSelectElement"},{"name":"add","signatures":[["request"]],"receiver":"Cache"},{"name":"add","signatures":[["value","?key"]],"receiver":"IDBObjectStore"},{"name":"all","signatures":[["values"]]},{"name":"race","signatures":[["values"]]},{"name":"reject","signatures":[["reason"]]},{"name":"resolve","signatures":[["?value"]]},{"name":"toString","signatures":[["?radix"]],"receiver":"Number"},{"name":"for","signatures":[["key"]]},{"name":"keyFor","signatures":[["sym"]]},{"name":"next","signatures":[["?value"]]},{"name":"return","signatures":[["?value"]]},{"name":"throw","signatures":[["?e"]]},{"name":"entries","signatures":[["o"]],"static":true},{"name":"keys","signatures":[["o"]],"static":true},{"name":"keys","signatures":[["?request","?options"]],"receiver":"Cache"},{"name":"values","signatures":[["o"]],"static":true},{"name":"getPrototypeOf","signatures":[["target"]],"receiver":"ProxyHandler"},{"name":"getPrototypeOf","signatures":[["o"]],"static":true},{"name":"isExtensible","signatures":[["target"]],"receiver":"ProxyHandler"},{"name":"isExtensible","signatures":[["o"]],"static":true},{"name":"preventExtensions","signatures":[["target"]],"receiver":"ProxyHandler"},{"name":"preventExtensions","signatures":[["o"]],"static":true},{"name":"deleteProperty","signatures":[["target","p"]]},{"name":"enumerate","signatures":[["target"]]},{"name":"ownKeys","signatures":[["target"]]},{"name":"apply","signatures":[["target","thisArg","?argArray"]],"receiver":"ProxyHandler"},{"name":"apply","signatures":[["thisArg","?argArray"]],"receiver":"Function"},{"name":"construct","signatures":[["target","argArray","?newTarget"]]},{"name":"revocable","signatures":[["target","handler"]]},{"name":"match","signatures":[["matcher"],["regexp"]],"receiver":"String"},{"name":"match","signatures":[["request"]],"receiver":"BackgroundFetchSettledFetches"},{"name":"match","signatures":[["request","?options"]],"receiver":"CacheStorage"},{"name":"match","signatures":[["request","?options"]],"receiver":"Cache"},{"name":"replace","signatures":[["searchValue","replaceValue"],["searchValue","replacer"]],"receiver":"String"},{"name":"replace","signatures":[["token","newToken"]],"receiver":"DOMTokenList"},{"name":"replace","signatures":[["url"]],"receiver":"Location"},{"name":"search","signatures":[["searcher"],["regexp"]]},{"name":"split","signatures":[["splitter","?limit"],["separator","?limit"]]},{"name":"eval","signatures":[["x"]]},{"name":"decodeURI","signatures":[["encodedURI"]]},{"name":"decodeURIComponent","signatures":[["encodedURIComponent"]]},{"name":"encodeURI","signatures":[["uri"]]},{"name":"encodeURIComponent","signatures":[["uriComponent"]]},{"name":"escape","signatures":[["string"]],"receiver":"Window"},{"name":"escape","signatures":[["ident"]],"static":true},{"name":"escape","signatures":[["html"]],"static":true},{"name":"unescape","signatures":[["string"]]},{"name":"toLocaleString","signatures":[["?locales","?options"]],"receiver":"Date"},{"name":"toLocaleString","signatures":[["?locales","?options"]],"receiver":"Number"},{"name":"isPrototypeOf","signatures":[["v"]]},{"name":"getOwnPropertyNames","signatures":[["o"]]},{"name":"create","signatures":[["o","?properties"]],"static":true},{"name":"create","signatures":[["url"]],"static":true},{"name":"create","signatures":[["?options"]],"receiver":"CredentialsContainer"},{"name":"defineProperties","signatures":[["o","properties"]]},{"name":"seal","signatures":[["o"]]},{"name":"freeze","signatures":[["a"],["f"],["o"]]},{"name":"isSealed","signatures":[["o"]]},{"name":"isFrozen","signatures":[["o"]]},{"name":"call","signatures":[["thisArg","...argArray"]]},{"name":"bind","signatures":[["thisArg","...argArray"]]},{"name":"charAt","signatures":[["pos"]]},{"name":"charCodeAt","signatures":[["index"]]},{"name":"concat","signatures":[["...strings"]],"receiver":"String"},{"name":"concat","signatures":[["...items"]],"receiver":"ReadonlyArray"},{"name":"concat","signatures":[["...items"]],"receiver":"Array"},{"name":"indexOf","signatures":[["searchString","?position"]],"receiver":"String"},{"name":"indexOf","signatures":[["searchElement","?fromIndex"]],"receiver":"ReadonlyArray"},{"name":"indexOf","signatures":[["searchElement","?fromIndex"]],"receiver":"Array"},{"name":"indexOf","signatures":[["searchElement","?fromIndex"]],"receiver":"Int8Array"},{"name":"indexOf","signatures":[["searchElement","?fromIndex"]],"receiver":"Uint8Array"},{"name":"indexOf","signatures":[["searchElement","?fromIndex"]],"receiver":"Uint8ClampedArray"},{"name":"indexOf","signatures":[["searchElement","?fromIndex"]],"receiver":"Int16Array"},{"name":"indexOf","signatures":[["searchElement","?fromIndex"]],"receiver":"Uint16Array"},{"name":"indexOf","signatures":[["searchElement","?fromIndex"]],"receiver":"Int32Array"},{"name":"indexOf","signatures":[["searchElement","?fromIndex"]],"receiver":"Uint32Array"},{"name":"indexOf","signatures":[["searchElement","?fromIndex"]],"receiver":"Float32Array"},{"name":"indexOf","signatures":[["searchElement","?fromIndex"]],"receiver":"Float64Array"},{"name":"lastIndexOf","signatures":[["searchString","?position"]],"receiver":"String"},{"name":"lastIndexOf","signatures":[["searchElement","?fromIndex"]],"receiver":"ReadonlyArray"},{"name":"lastIndexOf","signatures":[["searchElement","?fromIndex"]],"receiver":"Array"},{"name":"lastIndexOf","signatures":[["searchElement","?fromIndex"]],"receiver":"Int8Array"},{"name":"lastIndexOf","signatures":[["searchElement","?fromIndex"]],"receiver":"Uint8Array"},{"name":"lastIndexOf","signatures":[["searchElement","?fromIndex"]],"receiver":"Uint8ClampedArray"},{"name":"lastIndexOf","signatures":[["searchElement","?fromIndex"]],"receiver":"Int16Array"},{"name":"lastIndexOf","signatures":[["searchElement","?fromIndex"]],"receiver":"Uint16Array"},{"name":"lastIndexOf","signatures":[["searchElement","?fromIndex"]],"receiver":"Int32Array"},{"name":"lastIndexOf","signatures":[["searchElement","?fromIndex"]],"receiver":"Uint32Array"},{"name":"lastIndexOf","signatures":[["searchElement","?fromIndex"]],"receiver":"Float32Array"},{"name":"lastIndexOf","signatures":[["searchElement","?fromIndex"]],"receiver":"Float64Array"},{"name":"localeCompare","signatures":[["that","?locales","?options"]]},{"name":"slice","signatures":[["?start","?end"]],"receiver":"String"},{"name":"slice","signatures":[["?start","?end"]],"receiver":"ReadonlyArray"},{"name":"slice","signatures":[["?start","?end"]],"receiver":"ConcatArray"},{"name":"slice","signatures":[["?start","?end"]],"receiver":"Array"},{"name":"slice","signatures":[["begin","?end"]],"receiver":"ArrayBuffer"},{"name":"slice","signatures":[["?start","?end"]],"receiver":"Int8Array"},{"name":"slice","signatures":[["?start","?end"]],"receiver":"Uint8Array"},{"name":"slice","signatures":[["?start","?end"]],"receiver":"Uint8ClampedArray"},{"name":"slice","signatures":[["?start","?end"]],"receiver":"Int16Array"},{"name":"slice","signatures":[["?start","?end"]],"receiver":"Uint16Array"},{"name":"slice","signatures":[["?start","?end"]],"receiver":"Int32Array"},{"name":"slice","signatures":[["?start","?end"]],"receiver":"Uint32Array"},{"name":"slice","signatures":[["?start","?end"]],"receiver":"Float32Array"},{"name":"slice","signatures":[["?start","?end"]],"receiver":"Float64Array"},{"name":"slice","signatures":[["begin","?end"]],"receiver":"SharedArrayBuffer"},{"name":"slice","signatures":[["?start","?end","?contentType"]],"receiver":"Blob"},{"name":"substring","signatures":[["start","?end"]]},{"name":"substr","signatures":[["from","?length"]]},{"name":"fromCharCode","signatures":[["...codes"]]},{"name":"toFixed","signatures":[["?fractionDigits"]]},{"name":"toExponential","signatures":[["?fractionDigits"]]},{"name":"toPrecision","signatures":[["?precision"]]},{"name":"abs","signatures":[["x"]]},{"name":"acos","signatures":[["x"]]},{"name":"asin","signatures":[["x"]]},{"name":"atan","signatures":[["x"]]},{"name":"atan2","signatures":[["y","x"]]},{"name":"ceil","signatures":[["x"]]},{"name":"cos","signatures":[["x"]]},{"name":"exp","signatures":[["x"]]},{"name":"floor","signatures":[["x"]]},{"name":"log","signatures":[["x"]],"receiver":"Math"},{"name":"log","signatures":[["...data"]],"receiver":"console"},{"name":"max","signatures":[["...values"]]},{"name":"min","signatures":[["...values"]]},{"name":"pow","signatures":[["x","y"]]},{"name":"round","signatures":[["x"]]},{"name":"sin","signatures":[["x"]]},{"name":"sqrt","signatures":[["x"]]},{"name":"tan","signatures":[["x"]]},{"name":"toLocaleDateString","signatures":[["?locales","?options"]]},{"name":"toLocaleTimeString","signatures":[["?locales","?options"]]},{"name":"setTime","signatures":[["time"]]},{"name":"setMilliseconds","signatures":[["ms"]]},{"name":"setUTCMilliseconds","signatures":[["ms"]]},{"name":"setSeconds","signatures":[["sec","?ms"]]},{"name":"setUTCSeconds","signatures":[["sec","?ms"]]},{"name":"setMinutes","signatures":[["min","?sec","?ms"]]},{"name":"setUTCMinutes","signatures":[["min","?sec","?ms"]]},{"name":"setHours","signatures":[["hours","?min","?sec","?ms"]]},{"name":"setUTCHours","signatures":[["hours","?min","?sec","?ms"]]},{"name":"setDate","signatures":[["date"]]},{"name":"setUTCDate","signatures":[["date"]]},{"name":"setMonth","signatures":[["month","?date"]]},{"name":"setUTCMonth","signatures":[["month","?date"]]},{"name":"setFullYear","signatures":[["year","?month","?date"]]},{"name":"setUTCFullYear","signatures":[["year","?month","?date"]]},{"name":"toJSON","signatures":[["?key"]]},{"name":"parse","signatures":[["s"]],"static":true},{"name":"parse","signatures":[["text","?reviver"]],"receiver":"JSON"},{"name":"parse","signatures":[["cssText"]],"static":true},{"name":"UTC","signatures":[["year","month","?date","?hours","?minutes","?seconds","?ms"]]},{"name":"exec","signatures":[["string"]]},{"name":"test","signatures":[["string"]]},{"name":"stringify","signatures":[["value","?replacer","?space"]]},{"name":"join","signatures":[["?separator"]]},{"name":"every","signatures":[["callbackfn","?thisArg"]]},{"name":"some","signatures":[["callbackfn","?thisArg"]]},{"name":"map","signatures":[["callbackfn","?thisArg"]]},{"name":"filter","signatures":[["callbackfn","?thisArg"]]},{"name":"reduce","signatures":[["callbackfn","?initialValue"]]},{"name":"reduceRight","signatures":[["callbackfn","?initialValue"]]},{"name":"push","signatures":[["...items"]],"receiver":"Array"},{"name":"push","signatures":[["message","?options"]],"receiver":"NFC"},{"name":"sort","signatures":[["?compareFn"]],"receiver":"Array"},{"name":"sort","signatures":[["?compareFn"]],"receiver":"Int8Array"},{"name":"sort","signatures":[["?compareFn"]],"receiver":"Uint8Array"},{"name":"sort","signatures":[["?compareFn"]],"receiver":"Uint8ClampedArray"},{"name":"sort","signatures":[["?compareFn"]],"receiver":"Int16Array"},{"name":"sort","signatures":[["?compareFn"]],"receiver":"Uint16Array"},{"name":"sort","signatures":[["?compareFn"]],"receiver":"Int32Array"},{"name":"sort","signatures":[["?compareFn"]],"receiver":"Uint32Array"},{"name":"sort","signatures":[["?compareFn"]],"receiver":"Float32Array"},{"name":"sort","signatures":[["?compareFn"]],"receiver":"Float64Array"},{"name":"splice","signatures":[["start","?deleteCount","...items"]]},{"name":"unshift","signatures":[["...items"]]},{"name":"isArray","signatures":[["arg"]]},{"name":"then","signatures":[["?onfulfilled","?onrejected"]]},{"name":"catch","signatures":[["?onrejected"]]},{"name":"isView","signatures":[["arg"]]},{"name":"getFloat32","signatures":[["byteOffset","?littleEndian"]]},{"name":"getFloat64","signatures":[["byteOffset","?littleEndian"]]},{"name":"getInt8","signatures":[["byteOffset"]]},{"name":"getInt16","signatures":[["byteOffset","?littleEndian"]]},{"name":"getInt32","signatures":[["byteOffset","?littleEndian"]]},{"name":"getUint8","signatures":[["byteOffset"]]},{"name":"getUint16","signatures":[["byteOffset","?littleEndian"]]},{"name":"getUint32","signatures":[["byteOffset","?littleEndian"]]},{"name":"setFloat32","signatures":[["byteOffset","value","?littleEndian"]]},{"name":"setFloat64","signatures":[["byteOffset","value","?littleEndian"]]},{"name":"setInt8","signatures":[["byteOffset","value"]]},{"name":"setInt16","signatures":[["byteOffset","value","?littleEndian"]]},{"name":"setInt32","signatures":[["byteOffset","value","?littleEndian"]]},{"name":"setUint8","signatures":[["byteOffset","value"]]},{"name":"setUint16","signatures":[["byteOffset","value","?littleEndian"]]},{"name":"setUint32","signatures":[["byteOffset","value","?littleEndian"]]},{"name":"subarray","signatures":[["begin","?end"]]},{"name":"getOwnPropertyDescriptors","signatures":[["o"]]},{"name":"and","signatures":[["typedArray","index","value"]]},{"name":"compareExchange","signatures":[["typedArray","index","expectedValue","replacementValue"]]},{"name":"exchange","signatures":[["typedArray","index","value"]]},{"name":"isLockFree","signatures":[["size"]]},{"name":"load","signatures":[["typedArray","index"]],"receiver":"Atomics"},{"name":"load","signatures":[["font","?text"]],"receiver":"FontFaceSet"},{"name":"load","signatures":[["sessionId"]],"receiver":"MediaKeySession"},{"name":"or","signatures":[["typedArray","index","value"]]},{"name":"store","signatures":[["typedArray","index","value"]],"receiver":"Atomics"},{"name":"store","signatures":[["credential"]],"receiver":"CredentialsContainer"},{"name":"wait","signatures":[["typedArray","index","value","?timeout"]]},{"name":"wake","signatures":[["typedArray","index","count"]]},{"name":"xor","signatures":[["typedArray","index","value"]]},{"name":"padStart","signatures":[["maxLength","?fillString"]]},{"name":"padEnd","signatures":[["maxLength","?fillString"]]},{"name":"formatToParts","signatures":[["?date"]]},{"name":"finally","signatures":[["?onfinally"]]},{"name":"flatMap","signatures":[["callback","?thisArg"]]},{"name":"flatten","signatures":[["?depth"]]},{"name":"updateTiming","signatures":[["?timing"]]},{"name":"Animation","signatures":[["?effect","?timeline"]]},{"name":"cancel","signatures":[["?reason"]],"receiver":"UnderlyingSourceBase"},{"name":"animate","signatures":[["keyframes","?options"]]},{"name":"setKeyframes","signatures":[["keyframes"]]},{"name":"null","signatures":[["index"]],"receiver":"DataTransferItemList"},{"name":"null","signatures":[["index"]],"receiver":"CSSKeyframesRule"},{"name":"null","signatures":[["name"],["property","propertyValue"]],"receiver":"CSSStyleDeclaration"},{"name":"null","signatures":[["index"]],"receiver":"CSSNumericArray"},{"name":"null","signatures":[["index","?val"]],"receiver":"CSSTransformValue"},{"name":"null","signatures":[["index","?val"]],"receiver":"CSSUnparsedValue"},{"name":"null","signatures":[["name"]],"receiver":"StyleSheetList"},{"name":"null","signatures":[["index","node"]],"receiver":"AccessibleNodeList"},{"name":"null","signatures":[["name","?value"]],"receiver":"DOMStringMap"},{"name":"null","signatures":[["index"],["name"]],"receiver":"Window"},{"name":"null","signatures":[["index"]],"receiver":"HTMLFormControlsCollection"},{"name":"null","signatures":[["index"],["name"]],"receiver":"HTMLFormElement"},{"name":"null","signatures":[["index","?option"],["name"]],"receiver":"HTMLOptionsCollection"},{"name":"null","signatures":[["index","option"]],"receiver":"HTMLSelectElement"},{"name":"null","signatures":[["index"]],"receiver":"RadioNodeList"},{"name":"null","signatures":[["index"]],"receiver":"HTMLAllCollection"},{"name":"null","signatures":[["name","?value"]],"receiver":"HTMLEmbedElement"},{"name":"null","signatures":[["name"]],"receiver":"HTMLFrameSetElement"},{"name":"null","signatures":[["name","?value"]],"receiver":"HTMLObjectElement"},{"name":"null","signatures":[["index"]],"receiver":"AudioTrackList"},{"name":"null","signatures":[["index"]],"receiver":"TextTrackCueList"},{"name":"null","signatures":[["index"]],"receiver":"TextTrackList"},{"name":"null","signatures":[["index"]],"receiver":"VideoTrackList"},{"name":"null","signatures":[["index","newItem"]],"receiver":"SVGLengthList"},{"name":"null","signatures":[["index","newItem"]],"receiver":"SVGNumberList"},{"name":"null","signatures":[["index","newItem"]],"receiver":"SVGPointList"},{"name":"null","signatures":[["index","newItem"]],"receiver":"SVGStringList"},{"name":"null","signatures":[["index","newItem"]],"receiver":"SVGTransformList"},{"name":"null","signatures":[["index"]],"receiver":"SourceBufferList"},{"name":"null","signatures":[["index"]],"receiver":"TrackDefaultList"},{"name":"null","signatures":[["?name"]],"receiver":"RTCStatsResponse"},{"name":"remove","signatures":[["index"]],"receiver":"DataTransferItemList"},{"name":"remove","signatures":[["index"]],"receiver":"AccessibleNodeList"},{"name":"remove","signatures":[["...tokens"]],"receiver":"DOMTokenList"},{"name":"remove","signatures":[["index"]],"receiver":"HTMLOptionsCollection"},{"name":"remove","signatures":[["?index"]],"receiver":"HTMLSelectElement"},{"name":"remove","signatures":[["successCallback","?errorCallback"]],"receiver":"Entry"},{"name":"remove","signatures":[["start","end"]],"receiver":"SourceBuffer"},{"name":"getAsString","signatures":[["callback"]]},{"name":"setDragImage","signatures":[["image","x","y"]]},{"name":"getData","signatures":[["format"]]},{"name":"setData","signatures":[["format","data"]]},{"name":"clearData","signatures":[["?format"]]},{"name":"insertRule","signatures":[["rule","index"]],"receiver":"CSSGroupingRule"},{"name":"insertRule","signatures":[["rule","?index"]],"receiver":"CSSStyleSheet"},{"name":"deleteRule","signatures":[["index"]],"receiver":"CSSGroupingRule"},{"name":"deleteRule","signatures":[["select"]],"receiver":"CSSKeyframesRule"},{"name":"deleteRule","signatures":[["index"]],"receiver":"CSSStyleSheet"},{"name":"appendRule","signatures":[["rule"]]},{"name":"findRule","signatures":[["select"]]},{"name":"item","signatures":[["index"]],"receiver":"CSSRuleList"},{"name":"item","signatures":[["index"]],"receiver":"CSSStyleDeclaration"},{"name":"item","signatures":[["index"]],"receiver":"MediaList"},{"name":"item","signatures":[["index"]],"receiver":"StyleSheetList"},{"name":"item","signatures":[["index"]],"receiver":"AccessibleNodeList"},{"name":"item","signatures":[["index"]],"receiver":"DOMStringList"},{"name":"item","signatures":[["index"]],"receiver":"DOMTokenList"},{"name":"item","signatures":[["index"]],"receiver":"NamedNodeMap"},{"name":"item","signatures":[["index"]],"receiver":"NodeList"},{"name":"item","signatures":[["index"]],"receiver":"FileList"},{"name":"item","signatures":[["index"]],"receiver":"DOMRectList"},{"name":"item","signatures":[["index"]],"receiver":"HTMLSelectElement"},{"name":"item","signatures":[["?nameOrIndex"]],"receiver":"HTMLAllCollection"},{"name":"item","signatures":[["index"]],"receiver":"HTMLCollection"},{"name":"item","signatures":[["index"]],"receiver":"TouchList"},{"name":"item","signatures":[["?index"]],"receiver":"GamepadList"},{"name":"item","signatures":[["index"]],"receiver":"MimeTypeArray"},{"name":"item","signatures":[["index"]],"receiver":"PluginArray"},{"name":"item","signatures":[["index"]],"receiver":"Plugin"},{"name":"item","signatures":[["index"]],"receiver":"SpeechGrammarList"},{"name":"item","signatures":[["index"]],"receiver":"SpeechRecognitionResultList"},{"name":"item","signatures":[["index"]],"receiver":"SpeechRecognitionResult"},{"name":"item","signatures":[["index"]],"receiver":"SQLResultSetRowList"},{"name":"getPropertyValue","signatures":[["property"]]},{"name":"getPropertyPriority","signatures":[["property"]]},{"name":"setProperty","signatures":[["property","value","?priority"]]},{"name":"removeProperty","signatures":[["property"]]},{"name":"CSSStyleSheet","signatures":[["text","?options"]]},{"name":"addRule","signatures":[["?selector","?style","?index"]]},{"name":"removeRule","signatures":[["?index"]]},{"name":"supports","signatures":[["conditionText"],["property","value"]],"static":true},{"name":"supports","signatures":[["token"]],"receiver":"DOMTokenList"},{"name":"CSSKeywordValue","signatures":[["keyword"]]},{"name":"CSSMathInvert","signatures":[["arg"]]},{"name":"CSSMathMax","signatures":[["...args"]]},{"name":"CSSMathMin","signatures":[["...args"]]},{"name":"CSSMathNegate","signatures":[["arg"]]},{"name":"CSSMathProduct","signatures":[["...args"]]},{"name":"CSSMathSum","signatures":[["...args"]]},{"name":"CSSMatrixComponent","signatures":[["matrix","?options"]]},{"name":"mul","signatures":[["...values"]]},{"name":"div","signatures":[["...values"]]},{"name":"equals","signatures":[["...values"]]},{"name":"to","signatures":[["unit"]]},{"name":"toSum","signatures":[["...units"]]},{"name":"CSSPerspective","signatures":[["length"]]},{"name":"CSSPositionValue","signatures":[["x","y"]]},{"name":"CSSRotate","signatures":[["angleValue"]]},{"name":"CSSScale","signatures":[["x","y","?z"]]},{"name":"CSSSkewX","signatures":[["ax"]]},{"name":"CSSSkewY","signatures":[["ay"]]},{"name":"CSSSkew","signatures":[["ax","ay"]]},{"name":"CSSTransformValue","signatures":[["transforms"]]},{"name":"CSSTranslate","signatures":[["x","y","?z"]]},{"name":"CSSUnitValue","signatures":[["value","unit"]]},{"name":"number","signatures":[["value"]]},{"name":"percent","signatures":[["value"]]},{"name":"em","signatures":[["value"]]},{"name":"ex","signatures":[["value"]]},{"name":"ch","signatures":[["value"]]},{"name":"rem","signatures":[["value"]]},{"name":"vw","signatures":[["value"]]},{"name":"vh","signatures":[["value"]]},{"name":"vmin","signatures":[["value"]]},{"name":"vmax","signatures":[["value"]]},{"name":"cm","signatures":[["value"]]},{"name":"mm","signatures":[["value"]]},{"name":"in","signatures":[["value"]]},{"name":"pt","signatures":[["value"]]},{"name":"pc","signatures":[["value"]]},{"name":"px","signatures":[["value"]]},{"name":"Q","signatures":[["value"]]},{"name":"deg","signatures":[["value"]]},{"name":"grad","signatures":[["value"]]},{"name":"rad","signatures":[["value"]]},{"name":"turn","signatures":[["value"]]},{"name":"s","signatures":[["value"]]},{"name":"ms","signatures":[["value"]]},{"name":"Hz","signatures":[["value"]]},{"name":"kHz","signatures":[["value"]]},{"name":"dpi","signatures":[["value"]]},{"name":"dpcm","signatures":[["value"]]},{"name":"dppx","signatures":[["value"]]},{"name":"fr","signatures":[["value"]]},{"name":"CSSUnparsedValue","signatures":[["members"]]},{"name":"CSSVariableReferenceValue","signatures":[["variable","?fallback"]]},{"name":"getAll","signatures":[["property"]],"receiver":"StylePropertyMapReadOnly"},{"name":"getAll","signatures":[["name"]],"receiver":"FormData"},{"name":"getAll","signatures":[["name"]],"receiver":"URLSearchParams"},{"name":"getAll","signatures":[["?options"],["name","?options"]],"receiver":"CookieStore"},{"name":"getAll","signatures":[["?query","?count"]],"receiver":"IDBIndex"},{"name":"getAll","signatures":[["?query","?count"]],"receiver":"IDBObjectStore"},{"name":"append","signatures":[["property","...values"]],"receiver":"StylePropertyMap"},{"name":"append","signatures":[["...nodes"]],"receiver":"ParentNode"},{"name":"append","signatures":[["name","value"]],"receiver":"Headers"},{"name":"append","signatures":[["name","value","?filename"]],"receiver":"FormData"},{"name":"append","signatures":[["name","value"]],"receiver":"URLSearchParams"},{"name":"FontFaceSetLoadEvent","signatures":[["type","?eventInitDict"]]},{"name":"check","signatures":[["font","?text"]]},{"name":"FontFace","signatures":[["family","source","?descriptors"]]},{"name":"appendMedium","signatures":[["medium"]]},{"name":"deleteMedium","signatures":[["medium"]]},{"name":"MediaQueryListEvent","signatures":[["type","?eventInitDict"]]},{"name":"addListener","signatures":[["listener"]]},{"name":"removeListener","signatures":[["listener"]]},{"name":"registerProperty","signatures":[["descriptor"]]},{"name":"matchMedium","signatures":[["?mediaquery"]]},{"name":"StyleSheetList","signatures":[["sheets"]]},{"name":"appendChild","signatures":[["child"]],"receiver":"AccessibleNode"},{"name":"appendChild","signatures":[["node"]],"receiver":"Node"},{"name":"removeChild","signatures":[["child"]]},{"name":"substringData","signatures":[["offset","count"]]},{"name":"appendData","signatures":[["data"]]},{"name":"insertData","signatures":[["offset","data"]]},{"name":"deleteData","signatures":[["offset","count"]]},{"name":"replaceData","signatures":[["offset","count","data"]]},{"name":"before","signatures":[["...nodes"]]},{"name":"after","signatures":[["...nodes"]]},{"name":"replaceWith","signatures":[["...nodes"]]},{"name":"elementFromPoint","signatures":[["x","y"]]},{"name":"elementsFromPoint","signatures":[["x","y"]]},{"name":"getElementsByTagName","signatures":[["localName"]]},{"name":"getElementsByTagNameNS","signatures":[["namespaceURI","localName"]]},{"name":"getElementsByClassName","signatures":[["classNames"]]},{"name":"createElement","signatures":[["localName","?options"]]},{"name":"createElementNS","signatures":[["namespaceURI","qualifiedName","?options"]]},{"name":"createTextNode","signatures":[["data"]]},{"name":"createCDATASection","signatures":[["data"]]},{"name":"createComment","signatures":[["data"]]},{"name":"createProcessingInstruction","signatures":[["target","data"]]},{"name":"importNode","signatures":[["node","?deep"]]},{"name":"adoptNode","signatures":[["node"]]},{"name":"createAttribute","signatures":[["localName"]]},{"name":"createAttributeNS","signatures":[["namespaceURI","qualifiedName"]]},{"name":"createEvent","signatures":[["eventType"]]},{"name":"createNodeIterator","signatures":[["root","?whatToShow","?filter"]]},{"name":"createTreeWalker","signatures":[["root","?whatToShow","?filter"]]},{"name":"getElementsByName","signatures":[["elementName"]]},{"name":"open","signatures":[["?type","?replace"],["url","name","features"]],"receiver":"Document"},{"name":"open","signatures":[["?url","?target","?features"]],"receiver":"Window"},{"name":"open","signatures":[["method","url","?async","?username","?password"]],"receiver":"XMLHttpRequest"},{"name":"open","signatures":[["cacheName"]],"receiver":"CacheStorage"},{"name":"open","signatures":[["name","?version"]],"receiver":"IDBFactory"},{"name":"close","signatures":[["?returnValue"]],"receiver":"HTMLDialogElement"},{"name":"close","signatures":[["?code","?reason"]],"receiver":"WebSocket"},{"name":"write","signatures":[["...text"],["text"]],"receiver":"Document"},{"name":"write","signatures":[["data"]],"receiver":"Clipboard"},{"name":"write","signatures":[["data"]],"receiver":"FileWriterSync"},{"name":"write","signatures":[["data"]],"receiver":"FileWriter"},{"name":"writeln","signatures":[["...text"],["text"]]},{"name":"execCommand","signatures":[["commandId","?showUI","?value"]]},{"name":"queryCommandEnabled","signatures":[["commandId"]]},{"name":"queryCommandIndeterm","signatures":[["commandId"]]},{"name":"queryCommandState","signatures":[["commandId"]]},{"name":"queryCommandSupported","signatures":[["commandId"]]},{"name":"queryCommandValue","signatures":[["commandId"]]},{"name":"createTouchList","signatures":[["...touches"]]},{"name":"registerElement","signatures":[["type","?options"]]},{"name":"caretRangeFromPoint","signatures":[["?x","?y"]]},{"name":"DOMException","signatures":[["?message","?name"]]},{"name":"createDocumentType","signatures":[["qualifiedName","publicId","systemId"]]},{"name":"createDocument","signatures":[["namespaceURI","qualifiedName","?doctype"]]},{"name":"createHTMLDocument","signatures":[["?title"]]},{"name":"contains","signatures":[["string"]],"receiver":"DOMStringList"},{"name":"contains","signatures":[["token"]],"receiver":"DOMTokenList"},{"name":"contains","signatures":[["other"]],"receiver":"Node"},{"name":"toggle","signatures":[["token","?force"]]},{"name":"setPointerCapture","signatures":[["pointerId"]]},{"name":"releasePointerCapture","signatures":[["pointerId"]]},{"name":"hasPointerCapture","signatures":[["pointerId"]]},{"name":"getAttribute","signatures":[["name"]]},{"name":"getAttributeNS","signatures":[["namespaceURI","localName"]]},{"name":"setAttribute","signatures":[["name","value"]]},{"name":"setAttributeNS","signatures":[["namespaceURI","name","value"]]},{"name":"removeAttribute","signatures":[["name"]]},{"name":"removeAttributeNS","signatures":[["namespaceURI","localName"]]},{"name":"hasAttribute","signatures":[["name"]]},{"name":"hasAttributeNS","signatures":[["namespaceURI","localName"]]},{"name":"getAttributeNode","signatures":[["name"]]},{"name":"getAttributeNodeNS","signatures":[["namespaceURI","localName"]]},{"name":"setAttributeNode","signatures":[["attr"]]},{"name":"setAttributeNodeNS","signatures":[["attr"]]},{"name":"removeAttributeNode","signatures":[["attr"]]},{"name":"closest","signatures":[["selectors"]]},{"name":"matches","signatures":[["selectors"]]},{"name":"webkitMatchesSelector","signatures":[["selectors"]]},{"name":"attachShadow","signatures":[["shadowRootInitDict"]]},{"name":"insertAdjacentElement","signatures":[["where","element"]]},{"name":"insertAdjacentText","signatures":[["where","data"]]},{"name":"insertAdjacentHTML","signatures":[["position","text"]]},{"name":"scrollIntoView","signatures":[["?arg"]]},{"name":"scroll","signatures":[["?options"],["x","y"]]},{"name":"scrollTo","signatures":[["?options"],["x","y"]]},{"name":"scrollBy","signatures":[["?options"],["x","y"]]},{"name":"setApplyScroll","signatures":[["scrollStateCallback","nativeScrollBehavior"]]},{"name":"setDistributeScroll","signatures":[["scrollStateCallback","nativeScrollBehavior"]]},{"name":"scrollIntoViewIfNeeded","signatures":[["?centerIfNeeded"]]},{"name":"CustomEvent","signatures":[["type","?eventInitDict"]]},{"name":"initCustomEvent","signatures":[["type","?bubbles","?cancelable","?detail"]]},{"name":"handleEvent","signatures":[["event"]],"receiver":"EventListener"},{"name":"handleEvent","signatures":[["entries"]],"receiver":"EntriesCallback"},{"name":"handleEvent","signatures":[["entry"]],"receiver":"EntryCallback"},{"name":"handleEvent","signatures":[["error"]],"receiver":"ErrorCallback"},{"name":"handleEvent","signatures":[["file"]],"receiver":"FileCallback"},{"name":"handleEvent","signatures":[["fileSystem"]],"receiver":"FileSystemCallback"},{"name":"handleEvent","signatures":[["fileWriter"]],"receiver":"FileWriterCallback"},{"name":"handleEvent","signatures":[["metadata"]],"receiver":"MetadataCallback"},{"name":"handleEvent","signatures":[["transaction","resultSet"]],"receiver":"SQLStatementCallback"},{"name":"handleEvent","signatures":[["transaction","error"]],"receiver":"SQLStatementErrorCallback"},{"name":"handleEvent","signatures":[["transaction"]],"receiver":"SQLTransactionCallback"},{"name":"handleEvent","signatures":[["error"]],"receiver":"SQLTransactionErrorCallback"},{"name":"addEventListener","signatures":[["type","listener","?options"]]},{"name":"removeEventListener","signatures":[["type","listener","?options"]]},{"name":"dispatchEvent","signatures":[["event"]]},{"name":"Event","signatures":[["type","?eventInitDict"]]},{"name":"initEvent","signatures":[["type","?bubbles","?cancelable"]]},{"name":"MutationObserver","signatures":[["callback"]]},{"name":"observe","signatures":[["target","?options"]],"receiver":"MutationObserver"},{"name":"observe","signatures":[["target"]],"receiver":"IntersectionObserver"},{"name":"observe","signatures":[["target"]],"receiver":"ResizeObserver"},{"name":"observe","signatures":[["options"]],"receiver":"PerformanceObserver"},{"name":"observe","signatures":[["db","tx","options"]],"receiver":"IDBObserver"},{"name":"disconnect","signatures":[["?output"],["destination","?output","?input"]],"receiver":"AudioNode"},{"name":"getNamedItem","signatures":[["name"]]},{"name":"getNamedItemNS","signatures":[["namespaceURI","localName"]]},{"name":"setNamedItem","signatures":[["attr"]]},{"name":"setNamedItemNS","signatures":[["attr"]]},{"name":"removeNamedItem","signatures":[["name"]]},{"name":"removeNamedItemNS","signatures":[["namespaceURI","localName"]]},{"name":"acceptNode","signatures":[["node"]]},{"name":"getRootNode","signatures":[["?options"]]},{"name":"cloneNode","signatures":[["?deep"]]},{"name":"isEqualNode","signatures":[["otherNode"]]},{"name":"isSameNode","signatures":[["otherNode"]]},{"name":"compareDocumentPosition","signatures":[["other"]]},{"name":"lookupPrefix","signatures":[["namespaceURI"]]},{"name":"lookupNamespaceURI","signatures":[["prefix"]],"receiver":"Node"},{"name":"lookupNamespaceURI","signatures":[["?prefix"]],"receiver":"XPathNSResolver"},{"name":"isDefaultNamespace","signatures":[["namespaceURI"]]},{"name":"insertBefore","signatures":[["node","child"]]},{"name":"replaceChild","signatures":[["node","child"]]},{"name":"getElementById","signatures":[["elementId"]]},{"name":"prepend","signatures":[["...nodes"]]},{"name":"querySelector","signatures":[["selectors"]]},{"name":"querySelectorAll","signatures":[["selectors"]]},{"name":"setStart","signatures":[["node","offset"]]},{"name":"setEnd","signatures":[["node","offset"]]},{"name":"setStartBefore","signatures":[["node"]]},{"name":"setStartAfter","signatures":[["node"]]},{"name":"setEndBefore","signatures":[["node"]]},{"name":"setEndAfter","signatures":[["node"]]},{"name":"collapse","signatures":[["?toStart"]],"receiver":"Range"},{"name":"collapse","signatures":[["node","?offset"]],"receiver":"Selection"},{"name":"selectNode","signatures":[["node"]]},{"name":"selectNodeContents","signatures":[["node"]]},{"name":"compareBoundaryPoints","signatures":[["how","sourceRange"]]},{"name":"insertNode","signatures":[["node"]]},{"name":"surroundContents","signatures":[["newParent"]]},{"name":"isPointInRange","signatures":[["node","offset"]]},{"name":"comparePoint","signatures":[["node","offset"]]},{"name":"intersectsNode","signatures":[["node"]]},{"name":"splitText","signatures":[["offset"]]},{"name":"unsafelyCreate","signatures":[["html"]],"static":true},{"name":"unsafelyCreate","signatures":[["url"]],"static":true},{"name":"unsafelyCreate","signatures":[["url"]],"static":true},{"name":"getRangeAt","signatures":[["index"]]},{"name":"addRange","signatures":[["range"]]},{"name":"removeRange","signatures":[["range"]]},{"name":"setPosition","signatures":[["node","?offset"]],"receiver":"Selection"},{"name":"setPosition","signatures":[["x","y","z"]],"receiver":"AudioListener"},{"name":"setPosition","signatures":[["x","y","z"]],"receiver":"PannerNode"},{"name":"extend","signatures":[["node","?offset"]]},{"name":"setBaseAndExtent","signatures":[["baseNode","baseOffset","extentNode","extentOffset"]]},{"name":"selectAllChildren","signatures":[["node"]]},{"name":"containsNode","signatures":[["node","?allowPartialContainment"]]},{"name":"ClipboardEvent","signatures":[["type","?eventInitDict"]]},{"name":"CompositionEvent","signatures":[["type","?eventInitDict"]]},{"name":"initCompositionEvent","signatures":[["?type","?bubbles","?cancelable","?view","?data"]]},{"name":"DragEvent","signatures":[["type","?eventInitDict"]]},{"name":"FocusEvent","signatures":[["type","?eventInitDict"]]},{"name":"HashChangeEvent","signatures":[["type","?eventInitDict"]]},{"name":"InputEvent","signatures":[["type","?eventInitDict"]]},{"name":"KeyboardEvent","signatures":[["type","?eventInitDict"]]},{"name":"getModifierState","signatures":[["keyArg"]]},{"name":"initKeyboardEvent","signatures":[["type","?bubbles","?cancelable","?view","?keyIdentifier","?location","?ctrlKey","?altKey","?shiftKey","?metaKey"]]},{"name":"MessageEvent","signatures":[["type","?eventInitDict"]]},{"name":"initMessageEvent","signatures":[["typeArg","?canBubbleArg","?cancelableArg","?dataArg","?originArg","?lastEventIdArg","?sourceArg","?portsArg"]]},{"name":"MouseEvent","signatures":[["type","?eventInitDict"]]},{"name":"initMouseEvent","signatures":[["?type","?bubbles","?cancelable","?view","?detail","?screenX","?screenY","?clientX","?clientY","?ctrlKey","?altKey","?shiftKey","?metaKey","?button","?relatedTarget"]]},{"name":"initMutationEvent","signatures":[["?type","?bubbles","?cancelable","?relatedNode","?prevValue","?newValue","?attrName","?attrChange"]]},{"name":"PageTransitionEvent","signatures":[["type","?eventInitDict"]]},{"name":"PopStateEvent","signatures":[["type","?eventInitDict"]]},{"name":"ProgressEvent","signatures":[["type","?eventInitDict"]]},{"name":"PromiseRejectionEvent","signatures":[["type","eventInitDict"]]},{"name":"initTextEvent","signatures":[["?type","?bubbles","?cancelable","?view","?data"]]},{"name":"TouchEvent","signatures":[["type","?eventInitDict"]]},{"name":"UIEvent","signatures":[["type","?eventInitDict"]]},{"name":"initUIEvent","signatures":[["?type","?bubbles","?cancelable","?view","?detail"]]},{"name":"WheelEvent","signatures":[["type","?eventInitDict"]]},{"name":"Headers","signatures":[["?init"]]},{"name":"Request","signatures":[["input","?requestInitDict"]]},{"name":"Response","signatures":[["?body","?init"]]},{"name":"error","signatures":[["...data"]],"receiver":"console"},{"name":"redirect","signatures":[["url","?status"]]},{"name":"fetch","signatures":[["input","?init"]],"receiver":"Window"},{"name":"fetch","signatures":[["input","?init"]],"receiver":"WorkerGlobalScope"},{"name":"fetch","signatures":[["id","requests","?options"]],"receiver":"BackgroundFetchManager"},{"name":"fetch","signatures":[["input","?init"]],"receiver":"ServiceWorkerGlobalScope"},{"name":"Blob","signatures":[["?blobParts","?options"]]},{"name":"readAsArrayBuffer","signatures":[["blob"]]},{"name":"readAsBinaryString","signatures":[["blob"]]},{"name":"readAsText","signatures":[["blob","?label"]]},{"name":"readAsDataURL","signatures":[["blob"]]},{"name":"File","signatures":[["fileBits","fileName","?options"]]},{"name":"createObjectURL","signatures":[["blob"],["source"],["stream"]]},{"name":"revokeObjectURL","signatures":[["url"]]},{"name":"go","signatures":[["?delta"]]},{"name":"pushState","signatures":[["data","title","?url"]]},{"name":"replaceState","signatures":[["data","title","?url"]]},{"name":"btoa","signatures":[["btoa"]]},{"name":"atob","signatures":[["atob"]]},{"name":"setTimeout","signatures":[["handler","?timeout","...arguments"]]},{"name":"clearTimeout","signatures":[["?handle"]]},{"name":"setInterval","signatures":[["handler","?timeout","...arguments"]]},{"name":"clearInterval","signatures":[["?handle"]]},{"name":"stop","signatures":[["?when"]],"receiver":"AudioScheduledSourceNode"},{"name":"focus","signatures":[["?options"]],"receiver":"HTMLElement"},{"name":"alert","signatures":[["?message"]]},{"name":"confirm","signatures":[["?message"]]},{"name":"prompt","signatures":[["?message","?defaultValue"]],"receiver":"Window"},{"name":"requestAnimationFrame","signatures":[["callback"]]},{"name":"cancelAnimationFrame","signatures":[["handle"]]},{"name":"requestIdleCallback","signatures":[["callback","?options"]]},{"name":"cancelIdleCallback","signatures":[["handle"]]},{"name":"postMessage","signatures":[["message","targetOrigin","?transfer"]],"receiver":"Window"},{"name":"postMessage","signatures":[["message","?transfer"]],"receiver":"MessagePort"},{"name":"postMessage","signatures":[["message","?transfer"]],"receiver":"DedicatedWorkerGlobalScope"},{"name":"postMessage","signatures":[["message","?transfer"]],"receiver":"Worker"},{"name":"postMessage","signatures":[["message"]],"receiver":"BroadcastChannel"},{"name":"postMessage","signatures":[["message","?transfer"]],"receiver":"Client"},{"name":"postMessage","signatures":[["message","?transfer"]],"receiver":"ServiceWorker"},{"name":"getComputedStyle","signatures":[["elt","?pseudoElt"]]},{"name":"matchMedia","signatures":[["query"]]},{"name":"moveTo","signatures":[["x","y"]],"receiver":"Window"},{"name":"moveTo","signatures":[["x","y"]],"receiver":"CanvasPath"},{"name":"moveTo","signatures":[["parent","name"]],"receiver":"EntrySync"},{"name":"moveTo","signatures":[["parent","?name","?successCallback","?errorCallback"]],"receiver":"Entry"},{"name":"moveBy","signatures":[["x","y"]]},{"name":"resizeTo","signatures":[["x","y"]]},{"name":"resizeBy","signatures":[["x","y"]]},{"name":"getComputedAccessibleNode","signatures":[["element"]]},{"name":"webkitRequestAnimationFrame","signatures":[["callback"]]},{"name":"webkitCancelAnimationFrame","signatures":[["id"]]},{"name":"requestFullscreen","signatures":[["?options"]]},{"name":"webkitRequestFullScreen","signatures":[["?options"]]},{"name":"webkitRequestFullscreen","signatures":[["?options"]]},{"name":"DOMMatrixReadOnly","signatures":[["?init"]]},{"name":"fromMatrix","signatures":[["?other"]]},{"name":"fromFloat32Array","signatures":[["array32"]]},{"name":"fromFloat64Array","signatures":[["array64"]]},{"name":"translate","signatures":[["?tx","?ty","?tz"]],"receiver":"DOMMatrixReadOnly"},{"name":"translate","signatures":[["x","y"]],"receiver":"SVGMatrix"},{"name":"translate","signatures":[["x","y"]],"receiver":"CanvasRenderingContext2D"},{"name":"translate","signatures":[["x","y"]],"receiver":"OffscreenCanvasRenderingContext2D"},{"name":"translate","signatures":[["x","y"]],"receiver":"PaintRenderingContext2D"},{"name":"scale","signatures":[["?scaleX","?scaleY","?scaleZ","?originX","?originY","?originZ"]],"receiver":"DOMMatrixReadOnly"},{"name":"scale","signatures":[["scaleFactor"]],"receiver":"SVGMatrix"},{"name":"scale","signatures":[["x","y"]],"receiver":"CanvasRenderingContext2D"},{"name":"scale","signatures":[["x","y"]],"receiver":"OffscreenCanvasRenderingContext2D"},{"name":"scale","signatures":[["x","y"]],"receiver":"PaintRenderingContext2D"},{"name":"scale3d","signatures":[["?scale","?originX","?originY","?originZ"]]},{"name":"rotate","signatures":[["?rotX","?rotY","?rotZ"]],"receiver":"DOMMatrixReadOnly"},{"name":"rotate","signatures":[["angle"]],"receiver":"SVGMatrix"},{"name":"rotate","signatures":[["angle"]],"receiver":"CanvasRenderingContext2D"},{"name":"rotate","signatures":[["angle"]],"receiver":"OffscreenCanvasRenderingContext2D"},{"name":"rotate","signatures":[["angle"]],"receiver":"PaintRenderingContext2D"},{"name":"rotateFromVector","signatures":[["?x","?y"]],"receiver":"DOMMatrixReadOnly"},{"name":"rotateFromVector","signatures":[["x","y"]],"receiver":"SVGMatrix"},{"name":"rotateAxisAngle","signatures":[["?x","?y","?z","?angle"]]},{"name":"skewX","signatures":[["?sx"]],"receiver":"DOMMatrixReadOnly"},{"name":"skewX","signatures":[["angle"]],"receiver":"SVGMatrix"},{"name":"skewY","signatures":[["?sy"]],"receiver":"DOMMatrixReadOnly"},{"name":"skewY","signatures":[["angle"]],"receiver":"SVGMatrix"},{"name":"multiply","signatures":[["?other"]],"receiver":"DOMMatrixReadOnly"},{"name":"multiply","signatures":[["secondMatrix"]],"receiver":"SVGMatrix"},{"name":"transformPoint","signatures":[["?point"]]},{"name":"DOMMatrix","signatures":[["?init"]]},{"name":"multiplySelf","signatures":[["?other"]]},{"name":"preMultiplySelf","signatures":[["?other"]]},{"name":"translateSelf","signatures":[["?tx","?ty","?tz"]]},{"name":"scaleSelf","signatures":[["?scaleX","?scaleY","?scaleZ","?originX","?originY","?originZ"]]},{"name":"scale3dSelf","signatures":[["?scale","?originX","?originY","?originZ"]]},{"name":"rotateSelf","signatures":[["?rotX","?rotY","?rotZ"]]},{"name":"rotateFromVectorSelf","signatures":[["?x","?y"]]},{"name":"rotateAxisAngleSelf","signatures":[["?x","?y","?z","?angle"]]},{"name":"skewXSelf","signatures":[["?sx"]]},{"name":"skewYSelf","signatures":[["?sy"]]},{"name":"setMatrixValue","signatures":[["transformList"]]},{"name":"DOMPointReadOnly","signatures":[["?x","?y","?z","?w"]]},{"name":"fromPoint","signatures":[["?other"]]},{"name":"matrixTransform","signatures":[["?matrix"]],"receiver":"DOMPointReadOnly"},{"name":"matrixTransform","signatures":[["matrix"]],"receiver":"SVGPoint"},{"name":"DOMPoint","signatures":[["?x","?y","?z","?w"]]},{"name":"DOMQuad","signatures":[["?p1","?p2","?p3","?p4"]]},{"name":"fromRect","signatures":[["?other"]]},{"name":"fromQuad","signatures":[["?other"]]},{"name":"DOMRectReadOnly","signatures":[["?x","?y","?width","?height"]]},{"name":"DOMRect","signatures":[["?x","?y","?width","?height"]]},{"name":"toDataURL","signatures":[["?type","?arguments"]]},{"name":"toBlob","signatures":[["callback","?type","?arguments"]]},{"name":"ImageData","signatures":[["sw","sh"]]},{"name":"define","signatures":[["name","constructor","?options"]]},{"name":"whenDefined","signatures":[["name"]]},{"name":"upgrade","signatures":[["root"]]},{"name":"FormData","signatures":[["?form"]]},{"name":"setCustomValidity","signatures":[["error"]]},{"name":"namedItem","signatures":[["name"]],"receiver":"HTMLFormControlsCollection"},{"name":"namedItem","signatures":[["name"]],"receiver":"HTMLSelectElement"},{"name":"namedItem","signatures":[["name"]],"receiver":"HTMLAllCollection"},{"name":"namedItem","signatures":[["name"]],"receiver":"HTMLCollection"},{"name":"namedItem","signatures":[["?name"]],"receiver":"RTCStatsResponse"},{"name":"namedItem","signatures":[["name"]],"receiver":"MimeTypeArray"},{"name":"namedItem","signatures":[["name"]],"receiver":"PluginArray"},{"name":"namedItem","signatures":[["name"]],"receiver":"Plugin"},{"name":"stepUp","signatures":[["?n"]]},{"name":"stepDown","signatures":[["?n"]]},{"name":"setRangeText","signatures":[["replacement","?start","?end","?selectionMode"]]},{"name":"setSelectionRange","signatures":[["start","end","?direction"]]},{"name":"Option","signatures":[["?data","?value","?defaultSelected","?selected"]]},{"name":"Image","signatures":[["?width","?height"]]},{"name":"decode","signatures":[["?input","?options"]],"receiver":"TextDecoder"},{"name":"start","signatures":[["index"]],"receiver":"TimeRanges"},{"name":"start","signatures":[["stream"]],"receiver":"UnderlyingSourceBase"},{"name":"start","signatures":[["?timeslice"]],"receiver":"MediaRecorder"},{"name":"start","signatures":[["?when","?grainOffset","?grainDuration"]],"receiver":"AudioBufferSourceNode"},{"name":"start","signatures":[["?when"]],"receiver":"AudioScheduledSourceNode"},{"name":"assignedNodes","signatures":[["?options"]]},{"name":"assignedElements","signatures":[["?options"]]},{"name":"insertRow","signatures":[["?index"]]},{"name":"deleteRow","signatures":[["index"]]},{"name":"insertCell","signatures":[["?index"]]},{"name":"deleteCell","signatures":[["index"]]},{"name":"Audio","signatures":[["?src"]]},{"name":"canPlayType","signatures":[["type"]]},{"name":"addTextTrack","signatures":[["kind","?label","?language"]]},{"name":"end","signatures":[["index"]],"receiver":"TimeRanges"},{"name":"getTrackById","signatures":[["id"]],"receiver":"AudioTrackList"},{"name":"getTrackById","signatures":[["id"]],"receiver":"TextTrackList"},{"name":"getTrackById","signatures":[["id"]],"receiver":"VideoTrackList"},{"name":"getTrackById","signatures":[["trackId"]],"receiver":"MediaStream"},{"name":"getCueById","signatures":[["id"]]},{"name":"addCue","signatures":[["cue"]]},{"name":"removeCue","signatures":[["cue"]]},{"name":"createImageBitmap","signatures":[["imageBitmap","?options"],["imageBitmap","sx","sy","sw","sh","?options"]]},{"name":"Touch","signatures":[["initDict"]]},{"name":"copyText","signatures":[["text"]]},{"name":"showContextMenuAtPoint","signatures":[["x","y","items","?document"]]},{"name":"sendMessageToEmbedder","signatures":[["message"]]},{"name":"unobserve","signatures":[["target"]],"receiver":"IntersectionObserver"},{"name":"unobserve","signatures":[["target"]],"receiver":"ResizeObserver"},{"name":"unobserve","signatures":[["db"]],"receiver":"IDBObserver"},{"name":"layoutNextFragment","signatures":[["options"]]},{"name":"registerLayout","signatures":[["name","layoutCtor"]]},{"name":"update","signatures":[["response"]],"receiver":"MediaKeySession"},{"name":"update","signatures":[["value"]],"receiver":"IDBCursor"},{"name":"watch","signatures":[["signals","callback"]],"receiver":"MojoHandle"},{"name":"watch","signatures":[["callback","?options"]],"receiver":"NFC"},{"name":"writeMessage","signatures":[["buffer","handles"]]},{"name":"readMessage","signatures":[["?flags"]]},{"name":"writeData","signatures":[["buffer","?options"]]},{"name":"discardData","signatures":[["numBytes","?options"]]},{"name":"readData","signatures":[["buffer","?options"]]},{"name":"mapBuffer","signatures":[["offset","numBytes"]]},{"name":"duplicateBufferHandle","signatures":[["?options"]]},{"name":"createDataPipe","signatures":[["options"]]},{"name":"createSharedBuffer","signatures":[["numBytes"]]},{"name":"bindInterface","signatures":[["interfaceName","request_handle","?scope"]]},{"name":"MojoInterfaceInterceptor","signatures":[["interfaceName","?scope"]]},{"name":"MojoInterfaceRequestEvent","signatures":[["type","?eventInitDict"]]},{"name":"OffscreenCanvas","signatures":[["width","height"]]},{"name":"convertToBlob","signatures":[["?options"]]},{"name":"setValueAndClosePopup","signatures":[["numberValue","stringValue"]]},{"name":"setValue","signatures":[["value"]]},{"name":"selectFontsFromOwnerDocument","signatures":[["targetDocument"]]},{"name":"localizeNumberString","signatures":[["numberString"]]},{"name":"formatMonth","signatures":[["year","zeroBaseMonth"]]},{"name":"formatShortMonth","signatures":[["year","zeroBaseMonth"]]},{"name":"formatWeek","signatures":[["year","weekNumber","localizedStartDate"]]},{"name":"setWindowRect","signatures":[["x","y","width","height"]]},{"name":"consumeDelta","signatures":[["x","y"]]},{"name":"allowsFeature","signatures":[["feature","?url"]]},{"name":"getAllowlistForFeature","signatures":[["feature"]]},{"name":"newValueSpecifiedUnits","signatures":[["unitType","valueInSpecifiedUnits"]]},{"name":"convertToSpecifiedUnits","signatures":[["unitType"]]},{"name":"beginElementAt","signatures":[["offset"]]},{"name":"endElementAt","signatures":[["offset"]]},{"name":"setStdDeviation","signatures":[["stdDeviationX","stdDeviationY"]]},{"name":"isPointInFill","signatures":[["point"]]},{"name":"isPointInStroke","signatures":[["point"]],"receiver":"SVGGeometryElement"},{"name":"isPointInStroke","signatures":[["x","y"],["path","x","y"]],"receiver":"CanvasRenderingContext2D"},{"name":"isPointInStroke","signatures":[["x","y"],["path","x","y"]],"receiver":"OffscreenCanvasRenderingContext2D"},{"name":"isPointInStroke","signatures":[["x","y"],["path","x","y"]],"receiver":"PaintRenderingContext2D"},{"name":"getPointAtLength","signatures":[["distance"]]},{"name":"initialize","signatures":[["newItem"]]},{"name":"getItem","signatures":[["index"]],"receiver":"SVGLengthList"},{"name":"getItem","signatures":[["index"]],"receiver":"SVGNumberList"},{"name":"getItem","signatures":[["index"]],"receiver":"SVGPointList"},{"name":"getItem","signatures":[["index"]],"receiver":"SVGStringList"},{"name":"getItem","signatures":[["index"]],"receiver":"SVGTransformList"},{"name":"getItem","signatures":[["key"]],"receiver":"Storage"},{"name":"insertItemBefore","signatures":[["newItem","index"]],"receiver":"SVGLengthList"},{"name":"insertItemBefore","signatures":[["newItem","index"]],"receiver":"SVGNumberList"},{"name":"insertItemBefore","signatures":[["newItem","index"]],"receiver":"SVGPointList"},{"name":"insertItemBefore","signatures":[["item","index"]],"receiver":"SVGStringList"},{"name":"insertItemBefore","signatures":[["newItem","index"]],"receiver":"SVGTransformList"},{"name":"replaceItem","signatures":[["newItem","index"]]},{"name":"removeItem","signatures":[["index"]],"receiver":"SVGLengthList"},{"name":"removeItem","signatures":[["index"]],"receiver":"SVGNumberList"},{"name":"removeItem","signatures":[["index"]],"receiver":"SVGPointList"},{"name":"removeItem","signatures":[["index"]],"receiver":"SVGStringList"},{"name":"removeItem","signatures":[["index"]],"receiver":"SVGTransformList"},{"name":"removeItem","signatures":[["key"]],"receiver":"Storage"},{"name":"appendItem","signatures":[["newItem"]]},{"name":"setOrientToAngle","signatures":[["angle"]]},{"name":"scaleNonUniform","signatures":[["scaleFactorX","scaleFactorY"]]},{"name":"getIntersectionList","signatures":[["rect","referenceElement"]]},{"name":"getEnclosureList","signatures":[["rect","referenceElement"]]},{"name":"checkIntersection","signatures":[["element","rect"]]},{"name":"checkEnclosure","signatures":[["element","rect"]]},{"name":"createSVGTransformFromMatrix","signatures":[["matrix"]]},{"name":"suspendRedraw","signatures":[["maxWaitMilliseconds"]]},{"name":"unsuspendRedraw","signatures":[["suspendHandleId"]]},{"name":"setCurrentTime","signatures":[["seconds"]]},{"name":"getSubStringLength","signatures":[["charnum","nchars"]]},{"name":"getStartPositionOfChar","signatures":[["charnum"]]},{"name":"getEndPositionOfChar","signatures":[["charnum"]]},{"name":"getExtentOfChar","signatures":[["charnum"]]},{"name":"getRotationOfChar","signatures":[["charnum"]]},{"name":"getCharNumAtPosition","signatures":[["point"]]},{"name":"selectSubString","signatures":[["charnum","nchars"]]},{"name":"setMatrix","signatures":[["matrix"]]},{"name":"setTranslate","signatures":[["tx","ty"]]},{"name":"setScale","signatures":[["sx","sy"]]},{"name":"setRotate","signatures":[["angle","cx","cy"]]},{"name":"setSkewX","signatures":[["angle"]]},{"name":"setSkewY","signatures":[["angle"]]},{"name":"getEntriesByType","signatures":[["entryType"]]},{"name":"getEntriesByName","signatures":[["name","?entryType"]]},{"name":"PerformanceObserver","signatures":[["callback"]]},{"name":"URLSearchParams","signatures":[["?init"]]},{"name":"URL","signatures":[["url","?base"]]},{"name":"importScripts","signatures":[["...urls"]]},{"name":"addModule","signatures":[["moduleURL","?options"]]},{"name":"createExpression","signatures":[["expression","?resolver"]]},{"name":"createNSResolver","signatures":[["nodeResolver"]]},{"name":"evaluate","signatures":[["expression","contextNode","?resolver","?type","?inResult"]],"receiver":"Document"},{"name":"evaluate","signatures":[["expression","contextNode","?resolver","?type","?inResult"]],"receiver":"XPathEvaluator"},{"name":"evaluate","signatures":[["contextNode","?type","?inResult"]],"receiver":"XPathExpression"},{"name":"parseFromString","signatures":[["str","type"]]},{"name":"serializeToString","signatures":[["root"]]},{"name":"snapshotItem","signatures":[["index"]]},{"name":"importStylesheet","signatures":[["style"]]},{"name":"transformToFragment","signatures":[["source","output"]]},{"name":"transformToDocument","signatures":[["source"]]},{"name":"setParameter","signatures":[["namespaceURI","localName","value"]]},{"name":"getParameter","signatures":[["namespaceURI","localName"]],"receiver":"XSLTProcessor"},{"name":"getParameter","signatures":[["pname"]],"receiver":"WebGLRenderingContextBase"},{"name":"removeParameter","signatures":[["namespaceURI","localName"]]},{"name":"setRequestHeader","signatures":[["name","value"]]},{"name":"send","signatures":[["?body"]],"receiver":"XMLHttpRequest"},{"name":"send","signatures":[["data"]],"receiver":"RTCDataChannel"},{"name":"send","signatures":[["message"],["data"]],"receiver":"PresentationConnection"},{"name":"send","signatures":[["data","?timestamp"]],"receiver":"MIDIOutput"},{"name":"send","signatures":[["data"]],"receiver":"WebSocket"},{"name":"getResponseHeader","signatures":[["name"]]},{"name":"overrideMimeType","signatures":[["mime"]]},{"name":"registerAnimator","signatures":[["name","animatorConstructor"]]},{"name":"setSinkId","signatures":[["sinkId"]]},{"name":"BackgroundFetchClickEvent","signatures":[["type","init"]]},{"name":"BackgroundFetchEvent","signatures":[["type","init"]]},{"name":"BackgroundFetchFailEvent","signatures":[["type","init"]]},{"name":"BackgroundFetchSettledEvent","signatures":[["type","init"]]},{"name":"BackgroundFetchSettledFetch","signatures":[["request","response"]]},{"name":"BackgroundFetchUpdateEvent","signatures":[["type","init"]]},{"name":"updateUI","signatures":[["title"]]},{"name":"BackgroundFetchedEvent","signatures":[["type","init"]]},{"name":"SyncEvent","signatures":[["type","init"]]},{"name":"register","signatures":[["tag"]],"receiver":"SyncManager"},{"name":"register","signatures":[["url","?options"]],"receiver":"ServiceWorkerContainer"},{"name":"sendBeacon","signatures":[["url","?data"]]},{"name":"getDescriptor","signatures":[["descriptor"]],"receiver":"BluetoothRemoteGATTCharacteristic"},{"name":"getDescriptor","signatures":[["name"]],"static":true},{"name":"getDescriptors","signatures":[["?descriptor"]]},{"name":"writeValue","signatures":[["value"]]},{"name":"connect","signatures":[["destination","?output","?input"]],"receiver":"AudioNode"},{"name":"getPrimaryService","signatures":[["service"]]},{"name":"getPrimaryServices","signatures":[["?service"]]},{"name":"getCharacteristic","signatures":[["characteristic"]],"receiver":"BluetoothRemoteGATTService"},{"name":"getCharacteristic","signatures":[["name"]],"static":true},{"name":"getCharacteristics","signatures":[["?characteristic"]]},{"name":"getService","signatures":[["name"]]},{"name":"canonicalUUID","signatures":[["alias"]]},{"name":"requestDevice","signatures":[["?options"]],"receiver":"Bluetooth"},{"name":"requestDevice","signatures":[["options"]],"receiver":"USB"},{"name":"BroadcastChannel","signatures":[["name"]]},{"name":"getCost","signatures":[["operation"]]},{"name":"reserve","signatures":[["operation"]]},{"name":"matchAll","signatures":[["?request","?options"]],"receiver":"Cache"},{"name":"matchAll","signatures":[["?options"]],"receiver":"Clients"},{"name":"addAll","signatures":[["requests"]]},{"name":"put","signatures":[["request","response"]],"receiver":"Cache"},{"name":"put","signatures":[["value","?key"]],"receiver":"IDBObjectStore"},{"name":"addColorStop","signatures":[["offset","color"]]},{"name":"lineTo","signatures":[["x","y"]]},{"name":"quadraticCurveTo","signatures":[["cpx","cpy","x","y"]]},{"name":"bezierCurveTo","signatures":[["cp1x","cp1y","cp2x","cp2y","x","y"]]},{"name":"arcTo","signatures":[["x1","y1","x2","y2","radius"]]},{"name":"rect","signatures":[["x","y","width","height"]]},{"name":"arc","signatures":[["x","y","radius","startAngle","endAngle","?anticlockwise"]]},{"name":"ellipse","signatures":[["x","y","radiusX","radiusY","rotation","startAngle","endAngle","?anticlockwise"]]},{"name":"setTransform","signatures":[["transform"]],"receiver":"CanvasPattern"},{"name":"setTransform","signatures":[["a","b","c","d","e","f"]],"receiver":"CanvasRenderingContext2D"},{"name":"setTransform","signatures":[["a","b","c","d","e","f"]],"receiver":"OffscreenCanvasRenderingContext2D"},{"name":"setTransform","signatures":[["a","b","c","d","e","f"]],"receiver":"PaintRenderingContext2D"},{"name":"transform","signatures":[["a","b","c","d","e","f"]]},{"name":"createLinearGradient","signatures":[["x0","y0","x1","y1"]]},{"name":"createRadialGradient","signatures":[["x0","y0","r0","x1","y1","r1"]]},{"name":"createPattern","signatures":[["image","repetitionType"]]},{"name":"clearRect","signatures":[["x","y","width","height"]]},{"name":"fillRect","signatures":[["x","y","width","height"]]},{"name":"strokeRect","signatures":[["x","y","width","height"]]},{"name":"stroke","signatures":[["?path"]]},{"name":"drawFocusIfNeeded","signatures":[["element"],["path","element"]]},{"name":"scrollPathIntoView","signatures":[["?path"]]},{"name":"clip","signatures":[["?winding"],["path","?winding"]],"receiver":"CanvasRenderingContext2D"},{"name":"clip","signatures":[["?path"]],"receiver":"OffscreenCanvasRenderingContext2D"},{"name":"clip","signatures":[["?winding"],["path","?winding"]],"receiver":"PaintRenderingContext2D"},{"name":"isPointInPath","signatures":[["x","y","?winding"],["path","x","y","?winding"]]},{"name":"fillText","signatures":[["text","x","y","?maxWidth"]]},{"name":"strokeText","signatures":[["text","x","y","?maxWidth"]]},{"name":"measureText","signatures":[["text"]]},{"name":"drawImage","signatures":[["image","x","y","?width","?height"],["image","sx","sy","sw","sh","dx","dy","dw","dh"]]},{"name":"addHitRegion","signatures":[["?options"]]},{"name":"removeHitRegion","signatures":[["id"]]},{"name":"createImageData","signatures":[["imagedata"],["sw","sh","?imageDataColorSettings"],["data","sw","sh","?imageDataColorSettings"]],"receiver":"CanvasRenderingContext2D"},{"name":"createImageData","signatures":[["imagedata"],["sw","sh","?imageDataColorSettings"],["data","sw","sh","imageDataColorSettings"]],"receiver":"OffscreenCanvasRenderingContext2D"},{"name":"getImageData","signatures":[["sx","sy","sw","sh"]]},{"name":"putImageData","signatures":[["imagedata","dx","dy","?dirtyX","?dirtyY","?dirtyWidth","?dirtyHeight"]]},{"name":"setLineDash","signatures":[["dash"]]},{"name":"addPath","signatures":[["path","?transform"]]},{"name":"getContext","signatures":[["contextId","?attributes"]],"receiver":"HTMLCanvasElement"},{"name":"getContext","signatures":[["contextType","?attributes"]],"receiver":"OffscreenCanvas"},{"name":"transferFromImageBitmap","signatures":[["bitmap"]]},{"name":"writeText","signatures":[["data"]]},{"name":"CookieChangeEvent","signatures":[["type","?eventInitDict"]]},{"name":"subscribeToChanges","signatures":[["subscriptions"]]},{"name":"ExtendableCookieChangeEvent","signatures":[["type","?eventInitDict"]]},{"name":"FederatedCredential","signatures":[["data"]]},{"name":"PasswordCredential","signatures":[["data"]]},{"name":"getRandomValues","signatures":[["array"]]},{"name":"encrypt","signatures":[["algorithm","key","data"]]},{"name":"decrypt","signatures":[["algorithm","key","data"]]},{"name":"verify","signatures":[["algorithm","key","signature","data"]]},{"name":"digest","signatures":[["algorithm","data"]]},{"name":"generateKey","signatures":[["algorithm","extractable","keyUsages"]]},{"name":"importKey","signatures":[["format","keyData","algorithm","extractable","keyUsages"]]},{"name":"exportKey","signatures":[["format","key"]]},{"name":"deriveBits","signatures":[["algorithm","baseKey","length"]]},{"name":"deriveKey","signatures":[["algorithm","baseKey","derivedKeyType","extractable","keyUsages"]]},{"name":"wrapKey","signatures":[["format","key","wrappingKey","wrapAlgorithm"]]},{"name":"unwrapKey","signatures":[["format","wrappedKey","unwrappingKey","unwrapAlgorithm","unwrappedKeyAlgorithm","extractable","keyUsages"]]},{"name":"registerPaint","signatures":[["name","paintCtor"]]},{"name":"DeviceMotionEvent","signatures":[["type","?eventInitDict"]]},{"name":"DeviceOrientationEvent","signatures":[["type","?eventInitDict"]]},{"name":"TextDecoder","signatures":[["?label","?options"]]},{"name":"encode","signatures":[["?input"]]},{"name":"setMediaKeys","signatures":[["mediaKeys"]]},{"name":"generateRequest","signatures":[["initDataType","initData"]]},{"name":"getStatusForPolicy","signatures":[["policy"]]},{"name":"createSession","signatures":[["?sessionType"]]},{"name":"setServerCertificate","signatures":[["serverCertificate"]]},{"name":"requestMediaKeySystemAccess","signatures":[["keySystem","supportedConfigurations"]]},{"name":"EventSource","signatures":[["url","?eventSourceInitDict"]]},{"name":"webkitRequestFileSystem","signatures":[["type","size","?successCallback","?errorCallback"]],"receiver":"DedicatedWorkerGlobalScope"},{"name":"webkitRequestFileSystem","signatures":[["type","size","?successCallback","?errorCallback"]],"receiver":"SharedWorkerGlobalScope"},{"name":"webkitRequestFileSystem","signatures":[["type","size","successCallback","?errorCallback"]],"receiver":"Window"},{"name":"webkitRequestFileSystemSync","signatures":[["type","size"]]},{"name":"webkitResolveLocalFileSystemURL","signatures":[["url","successCallback","?errorCallback"]]},{"name":"webkitResolveLocalFileSystemSyncURL","signatures":[["url"]]},{"name":"isolatedFileSystem","signatures":[["fileSystemId","registeredName"]]},{"name":"upgradeDraggedFileSystemPermissions","signatures":[["domFileSystem"]]},{"name":"getFile","signatures":[["path","flags"]],"receiver":"DirectoryEntrySync"},{"name":"getFile","signatures":[["path","?options","?successCallback","?errorCallback"]],"receiver":"DirectoryEntry"},{"name":"getDirectory","signatures":[["path","flags"]],"receiver":"DirectoryEntrySync"},{"name":"getDirectory","signatures":[["path","?options","?successCallback","?errorCallback"]],"receiver":"DirectoryEntry"},{"name":"removeRecursively","signatures":[["successCallback","?errorCallback"]],"receiver":"DirectoryEntry"},{"name":"readEntries","signatures":[["successCallback","?errorCallback"]],"receiver":"DirectoryReader"},{"name":"getMetadata","signatures":[["successCallback","?errorCallback"]],"receiver":"Entry"},{"name":"copyTo","signatures":[["parent","name"]],"receiver":"EntrySync"},{"name":"copyTo","signatures":[["parent","?name","?successCallback","?errorCallback"]],"receiver":"Entry"},{"name":"getParent","signatures":[["?successCallback","?errorCallback"]],"receiver":"Entry"},{"name":"file","signatures":[["successCallback","?errorCallback"]],"receiver":"FileEntry"},{"name":"createWriter","signatures":[["successCallback","?errorCallback"]],"receiver":"FileEntry"},{"name":"seek","signatures":[["position"]]},{"name":"truncate","signatures":[["size"]]},{"name":"playEffect","signatures":[["type","params"]]},{"name":"getCurrentPosition","signatures":[["successCallback","?errorCallback","?options"]]},{"name":"watchPosition","signatures":[["successCallback","?errorCallback","?options"]]},{"name":"clearWatch","signatures":[["watchID"]]},{"name":"ImageCapture","signatures":[["track"]]},{"name":"takePhoto","signatures":[["?photoSettings"]]},{"name":"advance","signatures":[["count"]]},{"name":"continue","signatures":[["?key"]]},{"name":"continuePrimaryKey","signatures":[["key","primaryKey"]]},{"name":"transaction","signatures":[["storeNames","?mode"]],"receiver":"IDBDatabase"},{"name":"transaction","signatures":[["callback","?errorCallback","?successCallback"]],"receiver":"Database"},{"name":"createObjectStore","signatures":[["name","?options"]]},{"name":"deleteObjectStore","signatures":[["name"]]},{"name":"deleteDatabase","signatures":[["name"]]},{"name":"cmp","signatures":[["first","second"]]},{"name":"getKey","signatures":[["key"]]},{"name":"getAllKeys","signatures":[["?query","?count"]]},{"name":"count","signatures":[["?key"]],"receiver":"IDBIndex"},{"name":"count","signatures":[["?key"]],"receiver":"IDBObjectStore"},{"name":"count","signatures":[["?label"]],"receiver":"console"},{"name":"openCursor","signatures":[["?range","?direction"]]},{"name":"openKeyCursor","signatures":[["?range","?direction"]]},{"name":"only","signatures":[["value"]]},{"name":"lowerBound","signatures":[["bound","?open"]]},{"name":"upperBound","signatures":[["bound","?open"]]},{"name":"bound","signatures":[["lower","upper","?lowerOpen","?upperOpen"]]},{"name":"index","signatures":[["name"]]},{"name":"createIndex","signatures":[["name","keyPath","?options"]]},{"name":"deleteIndex","signatures":[["name"]]},{"name":"IDBObserver","signatures":[["callback"]]},{"name":"objectStore","signatures":[["name"]]},{"name":"IDBVersionChangeEvent","signatures":[["type","?eventInitDict"]]},{"name":"lock","signatures":[["?keyCodes"]],"receiver":"Keyboard"},{"name":"lock","signatures":[["orientation"]],"receiver":"ScreenOrientation"},{"name":"request","signatures":[["name","callback"],["name","options","callback"]],"receiver":"LockManager"},{"name":"request","signatures":[["permissions"]],"receiver":"Permissions"},{"name":"query","signatures":[["permission"]],"receiver":"Permissions"},{"name":"decodingInfo","signatures":[["configuration"]]},{"name":"encodingInfo","signatures":[["configuration"]]},{"name":"captureStream","signatures":[["?frameRate"]],"receiver":"HTMLCanvasElement"},{"name":"BlobEvent","signatures":[["type","eventInitDict"]]},{"name":"MediaRecorder","signatures":[["stream","?options"]]},{"name":"isTypeSupported","signatures":[["type"]]},{"name":"setActionHandler","signatures":[["action","handler"]]},{"name":"addSourceBuffer","signatures":[["type"]]},{"name":"removeSourceBuffer","signatures":[["buffer"]]},{"name":"endOfStream","signatures":[["?error"]]},{"name":"setLiveSeekableRange","signatures":[["start","end"]]},{"name":"appendBuffer","signatures":[["data"]]},{"name":"getUserMedia","signatures":[["?constraints"]],"receiver":"MediaDevices"},{"name":"getUserMedia","signatures":[["constraints","successCallback","errorCallback"]],"receiver":"Navigator"},{"name":"MediaStreamTrackEvent","signatures":[["type","eventInitDict"]]},{"name":"applyConstraints","signatures":[["?constraints"]]},{"name":"addTrack","signatures":[["track"]],"receiver":"MediaStream"},{"name":"addTrack","signatures":[["track","...streams"]],"receiver":"RTCPeerConnection"},{"name":"removeTrack","signatures":[["track"]],"receiver":"MediaStream"},{"name":"removeTrack","signatures":[["sender"]],"receiver":"RTCPeerConnection"},{"name":"webkitGetUserMedia","signatures":[["constraints","successCallback","errorCallback"]]},{"name":"registerProtocolHandler","signatures":[["scheme","url","title"]]},{"name":"unregisterProtocolHandler","signatures":[["scheme","url"]]},{"name":"cancelPush","signatures":[["?target"]]},{"name":"cancelWatch","signatures":[["?id"]]},{"name":"NotificationEvent","signatures":[["type","eventInitDict"]]},{"name":"Notification","signatures":[["title","?options"]]},{"name":"requestPermission","signatures":[["?deprecatedCallback"]]},{"name":"showNotification","signatures":[["title","?options"]]},{"name":"getNotifications","signatures":[["?filter"]]},{"name":"AbortPaymentEvent","signatures":[["type","eventInitDict"]]},{"name":"respondWith","signatures":[["paymentAbortedResponse"]],"receiver":"AbortPaymentEvent"},{"name":"respondWith","signatures":[["canMakePaymentResponse"]],"receiver":"CanMakePaymentEvent"},{"name":"respondWith","signatures":[["response"]],"receiver":"PaymentRequestEvent"},{"name":"respondWith","signatures":[["r"]],"receiver":"FetchEvent"},{"name":"CanMakePaymentEvent","signatures":[["type","eventInitDict"]]},{"name":"PaymentRequestEvent","signatures":[["type","eventInitDict"]]},{"name":"openWindow","signatures":[["url"]]},{"name":"PaymentRequestUpdateEvent","signatures":[["type","?eventInitDict"]]},{"name":"updateWith","signatures":[["detailsPromise"]]},{"name":"PaymentRequest","signatures":[["methodData","details","?options"]]},{"name":"RTCDataChannelEvent","signatures":[["type","eventInitDict"]]},{"name":"insertDTMF","signatures":[["tones","?duration","?interToneGap"]]},{"name":"stat","signatures":[["name"]]},{"name":"RTCPeerConnectionIceEvent","signatures":[["type","?eventInitDict"]]},{"name":"RTCPeerConnection","signatures":[["?configuration","?mediaConstraints"]]},{"name":"createOffer","signatures":[["?options"],["successCallback","failureCallback","?rtcOfferOptions"]]},{"name":"createAnswer","signatures":[["?options"],["successCallback","failureCallback","?mediaConstraints"]]},{"name":"setLocalDescription","signatures":[["description","?successCallback","?failureCallback"]]},{"name":"setRemoteDescription","signatures":[["description","?successCallback","?failureCallback"]]},{"name":"addIceCandidate","signatures":[["candidate","?successCallback","?failureCallback"]]},{"name":"setConfiguration","signatures":[["configuration"]]},{"name":"getStats","signatures":[["?callbackOrSelector"],["successCallback","selector"]],"receiver":"RTCPeerConnection"},{"name":"createDataChannel","signatures":[["label","?dataChannelDict"]]},{"name":"generateCertificate","signatures":[["keygenAlgorithm"]]},{"name":"addStream","signatures":[["stream","?mediaConstraints"]]},{"name":"removeStream","signatures":[["stream"]]},{"name":"createDTMFSender","signatures":[["track"]]},{"name":"setParameters","signatures":[["?parameters"]]},{"name":"replaceTrack","signatures":[["withTrack"]]},{"name":"revoke","signatures":[["permission"]]},{"name":"requestAll","signatures":[["permissions"]]},{"name":"refresh","signatures":[["?reload"]]},{"name":"PresentationConnectionAvailableEvent","signatures":[["type","eventInitDict"]]},{"name":"PresentationConnectionCloseEvent","signatures":[["type","eventInitDict"]]},{"name":"PresentationRequest","signatures":[["url"]]},{"name":"reconnect","signatures":[["id"]]},{"name":"PushEvent","signatures":[["type","?eventInitDict"]]},{"name":"queryUsageAndQuota","signatures":[["storageType","?usageCallback","?errorCallback"]],"receiver":"DeprecatedStorageInfo"},{"name":"queryUsageAndQuota","signatures":[["usageCallback","?errorCallback"]],"receiver":"DeprecatedStorageQuota"},{"name":"requestQuota","signatures":[["storageType","newQuotaInBytes","?quotaCallback","?errorCallback"]],"receiver":"DeprecatedStorageInfo"},{"name":"requestQuota","signatures":[["newQuotaInBytes","?quotaCallback","?errorCallback"]],"receiver":"DeprecatedStorageQuota"},{"name":"watchAvailability","signatures":[["callback"]]},{"name":"cancelWatchAvailability","signatures":[["?id"]]},{"name":"AbsoluteOrientationSensor","signatures":[["?sensorOptions"]]},{"name":"Accelerometer","signatures":[["?sensorOptions"]]},{"name":"AmbientLightSensor","signatures":[["?sensorOptions"]]},{"name":"Gyroscope","signatures":[["?sensorOptions"]]},{"name":"LinearAccelerationSensor","signatures":[["?sensorOptions"]]},{"name":"populateMatrix","signatures":[["targetBuffer"]]},{"name":"RelativeOrientationSensor","signatures":[["?sensorOptions"]]},{"name":"SensorErrorEvent","signatures":[["type","eventInitDict"]]},{"name":"ExtendableEvent","signatures":[["type","?eventInitDict"]]},{"name":"waitUntil","signatures":[["f"]]},{"name":"FetchEvent","signatures":[["type","eventInitDict"]]},{"name":"InstallEvent","signatures":[["type","?eventInitDict"]]},{"name":"enable","signatures":[["cap"]],"receiver":"WebGLRenderingContextBase"},{"name":"disable","signatures":[["cap"]],"receiver":"WebGLRenderingContextBase"},{"name":"setHeaderValue","signatures":[["value"]]},{"name":"getRegistration","signatures":[["?documentURL"]]},{"name":"navigate","signatures":[["url"]]},{"name":"detect","signatures":[["image"]]},{"name":"FaceDetector","signatures":[["?faceDetectorOptions"]]},{"name":"addFromUri","signatures":[["src","?weight"]]},{"name":"addFromString","signatures":[["string","?weight"]]},{"name":"speak","signatures":[["utterance"]]},{"name":"initStorageEvent","signatures":[["?typeArg","?canBubbleArg","?cancelableArg","?keyArg","?oldValueArg","?newValueArg","?urlArg","?storageAreaArg"]]},{"name":"key","signatures":[["index"]]},{"name":"setItem","signatures":[["key","value"]]},{"name":"vibrate","signatures":[["pattern"]]},{"name":"getFrameData","signatures":[["frameData"]]},{"name":"getEyeParameters","signatures":[["whichEye"]]},{"name":"requestPresent","signatures":[["layers"]]},{"name":"getFloatFrequencyData","signatures":[["array"]]},{"name":"getByteFrequencyData","signatures":[["array"]]},{"name":"getFloatTimeDomainData","signatures":[["array"]]},{"name":"getByteTimeDomainData","signatures":[["array"]]},{"name":"getChannelData","signatures":[["channelIndex"]]},{"name":"copyFromChannel","signatures":[["destination","channelNumber","?startInChannel"]]},{"name":"copyToChannel","signatures":[["source","channelNumber","?startInChannel"]]},{"name":"suspend","signatures":[["suspendTime"]],"receiver":"OfflineAudioContext"},{"name":"setOrientation","signatures":[["x","y","z","xUp","yUp","zUp"]],"receiver":"AudioListener"},{"name":"setOrientation","signatures":[["x","y","z"]],"receiver":"PannerNode"},{"name":"setValueAtTime","signatures":[["value","time"]]},{"name":"linearRampToValueAtTime","signatures":[["value","time"]]},{"name":"exponentialRampToValueAtTime","signatures":[["value","time"]]},{"name":"setTargetAtTime","signatures":[["target","time","timeConstant"]]},{"name":"setValueCurveAtTime","signatures":[["values","time","duration"]]},{"name":"cancelScheduledValues","signatures":[["startTime"]]},{"name":"cancelAndHoldAtTime","signatures":[["startTime"]]},{"name":"registerProcessor","signatures":[["name","processorConstructor"]]},{"name":"createBuffer","signatures":[["numberOfChannels","numberOfFrames","sampleRate"]],"receiver":"BaseAudioContext"},{"name":"decodeAudioData","signatures":[["audioData","?successCallback","?errorCallback"]]},{"name":"createDelay","signatures":[["?maxDelayTime"]]},{"name":"createIIRFilter","signatures":[["feedForward","feedBack"]]},{"name":"createScriptProcessor","signatures":[["?bufferSize","?numberOfInputChannels","?numberOfOutputChannels"]]},{"name":"createPeriodicWave","signatures":[["real","imag","?options"]]},{"name":"createChannelSplitter","signatures":[["?numberOfOutputs"]]},{"name":"createChannelMerger","signatures":[["?numberOfInputs"]]},{"name":"createMediaElementSource","signatures":[["mediaElement"]]},{"name":"createMediaStreamSource","signatures":[["mediaStream"]]},{"name":"getFrequencyResponse","signatures":[["frequencyHz","magResponse","phaseResponse"]]},{"name":"setPeriodicWave","signatures":[["periodicWave"]]},{"name":"changeVersion","signatures":[["oldVersion","newVersion","?callback","?errorCallback","?successCallback"]]},{"name":"readTransaction","signatures":[["callback","?errorCallback","?successCallback"]]},{"name":"executeSql","signatures":[["sqlStatement","?arguments","?callback","?errorCallback"]]},{"name":"openDatabase","signatures":[["name","version","displayName","estimatedSize","?creationCallback"]]},{"name":"drawArraysInstancedANGLE","signatures":[["mode","first","count","primcount"]]},{"name":"drawElementsInstancedANGLE","signatures":[["mode","count","type","offset","primcount"]]},{"name":"vertexAttribDivisorANGLE","signatures":[["index","divisor"]]},{"name":"queryCounterEXT","signatures":[["query","target"]]},{"name":"deleteQueryEXT","signatures":[["query"]]},{"name":"isQueryEXT","signatures":[["query"]]},{"name":"beginQueryEXT","signatures":[["target","query"]]},{"name":"endQueryEXT","signatures":[["target"]]},{"name":"getQueryEXT","signatures":[["target","pname"]]},{"name":"getQueryObjectEXT","signatures":[["query","pname"]]},{"name":"deleteVertexArrayOES","signatures":[["?arrayObject"]]},{"name":"isVertexArrayOES","signatures":[["?arrayObject"]]},{"name":"bindVertexArrayOES","signatures":[["?arrayObject"]]},{"name":"getTranslatedShaderSource","signatures":[["shader"]]},{"name":"drawBuffersWEBGL","signatures":[["buffers"]]},{"name":"getBufferSubDataAsync","signatures":[["target","srcByteOffset","dstData","?dstOffset","?length"]]},{"name":"activeTexture","signatures":[["texture"]]},{"name":"attachShader","signatures":[["program","shader"]]},{"name":"bindAttribLocation","signatures":[["program","index","name"]]},{"name":"bindBuffer","signatures":[["target","buffer"]]},{"name":"bindFramebuffer","signatures":[["target","framebuffer"]]},{"name":"bindRenderbuffer","signatures":[["target","renderbuffer"]]},{"name":"bindTexture","signatures":[["target","texture"]]},{"name":"blendColor","signatures":[["red","green","blue","alpha"]]},{"name":"blendEquation","signatures":[["mode"]]},{"name":"blendEquationSeparate","signatures":[["modeRGB","modeAlpha"]]},{"name":"blendFunc","signatures":[["sfactor","dfactor"]]},{"name":"blendFuncSeparate","signatures":[["srcRGB","dstRGB","srcAlpha","dstAlpha"]]},{"name":"bufferData","signatures":[["target","size","usage"],["target","data","usage"]],"receiver":"WebGLRenderingContextBase"},{"name":"bufferData","signatures":[["target","srcData","usage","srcOffset","?length"]],"receiver":"WebGL2RenderingContextBase"},{"name":"bufferSubData","signatures":[["target","offset","data"]],"receiver":"WebGLRenderingContextBase"},{"name":"bufferSubData","signatures":[["target","dstByteOffset","srcData","srcOffset","?length"]],"receiver":"WebGL2RenderingContextBase"},{"name":"checkFramebufferStatus","signatures":[["target"]]},{"name":"clearColor","signatures":[["red","green","blue","alpha"]]},{"name":"clearDepth","signatures":[["depth"]]},{"name":"clearStencil","signatures":[["s"]]},{"name":"colorMask","signatures":[["red","green","blue","alpha"]]},{"name":"compileShader","signatures":[["shader"]]},{"name":"compressedTexImage2D","signatures":[["target","level","internalformat","width","height","border","data"]],"receiver":"WebGLRenderingContextBase"},{"name":"compressedTexImage2D","signatures":[["target","level","internalformat","width","height","border","imageSize","offset"],["target","level","internalformat","width","height","border","data","srcOffset","?srcLengthOverride"]],"receiver":"WebGL2RenderingContextBase"},{"name":"compressedTexSubImage2D","signatures":[["target","level","xoffset","yoffset","width","height","format","data"]],"receiver":"WebGLRenderingContextBase"},{"name":"compressedTexSubImage2D","signatures":[["target","level","xoffset","yoffset","width","height","format","imageSize","offset"],["target","level","xoffset","yoffset","width","height","format","data","srcOffset","?srcLengthOverride"]],"receiver":"WebGL2RenderingContextBase"},{"name":"copyTexImage2D","signatures":[["target","level","internalformat","x","y","width","height","border"]]},{"name":"copyTexSubImage2D","signatures":[["target","level","xoffset","yoffset","x","y","width","height"]]},{"name":"createShader","signatures":[["type"]]},{"name":"cullFace","signatures":[["mode"]]},{"name":"deleteBuffer","signatures":[["buffer"]]},{"name":"deleteFramebuffer","signatures":[["framebuffer"]]},{"name":"deleteProgram","signatures":[["program"]]},{"name":"deleteRenderbuffer","signatures":[["renderbuffer"]]},{"name":"deleteShader","signatures":[["shader"]]},{"name":"deleteTexture","signatures":[["texture"]]},{"name":"depthFunc","signatures":[["func"]]},{"name":"depthMask","signatures":[["flag"]]},{"name":"depthRange","signatures":[["zNear","zFar"]]},{"name":"detachShader","signatures":[["program","shader"]]},{"name":"disableVertexAttribArray","signatures":[["index"]]},{"name":"drawArrays","signatures":[["mode","first","count"]]},{"name":"drawElements","signatures":[["mode","count","type","offset"]]},{"name":"enableVertexAttribArray","signatures":[["index"]]},{"name":"framebufferRenderbuffer","signatures":[["target","attachment","renderbuffertarget","renderbuffer"]]},{"name":"framebufferTexture2D","signatures":[["target","attachment","textarget","texture","level"]]},{"name":"frontFace","signatures":[["mode"]]},{"name":"generateMipmap","signatures":[["target"]]},{"name":"getActiveAttrib","signatures":[["program","index"]]},{"name":"getActiveUniform","signatures":[["program","index"]]},{"name":"getAttachedShaders","signatures":[["program"]]},{"name":"getAttribLocation","signatures":[["program","name"]]},{"name":"getBufferParameter","signatures":[["target","pname"]]},{"name":"getExtension","signatures":[["name"]]},{"name":"getFramebufferAttachmentParameter","signatures":[["target","attachment","pname"]]},{"name":"getProgramParameter","signatures":[["program","pname"]]},{"name":"getProgramInfoLog","signatures":[["program"]]},{"name":"getRenderbufferParameter","signatures":[["target","pname"]]},{"name":"getShaderParameter","signatures":[["shader","pname"]]},{"name":"getShaderInfoLog","signatures":[["shader"]]},{"name":"getShaderPrecisionFormat","signatures":[["shadertype","precisiontype"]]},{"name":"getShaderSource","signatures":[["shader"]]},{"name":"getTexParameter","signatures":[["target","pname"]]},{"name":"getUniform","signatures":[["program","location"]]},{"name":"getUniformLocation","signatures":[["program","name"]]},{"name":"getVertexAttrib","signatures":[["index","pname"]]},{"name":"getVertexAttribOffset","signatures":[["index","pname"]]},{"name":"hint","signatures":[["target","mode"]]},{"name":"isBuffer","signatures":[["buffer"]]},{"name":"isEnabled","signatures":[["cap"]]},{"name":"isFramebuffer","signatures":[["framebuffer"]]},{"name":"isProgram","signatures":[["program"]]},{"name":"isRenderbuffer","signatures":[["renderbuffer"]]},{"name":"isShader","signatures":[["shader"]]},{"name":"isTexture","signatures":[["texture"]]},{"name":"lineWidth","signatures":[["width"]]},{"name":"linkProgram","signatures":[["program"]]},{"name":"pixelStorei","signatures":[["pname","param"]]},{"name":"polygonOffset","signatures":[["factor","units"]]},{"name":"readPixels","signatures":[["x","y","width","height","format","type","pixels"]],"receiver":"WebGLRenderingContextBase"},{"name":"readPixels","signatures":[["x","y","width","height","format","type","offset"],["x","y","width","height","format","type","dstData","offset"]],"receiver":"WebGL2RenderingContextBase"},{"name":"renderbufferStorage","signatures":[["target","internalformat","width","height"]]},{"name":"sampleCoverage","signatures":[["value","invert"]]},{"name":"scissor","signatures":[["x","y","width","height"]]},{"name":"shaderSource","signatures":[["shader","string"]]},{"name":"stencilFunc","signatures":[["func","ref","mask"]]},{"name":"stencilFuncSeparate","signatures":[["face","func","ref","mask"]]},{"name":"stencilMask","signatures":[["mask"]]},{"name":"stencilMaskSeparate","signatures":[["face","mask"]]},{"name":"stencilOp","signatures":[["fail","zfail","zpass"]]},{"name":"stencilOpSeparate","signatures":[["face","fail","zfail","zpass"]]},{"name":"texParameterf","signatures":[["target","pname","param"]]},{"name":"texParameteri","signatures":[["target","pname","param"]]},{"name":"texImage2D","signatures":[["target","level","internalformat","format","type","pixels"],["target","level","internalformat","format","type","image"],["target","level","internalformat","format","type","canvas"],["target","level","internalformat","format","type","video"],["target","level","internalformat","format","type","bitmap"],["target","level","internalformat","width","height","border","format","type","pixels"]],"receiver":"WebGLRenderingContextBase"},{"name":"texImage2D","signatures":[["target","level","internalformat","width","height","border","format","type","offset"],["target","level","internalformat","width","height","border","format","type","data"],["target","level","internalformat","width","height","border","format","type","image"],["target","level","internalformat","width","height","border","format","type","canvas"],["target","level","internalformat","width","height","border","format","type","video"],["target","level","internalformat","width","height","border","format","type","bitmap"],["target","level","internalformat","width","height","border","format","type","srcData","srcOffset"]],"receiver":"WebGL2RenderingContextBase"},{"name":"texSubImage2D","signatures":[["target","level","xoffset","yoffset","format","type","pixels"],["target","level","xoffset","yoffset","format","type","image"],["target","level","xoffset","yoffset","format","type","canvas"],["target","level","xoffset","yoffset","format","type","video"],["target","level","xoffset","yoffset","format","type","bitmap"],["target","level","xoffset","yoffset","width","height","format","type","pixels"]],"receiver":"WebGLRenderingContextBase"},{"name":"texSubImage2D","signatures":[["target","level","xoffset","yoffset","width","height","format","type","offset"],["target","level","xoffset","yoffset","width","height","format","type","data"],["target","level","xoffset","yoffset","width","height","format","type","image"],["target","level","xoffset","yoffset","width","height","format","type","canvas"],["target","level","xoffset","yoffset","width","height","format","type","video"],["target","level","xoffset","yoffset","width","height","format","type","bitmap"],["target","level","xoffset","yoffset","width","height","format","type","srcData","srcOffset"]],"receiver":"WebGL2RenderingContextBase"},{"name":"uniform1f","signatures":[["location","x"]]},{"name":"uniform1fv","signatures":[["location","v"]],"receiver":"WebGLRenderingContextBase"},{"name":"uniform1fv","signatures":[["location","v","srcOffset","?srcLength"]],"receiver":"WebGL2RenderingContextBase"},{"name":"uniform1i","signatures":[["location","x"]]},{"name":"uniform1iv","signatures":[["location","v"]],"receiver":"WebGLRenderingContextBase"},{"name":"uniform1iv","signatures":[["location","v","srcOffset","?srcLength"]],"receiver":"WebGL2RenderingContextBase"},{"name":"uniform2f","signatures":[["location","x","y"]]},{"name":"uniform2fv","signatures":[["location","v"]],"receiver":"WebGLRenderingContextBase"},{"name":"uniform2fv","signatures":[["location","v","srcOffset","?srcLength"]],"receiver":"WebGL2RenderingContextBase"},{"name":"uniform2i","signatures":[["location","x","y"]]},{"name":"uniform2iv","signatures":[["location","v"]],"receiver":"WebGLRenderingContextBase"},{"name":"uniform2iv","signatures":[["location","v","srcOffset","?srcLength"]],"receiver":"WebGL2RenderingContextBase"},{"name":"uniform3f","signatures":[["location","x","y","z"]]},{"name":"uniform3fv","signatures":[["location","v"]],"receiver":"WebGLRenderingContextBase"},{"name":"uniform3fv","signatures":[["location","v","srcOffset","?srcLength"]],"receiver":"WebGL2RenderingContextBase"},{"name":"uniform3i","signatures":[["location","x","y","z"]]},{"name":"uniform3iv","signatures":[["location","v"]],"receiver":"WebGLRenderingContextBase"},{"name":"uniform3iv","signatures":[["location","v","srcOffset","?srcLength"]],"receiver":"WebGL2RenderingContextBase"},{"name":"uniform4f","signatures":[["location","x","y","z","w"]]},{"name":"uniform4fv","signatures":[["location","v"]],"receiver":"WebGLRenderingContextBase"},{"name":"uniform4fv","signatures":[["location","v","srcOffset","?srcLength"]],"receiver":"WebGL2RenderingContextBase"},{"name":"uniform4i","signatures":[["location","x","y","z","w"]]},{"name":"uniform4iv","signatures":[["location","v"]],"receiver":"WebGLRenderingContextBase"},{"name":"uniform4iv","signatures":[["location","v","srcOffset","?srcLength"]],"receiver":"WebGL2RenderingContextBase"},{"name":"uniformMatrix2fv","signatures":[["location","transpose","array"]],"receiver":"WebGLRenderingContextBase"},{"name":"uniformMatrix2fv","signatures":[["location","transpose","array","srcOffset","?srcLength"]],"receiver":"WebGL2RenderingContextBase"},{"name":"uniformMatrix3fv","signatures":[["location","transpose","array"]],"receiver":"WebGLRenderingContextBase"},{"name":"uniformMatrix3fv","signatures":[["location","transpose","array","srcOffset","?srcLength"]],"receiver":"WebGL2RenderingContextBase"},{"name":"uniformMatrix4fv","signatures":[["location","transpose","array"]],"receiver":"WebGLRenderingContextBase"},{"name":"uniformMatrix4fv","signatures":[["location","transpose","array","srcOffset","?srcLength"]],"receiver":"WebGL2RenderingContextBase"},{"name":"useProgram","signatures":[["program"]]},{"name":"validateProgram","signatures":[["program"]]},{"name":"vertexAttrib1f","signatures":[["indx","x"]]},{"name":"vertexAttrib1fv","signatures":[["indx","values"]]},{"name":"vertexAttrib2f","signatures":[["indx","x","y"]]},{"name":"vertexAttrib2fv","signatures":[["indx","values"]]},{"name":"vertexAttrib3f","signatures":[["indx","x","y","z"]]},{"name":"vertexAttrib3fv","signatures":[["indx","values"]]},{"name":"vertexAttrib4f","signatures":[["indx","x","y","z","w"]]},{"name":"vertexAttrib4fv","signatures":[["indx","values"]]},{"name":"vertexAttribPointer","signatures":[["indx","size","type","normalized","stride","offset"]]},{"name":"viewport","signatures":[["x","y","width","height"]]},{"name":"setCompatibleXRDevice","signatures":[["device"]]},{"name":"copyBufferSubData","signatures":[["readTarget","writeTarget","readOffset","writeOffset","size"]]},{"name":"getBufferSubData","signatures":[["target","srcByteOffset","dstData","?dstOffset","?length"]]},{"name":"blitFramebuffer","signatures":[["srcX0","srcY0","srcX1","srcY1","dstX0","dstY0","dstX1","dstY1","mask","filter"]]},{"name":"framebufferTextureLayer","signatures":[["target","attachment","texture","level","layer"]]},{"name":"getInternalformatParameter","signatures":[["target","internalformat","pname"]]},{"name":"invalidateFramebuffer","signatures":[["target","attachments"]]},{"name":"invalidateSubFramebuffer","signatures":[["target","attachments","x","y","width","height"]]},{"name":"readBuffer","signatures":[["mode"]]},{"name":"renderbufferStorageMultisample","signatures":[["target","samples","internalformat","width","height"]]},{"name":"texStorage2D","signatures":[["target","levels","internalformat","width","height"]]},{"name":"texStorage3D","signatures":[["target","levels","internalformat","width","height","depth"]]},{"name":"texImage3D","signatures":[["target","level","internalformat","width","height","depth","border","format","type","offset"],["target","level","internalformat","width","height","depth","border","format","type","data"],["target","level","internalformat","width","height","depth","border","format","type","image"],["target","level","internalformat","width","height","depth","border","format","type","canvas"],["target","level","internalformat","width","height","depth","border","format","type","video"],["target","level","internalformat","width","height","depth","border","format","type","bitmap"],["target","level","internalformat","width","height","depth","border","format","type","pixels","?srcOffset"]]},{"name":"texSubImage3D","signatures":[["target","level","xoffset","yoffset","zoffset","width","height","depth","format","type","offset"],["target","level","xoffset","yoffset","zoffset","width","height","depth","format","type","data"],["target","level","xoffset","yoffset","zoffset","width","height","depth","format","type","image"],["target","level","xoffset","yoffset","zoffset","width","height","depth","format","type","canvas"],["target","level","xoffset","yoffset","zoffset","width","height","depth","format","type","video"],["target","level","xoffset","yoffset","zoffset","width","height","depth","format","type","bitmap"],["target","level","xoffset","yoffset","zoffset","width","height","depth","format","type","pixels","?srcOffset"]]},{"name":"copyTexSubImage3D","signatures":[["target","level","xoffset","yoffset","zoffset","x","y","width","height"]]},{"name":"compressedTexImage3D","signatures":[["target","level","internalformat","width","height","depth","border","imageSize","offset"],["target","level","internalformat","width","height","depth","border","data","?srcOffset","?srcLengthOverride"]]},{"name":"compressedTexSubImage3D","signatures":[["target","level","xoffset","yoffset","zoffset","width","height","depth","format","imageSize","offset"],["target","level","xoffset","yoffset","zoffset","width","height","depth","format","data","?srcOffset","?srcLengthOverride"]]},{"name":"getFragDataLocation","signatures":[["program","name"]]},{"name":"uniform1ui","signatures":[["location","v0"]]},{"name":"uniform2ui","signatures":[["location","v0","v1"]]},{"name":"uniform3ui","signatures":[["location","v0","v1","v2"]]},{"name":"uniform4ui","signatures":[["location","v0","v1","v2","v3"]]},{"name":"uniform1uiv","signatures":[["location","v","?srcOffset","?srcLength"]]},{"name":"uniform2uiv","signatures":[["location","v","?srcOffset","?srcLength"]]},{"name":"uniform3uiv","signatures":[["location","v","?srcOffset","?srcLength"]]},{"name":"uniform4uiv","signatures":[["location","v","?srcOffset","?srcLength"]]},{"name":"uniformMatrix2x3fv","signatures":[["location","transpose","value","?srcOffset","?srcLength"]]},{"name":"uniformMatrix3x2fv","signatures":[["location","transpose","value","?srcOffset","?srcLength"]]},{"name":"uniformMatrix2x4fv","signatures":[["location","transpose","value","?srcOffset","?srcLength"]]},{"name":"uniformMatrix4x2fv","signatures":[["location","transpose","value","?srcOffset","?srcLength"]]},{"name":"uniformMatrix3x4fv","signatures":[["location","transpose","value","?srcOffset","?srcLength"]]},{"name":"uniformMatrix4x3fv","signatures":[["location","transpose","value","?srcOffset","?srcLength"]]},{"name":"vertexAttribI4i","signatures":[["index","x","y","z","w"]]},{"name":"vertexAttribI4iv","signatures":[["index","v"]]},{"name":"vertexAttribI4ui","signatures":[["index","x","y","z","w"]]},{"name":"vertexAttribI4uiv","signatures":[["index","v"]]},{"name":"vertexAttribIPointer","signatures":[["index","size","type","stride","offset"]]},{"name":"vertexAttribDivisor","signatures":[["index","divisor"]]},{"name":"drawArraysInstanced","signatures":[["mode","first","count","instanceCount"]]},{"name":"drawElementsInstanced","signatures":[["mode","count","type","offset","instanceCount"]]},{"name":"drawRangeElements","signatures":[["mode","start","end","count","type","offset"]]},{"name":"drawBuffers","signatures":[["buffers"]]},{"name":"clearBufferiv","signatures":[["buffer","drawbuffer","value","?srcOffset"]]},{"name":"clearBufferuiv","signatures":[["buffer","drawbuffer","value","?srcOffset"]]},{"name":"clearBufferfv","signatures":[["buffer","drawbuffer","value","?srcOffset"]]},{"name":"clearBufferfi","signatures":[["buffer","drawbuffer","depth","stencil"]]},{"name":"deleteQuery","signatures":[["query"]]},{"name":"isQuery","signatures":[["query"]]},{"name":"beginQuery","signatures":[["target","query"]]},{"name":"endQuery","signatures":[["target"]]},{"name":"getQuery","signatures":[["target","pname"]]},{"name":"getQueryParameter","signatures":[["query","pname"]]},{"name":"deleteSampler","signatures":[["sampler"]]},{"name":"isSampler","signatures":[["sampler"]]},{"name":"bindSampler","signatures":[["unit","sampler"]]},{"name":"samplerParameteri","signatures":[["sampler","pname","param"]]},{"name":"samplerParameterf","signatures":[["sampler","pname","param"]]},{"name":"getSamplerParameter","signatures":[["sampler","pname"]]},{"name":"fenceSync","signatures":[["condition","flags"]]},{"name":"isSync","signatures":[["sync"]]},{"name":"deleteSync","signatures":[["sync"]]},{"name":"clientWaitSync","signatures":[["sync","flags","timeout"]]},{"name":"waitSync","signatures":[["sync","flags","timeout"]]},{"name":"getSyncParameter","signatures":[["sync","pname"]]},{"name":"deleteTransformFeedback","signatures":[["feedback"]]},{"name":"isTransformFeedback","signatures":[["feedback"]]},{"name":"bindTransformFeedback","signatures":[["target","feedback"]]},{"name":"beginTransformFeedback","signatures":[["primitiveMode"]]},{"name":"transformFeedbackVaryings","signatures":[["program","varyings","bufferMode"]]},{"name":"getTransformFeedbackVarying","signatures":[["program","index"]]},{"name":"bindBufferBase","signatures":[["target","index","buffer"]]},{"name":"bindBufferRange","signatures":[["target","index","buffer","offset","size"]]},{"name":"getIndexedParameter","signatures":[["target","index"]]},{"name":"getUniformIndices","signatures":[["program","uniformNames"]]},{"name":"getActiveUniforms","signatures":[["program","uniformIndices","pname"]]},{"name":"getUniformBlockIndex","signatures":[["program","uniformBlockName"]]},{"name":"getActiveUniformBlockParameter","signatures":[["program","uniformBlockIndex","pname"]]},{"name":"getActiveUniformBlockName","signatures":[["program","uniformBlockIndex"]]},{"name":"uniformBlockBinding","signatures":[["program","uniformBlockIndex","uniformBlockBinding"]]},{"name":"deleteVertexArray","signatures":[["vertexArray"]]},{"name":"isVertexArray","signatures":[["vertexArray"]]},{"name":"bindVertexArray","signatures":[["vertexArray"]]},{"name":"requestMIDIAccess","signatures":[["?options"]]},{"name":"share","signatures":[["?data"]]},{"name":"CloseEvent","signatures":[["type","?eventInitDict"]]},{"name":"WebSocket","signatures":[["url","?protocols"]]},{"name":"selectConfiguration","signatures":[["configurationValue"]]},{"name":"claimInterface","signatures":[["interfaceNumber"]]},{"name":"releaseInterface","signatures":[["interfaceNumber"]]},{"name":"selectAlternateInterface","signatures":[["interfaceNumber","alternateSetting"]]},{"name":"controlTransferIn","signatures":[["setup","length"]]},{"name":"controlTransferOut","signatures":[["setup","?data"]]},{"name":"clearHalt","signatures":[["direction","endpointNumber"]]},{"name":"transferIn","signatures":[["endpointNumber","length"]]},{"name":"transferOut","signatures":[["endpointNumber","data"]]},{"name":"isochronousTransferIn","signatures":[["endpointNumber","packetLengths"]]},{"name":"isochronousTransferOut","signatures":[["endpointNumber","data","packetLengths"]]},{"name":"getTransformTo","signatures":[["other"]]},{"name":"getViewMatrix","signatures":[["view"]]},{"name":"supportsSession","signatures":[["?options"]]},{"name":"requestSession","signatures":[["?options"]]},{"name":"getDevicePose","signatures":[["coordinateSystem"]]},{"name":"getInputPose","signatures":[["inputSource","coordinateSystem"]]},{"name":"requestFrameOfReference","signatures":[["type","?options"]]},{"name":"getViewport","signatures":[["view"]]},{"name":"requestViewportScaling","signatures":[["viewportScaleFactor"]]},{"name":"assert","signatures":[["?condition","...data"]]},{"name":"debug","signatures":[["...data"]]},{"name":"dir","signatures":[["item","?options"]]},{"name":"dirxml","signatures":[["...data"]]},{"name":"group","signatures":[["...data"]]},{"name":"groupCollapsed","signatures":[["...data"]]},{"name":"info","signatures":[["...data"]]},{"name":"profile","signatures":[["?title"]]},{"name":"profileEnd","signatures":[["?title"]]},{"name":"table","signatures":[["...tabularData"]]},{"name":"time","signatures":[["?label"]]},{"name":"timeEnd","signatures":[["?label"]]},{"name":"timeStamp","signatures":[["?name"]]},{"name":"trace","signatures":[["...data"]]},{"name":"warn","signatures":[["...data"]]}];
\ No newline at end of file
+JavaScriptMetadata.NativeFunctions = [{"name":"find","signatures":[["predicate","?thisArg"]],"receiver":"Array"},{"name":"find","signatures":[["predicate","?thisArg"]],"receiver":"ReadonlyArray"},{"name":"find","signatures":[["predicate","?thisArg"]],"receiver":"Int8Array"},{"name":"find","signatures":[["predicate","?thisArg"]],"receiver":"Uint8Array"},{"name":"find","signatures":[["predicate","?thisArg"]],"receiver":"Uint8ClampedArray"},{"name":"find","signatures":[["predicate","?thisArg"]],"receiver":"Int16Array"},{"name":"find","signatures":[["predicate","?thisArg"]],"receiver":"Uint16Array"},{"name":"find","signatures":[["predicate","?thisArg"]],"receiver":"Int32Array"},{"name":"find","signatures":[["predicate","?thisArg"]],"receiver":"Uint32Array"},{"name":"find","signatures":[["predicate","?thisArg"]],"receiver":"Float32Array"},{"name":"find","signatures":[["predicate","?thisArg"]],"receiver":"Float64Array"},{"name":"find","signatures":[["?string","?caseSensitive","?backwards","?wrap","?wholeWord","?searchInFrames","?showDialog"]],"receiver":"Window"},{"name":"findIndex","signatures":[["predicate","?thisArg"]]},{"name":"fill","signatures":[["value","?start","?end"]],"receiver":"Array"},{"name":"fill","signatures":[["value","?start","?end"]],"receiver":"Int8Array"},{"name":"fill","signatures":[["value","?start","?end"]],"receiver":"Uint8Array"},{"name":"fill","signatures":[["value","?start","?end"]],"receiver":"Uint8ClampedArray"},{"name":"fill","signatures":[["value","?start","?end"]],"receiver":"Int16Array"},{"name":"fill","signatures":[["value","?start","?end"]],"receiver":"Uint16Array"},{"name":"fill","signatures":[["value","?start","?end"]],"receiver":"Int32Array"},{"name":"fill","signatures":[["value","?start","?end"]],"receiver":"Uint32Array"},{"name":"fill","signatures":[["value","?start","?end"]],"receiver":"Float32Array"},{"name":"fill","signatures":[["value","?start","?end"]],"receiver":"Float64Array"},{"name":"fill","signatures":[["?winding"],["path","?winding"]],"receiver":"CanvasRenderingContext2D"},{"name":"fill","signatures":[["?winding"],["path","?winding"]],"receiver":"OffscreenCanvasRenderingContext2D"},{"name":"fill","signatures":[["?winding"],["path","?winding"]],"receiver":"PaintRenderingContext2D"},{"name":"copyWithin","signatures":[["target","start","?end"]]},{"name":"from","signatures":[["arrayLike","?mapfn","?thisArg"],["iterable","?mapfn","?thisArg"]],"static":true,"receiver":"Array"},{"name":"from","signatures":[["arrayLike","?mapfn","?thisArg"]],"static":true,"receiver":"Int8Array"},{"name":"from","signatures":[["arrayLike","?mapfn","?thisArg"]],"static":true,"receiver":"Uint8Array"},{"name":"from","signatures":[["arrayLike","?mapfn","?thisArg"]],"static":true,"receiver":"Uint8ClampedArray"},{"name":"from","signatures":[["arrayLike","?mapfn","?thisArg"]],"static":true,"receiver":"Int16Array"},{"name":"from","signatures":[["arrayLike","?mapfn","?thisArg"]],"static":true,"receiver":"Uint16Array"},{"name":"from","signatures":[["arrayLike","?mapfn","?thisArg"]],"static":true,"receiver":"Int32Array"},{"name":"from","signatures":[["arrayLike","?mapfn","?thisArg"]],"static":true,"receiver":"Uint32Array"},{"name":"from","signatures":[["arrayLike","?mapfn","?thisArg"]],"static":true,"receiver":"Float32Array"},{"name":"from","signatures":[["arrayLike","?mapfn","?thisArg"]],"static":true,"receiver":"Float64Array"},{"name":"of","signatures":[["...items"]]},{"name":"clz32","signatures":[["x"]]},{"name":"imul","signatures":[["x","y"]]},{"name":"sign","signatures":[["x"]],"receiver":"Math"},{"name":"sign","signatures":[["algorithm","key","data"]],"receiver":"SubtleCrypto"},{"name":"log10","signatures":[["x"]]},{"name":"log2","signatures":[["x"]]},{"name":"log1p","signatures":[["x"]]},{"name":"expm1","signatures":[["x"]]},{"name":"cosh","signatures":[["x"]]},{"name":"sinh","signatures":[["x"]]},{"name":"tanh","signatures":[["x"]]},{"name":"acosh","signatures":[["x"]]},{"name":"asinh","signatures":[["x"]]},{"name":"atanh","signatures":[["x"]]},{"name":"hypot","signatures":[["...values"]]},{"name":"trunc","signatures":[["x"]]},{"name":"fround","signatures":[["x"]]},{"name":"cbrt","signatures":[["x"]]},{"name":"isFinite","signatures":[["number"]]},{"name":"isInteger","signatures":[["number"]]},{"name":"isNaN","signatures":[["number"]]},{"name":"isSafeInteger","signatures":[["number"]]},{"name":"parseFloat","signatures":[["string"]]},{"name":"parseInt","signatures":[["string","?radix"]],"static":true,"receiver":"Number"},{"name":"parseInt","signatures":[["s","?radix"]],"receiver":"Window"},{"name":"hasOwnProperty","signatures":[["v"]]},{"name":"propertyIsEnumerable","signatures":[["v"]]},{"name":"assign","signatures":[["target","source"],["target","...sources"],["target","source1","source2","?source3"]],"static":true,"receiver":"Object"},{"name":"assign","signatures":[["url"]],"receiver":"Location"},{"name":"assign","signatures":[["nodes"]],"receiver":"HTMLSlotElement"},{"name":"getOwnPropertySymbols","signatures":[["o"]]},{"name":"is","signatures":[["value1","value2"]]},{"name":"setPrototypeOf","signatures":[["o","proto"]],"static":true,"receiver":"Object"},{"name":"setPrototypeOf","signatures":[["target","v"]],"receiver":"ProxyHandler"},{"name":"getOwnPropertyDescriptor","signatures":[["o","propertyKey"],["o","p"]],"static":true,"receiver":"Object"},{"name":"getOwnPropertyDescriptor","signatures":[["target","p"]],"receiver":"ProxyHandler"},{"name":"defineProperty","signatures":[["o","propertyKey","attributes"],["o","p","attributes"]],"static":true,"receiver":"Object"},{"name":"defineProperty","signatures":[["target","p","attributes"]],"receiver":"ProxyHandler"},{"name":"codePointAt","signatures":[["pos"]]},{"name":"includes","signatures":[["searchString","?position"]],"receiver":"String"},{"name":"includes","signatures":[["searchElement","?fromIndex"]],"receiver":"Array"},{"name":"includes","signatures":[["searchElement","?fromIndex"]],"receiver":"ReadonlyArray"},{"name":"includes","signatures":[["searchElement","?fromIndex"]],"receiver":"Int8Array"},{"name":"includes","signatures":[["searchElement","?fromIndex"]],"receiver":"Uint8Array"},{"name":"includes","signatures":[["searchElement","?fromIndex"]],"receiver":"Uint8ClampedArray"},{"name":"includes","signatures":[["searchElement","?fromIndex"]],"receiver":"Int16Array"},{"name":"includes","signatures":[["searchElement","?fromIndex"]],"receiver":"Uint16Array"},{"name":"includes","signatures":[["searchElement","?fromIndex"]],"receiver":"Int32Array"},{"name":"includes","signatures":[["searchElement","?fromIndex"]],"receiver":"Uint32Array"},{"name":"includes","signatures":[["searchElement","?fromIndex"]],"receiver":"Float32Array"},{"name":"includes","signatures":[["searchElement","?fromIndex"]],"receiver":"Float64Array"},{"name":"includes","signatures":[["key"]],"receiver":"IDBKeyRange"},{"name":"endsWith","signatures":[["searchString","?endPosition"]]},{"name":"normalize","signatures":[["?form"]],"receiver":"String"},{"name":"repeat","signatures":[["count"]]},{"name":"startsWith","signatures":[["searchString","?position"]]},{"name":"anchor","signatures":[["name"]]},{"name":"fontcolor","signatures":[["color"]]},{"name":"fontsize","signatures":[["size"]]},{"name":"link","signatures":[["url"]]},{"name":"sub","signatures":[["typedArray","index","value"]],"receiver":"Atomics"},{"name":"sub","signatures":[["...values"]],"receiver":"CSSNumericValue"},{"name":"fromCodePoint","signatures":[["...codePoints"]]},{"name":"raw","signatures":[["template","...substitutions"]]},{"name":"clear","signatures":[["mask"]],"receiver":"WebGLRenderingContextBase"},{"name":"delete","signatures":[["key"]],"receiver":"Map"},{"name":"delete","signatures":[["key"]],"receiver":"WeakMap"},{"name":"delete","signatures":[["value"]],"receiver":"Set"},{"name":"delete","signatures":[["value"]],"receiver":"WeakSet"},{"name":"delete","signatures":[["property"]],"receiver":"StylePropertyMap"},{"name":"delete","signatures":[["key"]],"receiver":"Headers"},{"name":"delete","signatures":[["name"]],"receiver":"FormData"},{"name":"delete","signatures":[["name"]],"receiver":"URLSearchParams"},{"name":"delete","signatures":[["cacheName"]],"receiver":"CacheStorage"},{"name":"delete","signatures":[["request","?options"]],"receiver":"Cache"},{"name":"delete","signatures":[["name"],["options"]],"receiver":"CookieStore"},{"name":"delete","signatures":[["key"]],"receiver":"IDBObjectStore"},{"name":"delete","signatures":[["instrumentKey"]],"receiver":"PaymentInstruments"},{"name":"forEach","signatures":[["callbackfn","?thisArg"]]},{"name":"get","signatures":[["key"]],"receiver":"Map"},{"name":"get","signatures":[["key"]],"receiver":"ReadonlyMap"},{"name":"get","signatures":[["key"]],"receiver":"WeakMap"},{"name":"get","signatures":[["target","p","receiver"]],"receiver":"ProxyHandler"},{"name":"get","signatures":[["property"]],"receiver":"StylePropertyMapReadOnly"},{"name":"get","signatures":[["key"]],"receiver":"Headers"},{"name":"get","signatures":[["name"]],"receiver":"CustomElementRegistry"},{"name":"get","signatures":[["name"]],"receiver":"FormData"},{"name":"get","signatures":[["name"]],"receiver":"URLSearchParams"},{"name":"get","signatures":[["id"]],"receiver":"BackgroundFetchManager"},{"name":"get","signatures":[["name"],["?options"]],"receiver":"CookieStore"},{"name":"get","signatures":[["?options"]],"receiver":"CredentialsContainer"},{"name":"get","signatures":[["keyId"]],"receiver":"MediaKeyStatusMap"},{"name":"get","signatures":[["key"]],"receiver":"IDBIndex"},{"name":"get","signatures":[["key"]],"receiver":"IDBObjectStore"},{"name":"get","signatures":[["instrumentKey"]],"receiver":"PaymentInstruments"},{"name":"get","signatures":[["id"]],"receiver":"Clients"},{"name":"has","signatures":[["key"]],"receiver":"Map"},{"name":"has","signatures":[["key"]],"receiver":"ReadonlyMap"},{"name":"has","signatures":[["key"]],"receiver":"WeakMap"},{"name":"has","signatures":[["value"]],"receiver":"Set"},{"name":"has","signatures":[["value"]],"receiver":"ReadonlySet"},{"name":"has","signatures":[["value"]],"receiver":"WeakSet"},{"name":"has","signatures":[["target","p"]],"receiver":"ProxyHandler"},{"name":"has","signatures":[["property"]],"receiver":"StylePropertyMapReadOnly"},{"name":"has","signatures":[["key"]],"receiver":"Headers"},{"name":"has","signatures":[["name"]],"receiver":"FormData"},{"name":"has","signatures":[["name"]],"receiver":"URLSearchParams"},{"name":"has","signatures":[["cacheName"]],"receiver":"CacheStorage"},{"name":"has","signatures":[["keyId"]],"receiver":"MediaKeyStatusMap"},{"name":"has","signatures":[["instrumentKey"]],"receiver":"PaymentInstruments"},{"name":"set","signatures":[["key","value"]],"receiver":"Map"},{"name":"set","signatures":[["key","value"]],"receiver":"WeakMap"},{"name":"set","signatures":[["target","p","value","receiver"]],"receiver":"ProxyHandler"},{"name":"set","signatures":[["v"]],"receiver":"PropertyDescriptor"},{"name":"set","signatures":[["array","?offset"]],"receiver":"Int8Array"},{"name":"set","signatures":[["array","?offset"]],"receiver":"Uint8Array"},{"name":"set","signatures":[["array","?offset"]],"receiver":"Uint8ClampedArray"},{"name":"set","signatures":[["array","?offset"]],"receiver":"Int16Array"},{"name":"set","signatures":[["array","?offset"]],"receiver":"Uint16Array"},{"name":"set","signatures":[["array","?offset"]],"receiver":"Int32Array"},{"name":"set","signatures":[["array","?offset"]],"receiver":"Uint32Array"},{"name":"set","signatures":[["array","?offset"]],"receiver":"Float32Array"},{"name":"set","signatures":[["array","?offset"]],"receiver":"Float64Array"},{"name":"set","signatures":[["property","...values"]],"receiver":"StylePropertyMap"},{"name":"set","signatures":[["key","value"]],"receiver":"Headers"},{"name":"set","signatures":[["name","value","?filename"]],"receiver":"FormData"},{"name":"set","signatures":[["name","value"]],"receiver":"URLSearchParams"},{"name":"set","signatures":[["?contents"]],"static":true,"receiver":"ExperimentalBadge"},{"name":"set","signatures":[["options"],["name","value","?options"]],"receiver":"CookieStore"},{"name":"set","signatures":[["instrumentKey","details"]],"receiver":"PaymentInstruments"},{"name":"add","signatures":[["value"]],"receiver":"Set"},{"name":"add","signatures":[["value"]],"receiver":"WeakSet"},{"name":"add","signatures":[["typedArray","index","value"]],"receiver":"Atomics"},{"name":"add","signatures":[["node","?before"]],"receiver":"AccessibleNodeList"},{"name":"add","signatures":[["file"],["data","type"]],"receiver":"DataTransferItemList"},{"name":"add","signatures":[["...values"]],"receiver":"CSSNumericValue"},{"name":"add","signatures":[["...tokens"]],"receiver":"DOMTokenList"},{"name":"add","signatures":[["element","?before"]],"receiver":"HTMLOptionsCollection"},{"name":"add","signatures":[["element","?before"]],"receiver":"HTMLSelectElement"},{"name":"add","signatures":[["request"]],"receiver":"Cache"},{"name":"add","signatures":[["value","?key"]],"receiver":"IDBObjectStore"},{"name":"all","signatures":[["values"]]},{"name":"race","signatures":[["values"]]},{"name":"reject","signatures":[["reason"]]},{"name":"resolve","signatures":[["?value"]]},{"name":"toString","signatures":[["?radix"]],"receiver":"Number"},{"name":"for","signatures":[["key"]]},{"name":"keyFor","signatures":[["sym"]]},{"name":"next","signatures":[["?value"]],"receiver":"Iterator"},{"name":"next","signatures":[["?value"]],"receiver":"AsyncIterator"},{"name":"return","signatures":[["?value"]],"receiver":"Iterator"},{"name":"return","signatures":[["?value"]],"receiver":"AsyncIterator"},{"name":"throw","signatures":[["?e"]]},{"name":"entries","signatures":[["o"]],"static":true,"receiver":"Object"},{"name":"keys","signatures":[["o"]],"static":true,"receiver":"Object"},{"name":"keys","signatures":[["?request","?options"]],"receiver":"Cache"},{"name":"values","signatures":[["o"]],"static":true,"receiver":"Object"},{"name":"getPrototypeOf","signatures":[["target"]],"receiver":"ProxyHandler"},{"name":"getPrototypeOf","signatures":[["o"]],"static":true,"receiver":"Object"},{"name":"isExtensible","signatures":[["target"]],"receiver":"ProxyHandler"},{"name":"isExtensible","signatures":[["o"]],"static":true,"receiver":"Object"},{"name":"preventExtensions","signatures":[["target"]],"receiver":"ProxyHandler"},{"name":"preventExtensions","signatures":[["o"]],"static":true,"receiver":"Object"},{"name":"deleteProperty","signatures":[["target","p"]]},{"name":"enumerate","signatures":[["target"]]},{"name":"ownKeys","signatures":[["target"]]},{"name":"apply","signatures":[["target","thisArg","?argArray"]],"receiver":"ProxyHandler"},{"name":"apply","signatures":[["thisArg","?argArray"]],"receiver":"Function"},{"name":"construct","signatures":[["target","argArray","?newTarget"]]},{"name":"revocable","signatures":[["target","handler"]]},{"name":"match","signatures":[["matcher"],["regexp"]],"receiver":"String"},{"name":"match","signatures":[["request","?options"]],"receiver":"BackgroundFetchRegistration"},{"name":"match","signatures":[["request","?options"]],"receiver":"CacheStorage"},{"name":"match","signatures":[["request","?options"]],"receiver":"Cache"},{"name":"replace","signatures":[["searchValue","replaceValue"],["searchValue","replacer"]],"receiver":"String"},{"name":"replace","signatures":[["text"]],"receiver":"CSSStyleSheet"},{"name":"replace","signatures":[["token","newToken"]],"receiver":"DOMTokenList"},{"name":"replace","signatures":[["url"]],"receiver":"Location"},{"name":"search","signatures":[["searcher"],["regexp"]]},{"name":"split","signatures":[["splitter","?limit"],["separator","?limit"]]},{"name":"eval","signatures":[["x"]]},{"name":"decodeURI","signatures":[["encodedURI"]]},{"name":"decodeURIComponent","signatures":[["encodedURIComponent"]]},{"name":"encodeURI","signatures":[["uri"]]},{"name":"encodeURIComponent","signatures":[["uriComponent"]]},{"name":"escape","signatures":[["string"]],"receiver":"Window"},{"name":"escape","signatures":[["ident"]],"static":true,"receiver":"CSS"},{"name":"unescape","signatures":[["string"]]},{"name":"toLocaleString","signatures":[["?locales","?options"]],"receiver":"Date"},{"name":"toLocaleString","signatures":[["?locales","?options"]],"receiver":"Number"},{"name":"isPrototypeOf","signatures":[["v"]]},{"name":"getOwnPropertyNames","signatures":[["o"]]},{"name":"create","signatures":[["o","?properties"]],"static":true,"receiver":"Object"},{"name":"create","signatures":[["?options"]],"receiver":"CredentialsContainer"},{"name":"defineProperties","signatures":[["o","properties"]]},{"name":"seal","signatures":[["o"]]},{"name":"freeze","signatures":[["a"],["f"],["o"]]},{"name":"isSealed","signatures":[["o"]]},{"name":"isFrozen","signatures":[["o"]]},{"name":"call","signatures":[["thisArg","...argArray"]]},{"name":"bind","signatures":[["thisArg","...argArray"]]},{"name":"charAt","signatures":[["pos"]]},{"name":"charCodeAt","signatures":[["index"]]},{"name":"concat","signatures":[["...strings"]],"receiver":"String"},{"name":"concat","signatures":[["...items"]],"receiver":"ReadonlyArray"},{"name":"concat","signatures":[["...items"]],"receiver":"Array"},{"name":"indexOf","signatures":[["searchString","?position"]],"receiver":"String"},{"name":"indexOf","signatures":[["searchElement","?fromIndex"]],"receiver":"ReadonlyArray"},{"name":"indexOf","signatures":[["searchElement","?fromIndex"]],"receiver":"Array"},{"name":"indexOf","signatures":[["searchElement","?fromIndex"]],"receiver":"Int8Array"},{"name":"indexOf","signatures":[["searchElement","?fromIndex"]],"receiver":"Uint8Array"},{"name":"indexOf","signatures":[["searchElement","?fromIndex"]],"receiver":"Uint8ClampedArray"},{"name":"indexOf","signatures":[["searchElement","?fromIndex"]],"receiver":"Int16Array"},{"name":"indexOf","signatures":[["searchElement","?fromIndex"]],"receiver":"Uint16Array"},{"name":"indexOf","signatures":[["searchElement","?fromIndex"]],"receiver":"Int32Array"},{"name":"indexOf","signatures":[["searchElement","?fromIndex"]],"receiver":"Uint32Array"},{"name":"indexOf","signatures":[["searchElement","?fromIndex"]],"receiver":"Float32Array"},{"name":"indexOf","signatures":[["searchElement","?fromIndex"]],"receiver":"Float64Array"},{"name":"lastIndexOf","signatures":[["searchString","?position"]],"receiver":"String"},{"name":"lastIndexOf","signatures":[["searchElement","?fromIndex"]],"receiver":"ReadonlyArray"},{"name":"lastIndexOf","signatures":[["searchElement","?fromIndex"]],"receiver":"Array"},{"name":"lastIndexOf","signatures":[["searchElement","?fromIndex"]],"receiver":"Int8Array"},{"name":"lastIndexOf","signatures":[["searchElement","?fromIndex"]],"receiver":"Uint8Array"},{"name":"lastIndexOf","signatures":[["searchElement","?fromIndex"]],"receiver":"Uint8ClampedArray"},{"name":"lastIndexOf","signatures":[["searchElement","?fromIndex"]],"receiver":"Int16Array"},{"name":"lastIndexOf","signatures":[["searchElement","?fromIndex"]],"receiver":"Uint16Array"},{"name":"lastIndexOf","signatures":[["searchElement","?fromIndex"]],"receiver":"Int32Array"},{"name":"lastIndexOf","signatures":[["searchElement","?fromIndex"]],"receiver":"Uint32Array"},{"name":"lastIndexOf","signatures":[["searchElement","?fromIndex"]],"receiver":"Float32Array"},{"name":"lastIndexOf","signatures":[["searchElement","?fromIndex"]],"receiver":"Float64Array"},{"name":"localeCompare","signatures":[["that","?locales","?options"]]},{"name":"slice","signatures":[["?start","?end"]],"receiver":"String"},{"name":"slice","signatures":[["?start","?end"]],"receiver":"ReadonlyArray"},{"name":"slice","signatures":[["?start","?end"]],"receiver":"ConcatArray"},{"name":"slice","signatures":[["?start","?end"]],"receiver":"Array"},{"name":"slice","signatures":[["begin","?end"]],"receiver":"ArrayBuffer"},{"name":"slice","signatures":[["?start","?end"]],"receiver":"Int8Array"},{"name":"slice","signatures":[["?start","?end"]],"receiver":"Uint8Array"},{"name":"slice","signatures":[["?start","?end"]],"receiver":"Uint8ClampedArray"},{"name":"slice","signatures":[["?start","?end"]],"receiver":"Int16Array"},{"name":"slice","signatures":[["?start","?end"]],"receiver":"Uint16Array"},{"name":"slice","signatures":[["?start","?end"]],"receiver":"Int32Array"},{"name":"slice","signatures":[["?start","?end"]],"receiver":"Uint32Array"},{"name":"slice","signatures":[["?start","?end"]],"receiver":"Float32Array"},{"name":"slice","signatures":[["?start","?end"]],"receiver":"Float64Array"},{"name":"slice","signatures":[["begin","?end"]],"receiver":"SharedArrayBuffer"},{"name":"slice","signatures":[["?start","?end","?contentType"]],"receiver":"Blob"},{"name":"substring","signatures":[["start","?end"]]},{"name":"substr","signatures":[["from","?length"]]},{"name":"fromCharCode","signatures":[["...codes"]]},{"name":"toFixed","signatures":[["?fractionDigits"]]},{"name":"toExponential","signatures":[["?fractionDigits"]]},{"name":"toPrecision","signatures":[["?precision"]]},{"name":"abs","signatures":[["x"]]},{"name":"acos","signatures":[["x"]]},{"name":"asin","signatures":[["x"]]},{"name":"atan","signatures":[["x"]]},{"name":"atan2","signatures":[["y","x"]]},{"name":"ceil","signatures":[["x"]]},{"name":"cos","signatures":[["x"]]},{"name":"exp","signatures":[["x"]]},{"name":"floor","signatures":[["x"]]},{"name":"log","signatures":[["x"]],"receiver":"Math"},{"name":"log","signatures":[["...data"]],"receiver":"console"},{"name":"max","signatures":[["...values"]]},{"name":"min","signatures":[["...values"]]},{"name":"pow","signatures":[["x","y"]]},{"name":"round","signatures":[["x"]]},{"name":"sin","signatures":[["x"]]},{"name":"sqrt","signatures":[["x"]]},{"name":"tan","signatures":[["x"]]},{"name":"toLocaleDateString","signatures":[["?locales","?options"]]},{"name":"toLocaleTimeString","signatures":[["?locales","?options"]]},{"name":"setTime","signatures":[["time"]]},{"name":"setMilliseconds","signatures":[["ms"]]},{"name":"setUTCMilliseconds","signatures":[["ms"]]},{"name":"setSeconds","signatures":[["sec","?ms"]]},{"name":"setUTCSeconds","signatures":[["sec","?ms"]]},{"name":"setMinutes","signatures":[["min","?sec","?ms"]]},{"name":"setUTCMinutes","signatures":[["min","?sec","?ms"]]},{"name":"setHours","signatures":[["hours","?min","?sec","?ms"]]},{"name":"setUTCHours","signatures":[["hours","?min","?sec","?ms"]]},{"name":"setDate","signatures":[["date"]]},{"name":"setUTCDate","signatures":[["date"]]},{"name":"setMonth","signatures":[["month","?date"]]},{"name":"setUTCMonth","signatures":[["month","?date"]]},{"name":"setFullYear","signatures":[["year","?month","?date"]]},{"name":"setUTCFullYear","signatures":[["year","?month","?date"]]},{"name":"toJSON","signatures":[["?key"]],"receiver":"Date"},{"name":"parse","signatures":[["s"]],"static":true,"receiver":"Date"},{"name":"parse","signatures":[["text","?reviver"]],"receiver":"JSON"},{"name":"parse","signatures":[["cssText"]],"static":true,"receiver":"CSSNumericValue"},{"name":"UTC","signatures":[["year","month","?date","?hours","?minutes","?seconds","?ms"]]},{"name":"exec","signatures":[["string"]]},{"name":"test","signatures":[["string"]]},{"name":"stringify","signatures":[["value","?replacer","?space"]]},{"name":"join","signatures":[["?separator"]]},{"name":"every","signatures":[["callbackfn","?thisArg"]]},{"name":"some","signatures":[["callbackfn","?thisArg"]]},{"name":"map","signatures":[["callbackfn","?thisArg"]]},{"name":"filter","signatures":[["callbackfn","?thisArg"]]},{"name":"reduce","signatures":[["callbackfn","?initialValue"]]},{"name":"reduceRight","signatures":[["callbackfn","?initialValue"]]},{"name":"push","signatures":[["...items"]],"receiver":"Array"},{"name":"push","signatures":[["message","?options"]],"receiver":"NFC"},{"name":"sort","signatures":[["?compareFn"]],"receiver":"Array"},{"name":"sort","signatures":[["?compareFn"]],"receiver":"Int8Array"},{"name":"sort","signatures":[["?compareFn"]],"receiver":"Uint8Array"},{"name":"sort","signatures":[["?compareFn"]],"receiver":"Uint8ClampedArray"},{"name":"sort","signatures":[["?compareFn"]],"receiver":"Int16Array"},{"name":"sort","signatures":[["?compareFn"]],"receiver":"Uint16Array"},{"name":"sort","signatures":[["?compareFn"]],"receiver":"Int32Array"},{"name":"sort","signatures":[["?compareFn"]],"receiver":"Uint32Array"},{"name":"sort","signatures":[["?compareFn"]],"receiver":"Float32Array"},{"name":"sort","signatures":[["?compareFn"]],"receiver":"Float64Array"},{"name":"splice","signatures":[["start","?deleteCount","...items"]]},{"name":"unshift","signatures":[["...items"]]},{"name":"isArray","signatures":[["arg"]]},{"name":"then","signatures":[["?onfulfilled","?onrejected"]]},{"name":"catch","signatures":[["?onrejected"]]},{"name":"isView","signatures":[["arg"]]},{"name":"getFloat32","signatures":[["byteOffset","?littleEndian"]]},{"name":"getFloat64","signatures":[["byteOffset","?littleEndian"]]},{"name":"getInt8","signatures":[["byteOffset"]]},{"name":"getInt16","signatures":[["byteOffset","?littleEndian"]]},{"name":"getInt32","signatures":[["byteOffset","?littleEndian"]]},{"name":"getUint8","signatures":[["byteOffset"]]},{"name":"getUint16","signatures":[["byteOffset","?littleEndian"]]},{"name":"getUint32","signatures":[["byteOffset","?littleEndian"]]},{"name":"setFloat32","signatures":[["byteOffset","value","?littleEndian"]]},{"name":"setFloat64","signatures":[["byteOffset","value","?littleEndian"]]},{"name":"setInt8","signatures":[["byteOffset","value"]]},{"name":"setInt16","signatures":[["byteOffset","value","?littleEndian"]]},{"name":"setInt32","signatures":[["byteOffset","value","?littleEndian"]]},{"name":"setUint8","signatures":[["byteOffset","value"]]},{"name":"setUint16","signatures":[["byteOffset","value","?littleEndian"]]},{"name":"setUint32","signatures":[["byteOffset","value","?littleEndian"]]},{"name":"subarray","signatures":[["begin","?end"]]},{"name":"getOwnPropertyDescriptors","signatures":[["o"]]},{"name":"and","signatures":[["typedArray","index","value"]]},{"name":"compareExchange","signatures":[["typedArray","index","expectedValue","replacementValue"]]},{"name":"exchange","signatures":[["typedArray","index","value"]]},{"name":"isLockFree","signatures":[["size"]]},{"name":"load","signatures":[["typedArray","index"]],"receiver":"Atomics"},{"name":"load","signatures":[["font","?text"]],"receiver":"FontFaceSet"},{"name":"load","signatures":[["sessionId"]],"receiver":"MediaKeySession"},{"name":"or","signatures":[["typedArray","index","value"]]},{"name":"store","signatures":[["typedArray","index","value"]],"receiver":"Atomics"},{"name":"store","signatures":[["credential"]],"receiver":"CredentialsContainer"},{"name":"wait","signatures":[["typedArray","index","value","?timeout"]]},{"name":"wake","signatures":[["typedArray","index","count"]]},{"name":"xor","signatures":[["typedArray","index","value"]]},{"name":"padStart","signatures":[["maxLength","?fillString"]]},{"name":"padEnd","signatures":[["maxLength","?fillString"]]},{"name":"formatToParts","signatures":[["?date"]]},{"name":"finally","signatures":[["?onfinally"]]},{"name":"flatMap","signatures":[["callback","?thisArg"]]},{"name":"flatten","signatures":[["?depth"]]},{"name":"updateTiming","signatures":[["?timing"]]},{"name":"Animation","signatures":[["?effect","?timeline"]]},{"name":"cancel","signatures":[["?reason"]],"receiver":"ReadableStream"},{"name":"cancel","signatures":[["?reason"]],"receiver":"UnderlyingSourceBase"},{"name":"animate","signatures":[["keyframes","?options"]]},{"name":"setKeyframes","signatures":[["keyframes"]]},{"name":"item","signatures":[["index"]],"receiver":"AccessibleNodeList"},{"name":"item","signatures":[["index"]],"receiver":"CSSRuleList"},{"name":"item","signatures":[["index"]],"receiver":"CSSStyleDeclaration"},{"name":"item","signatures":[["index"]],"receiver":"MediaList"},{"name":"item","signatures":[["index"]],"receiver":"StyleSheetList"},{"name":"item","signatures":[["index"]],"receiver":"DOMStringList"},{"name":"item","signatures":[["index"]],"receiver":"DOMTokenList"},{"name":"item","signatures":[["index"]],"receiver":"NamedNodeMap"},{"name":"item","signatures":[["index"]],"receiver":"NodeList"},{"name":"item","signatures":[["index"]],"receiver":"FileList"},{"name":"item","signatures":[["index"]],"receiver":"DOMRectList"},{"name":"item","signatures":[["index"]],"receiver":"HTMLSelectElement"},{"name":"item","signatures":[["?nameOrIndex"]],"receiver":"HTMLAllCollection"},{"name":"item","signatures":[["index"]],"receiver":"HTMLCollection"},{"name":"item","signatures":[["index"]],"receiver":"TouchList"},{"name":"item","signatures":[["?index"]],"receiver":"GamepadList"},{"name":"item","signatures":[["index"]],"receiver":"MimeTypeArray"},{"name":"item","signatures":[["index"]],"receiver":"PluginArray"},{"name":"item","signatures":[["index"]],"receiver":"Plugin"},{"name":"item","signatures":[["index"]],"receiver":"SpeechGrammarList"},{"name":"item","signatures":[["index"]],"receiver":"SpeechRecognitionResultList"},{"name":"item","signatures":[["index"]],"receiver":"SpeechRecognitionResult"},{"name":"item","signatures":[["index"]],"receiver":"SQLResultSetRowList"},{"name":"null","signatures":[["index","node"]],"receiver":"AccessibleNodeList"},{"name":"null","signatures":[["index"]],"receiver":"DataTransferItemList"},{"name":"null","signatures":[["index"]],"receiver":"CSSKeyframesRule"},{"name":"null","signatures":[["name"],["property","propertyValue"]],"receiver":"CSSStyleDeclaration"},{"name":"null","signatures":[["index"]],"receiver":"CSSNumericArray"},{"name":"null","signatures":[["index","?val"]],"receiver":"CSSTransformValue"},{"name":"null","signatures":[["index","?val"]],"receiver":"CSSUnparsedValue"},{"name":"null","signatures":[["name"]],"receiver":"StyleSheetList"},{"name":"null","signatures":[["name","?value"]],"receiver":"DOMStringMap"},{"name":"null","signatures":[["index"],["name"]],"receiver":"Window"},{"name":"null","signatures":[["index"]],"receiver":"HTMLFormControlsCollection"},{"name":"null","signatures":[["index"],["name"]],"receiver":"HTMLFormElement"},{"name":"null","signatures":[["index","?option"],["name"]],"receiver":"HTMLOptionsCollection"},{"name":"null","signatures":[["index","option"]],"receiver":"HTMLSelectElement"},{"name":"null","signatures":[["index"]],"receiver":"RadioNodeList"},{"name":"null","signatures":[["index"]],"receiver":"HTMLAllCollection"},{"name":"null","signatures":[["name","?value"]],"receiver":"HTMLEmbedElement"},{"name":"null","signatures":[["name","?value"]],"receiver":"HTMLObjectElement"},{"name":"null","signatures":[["index"]],"receiver":"AudioTrackList"},{"name":"null","signatures":[["index"]],"receiver":"TextTrackCueList"},{"name":"null","signatures":[["index"]],"receiver":"TextTrackList"},{"name":"null","signatures":[["index"]],"receiver":"VideoTrackList"},{"name":"null","signatures":[["index","newItem"]],"receiver":"SVGLengthList"},{"name":"null","signatures":[["index","newItem"]],"receiver":"SVGNumberList"},{"name":"null","signatures":[["index","newItem"]],"receiver":"SVGPointList"},{"name":"null","signatures":[["index","newItem"]],"receiver":"SVGStringList"},{"name":"null","signatures":[["index","newItem"]],"receiver":"SVGTransformList"},{"name":"null","signatures":[["index"]],"receiver":"SourceBufferList"},{"name":"null","signatures":[["index"]],"receiver":"TrackDefaultList"},{"name":"null","signatures":[["?name"]],"receiver":"RTCStatsResponse"},{"name":"remove","signatures":[["index"]],"receiver":"AccessibleNodeList"},{"name":"remove","signatures":[["index"]],"receiver":"DataTransferItemList"},{"name":"remove","signatures":[["...tokens"]],"receiver":"DOMTokenList"},{"name":"remove","signatures":[["index"]],"receiver":"HTMLOptionsCollection"},{"name":"remove","signatures":[["?index"]],"receiver":"HTMLSelectElement"},{"name":"remove","signatures":[["successCallback","?errorCallback"]],"receiver":"Entry"},{"name":"remove","signatures":[["start","end"]],"receiver":"SourceBuffer"},{"name":"appendChild","signatures":[["child"]],"receiver":"AccessibleNode"},{"name":"appendChild","signatures":[["node"]],"receiver":"Node"},{"name":"removeChild","signatures":[["child"]]},{"name":"getAsString","signatures":[["callback"]]},{"name":"setDragImage","signatures":[["image","x","y"]]},{"name":"getData","signatures":[["format"]]},{"name":"setData","signatures":[["format","data"]]},{"name":"clearData","signatures":[["?format"]]},{"name":"insertRule","signatures":[["rule","index"]],"receiver":"CSSGroupingRule"},{"name":"insertRule","signatures":[["rule","?index"]],"receiver":"CSSStyleSheet"},{"name":"deleteRule","signatures":[["index"]],"receiver":"CSSGroupingRule"},{"name":"deleteRule","signatures":[["select"]],"receiver":"CSSKeyframesRule"},{"name":"deleteRule","signatures":[["index"]],"receiver":"CSSStyleSheet"},{"name":"appendRule","signatures":[["rule"]]},{"name":"findRule","signatures":[["select"]]},{"name":"getPropertyValue","signatures":[["property"]]},{"name":"getPropertyPriority","signatures":[["property"]]},{"name":"setProperty","signatures":[["property","value","?priority"]]},{"name":"removeProperty","signatures":[["property"]]},{"name":"CSSStyleSheet","signatures":[["?options"]]},{"name":"replaceSync","signatures":[["text"]]},{"name":"addRule","signatures":[["?selector","?style","?index"]]},{"name":"removeRule","signatures":[["?index"]]},{"name":"supports","signatures":[["conditionText"],["property","value"]],"static":true,"receiver":"CSS"},{"name":"supports","signatures":[["token"]],"receiver":"DOMTokenList"},{"name":"CSSKeywordValue","signatures":[["keyword"]]},{"name":"CSSMathInvert","signatures":[["arg"]]},{"name":"CSSMathMax","signatures":[["...args"]]},{"name":"CSSMathMin","signatures":[["...args"]]},{"name":"CSSMathNegate","signatures":[["arg"]]},{"name":"CSSMathProduct","signatures":[["...args"]]},{"name":"CSSMathSum","signatures":[["...args"]]},{"name":"CSSMatrixComponent","signatures":[["matrix","?options"]]},{"name":"mul","signatures":[["...values"]]},{"name":"div","signatures":[["...values"]]},{"name":"equals","signatures":[["...values"]]},{"name":"to","signatures":[["unit"]]},{"name":"toSum","signatures":[["...units"]]},{"name":"CSSPerspective","signatures":[["length"]]},{"name":"CSSPositionValue","signatures":[["x","y"]]},{"name":"CSSRotate","signatures":[["angleValue"]]},{"name":"CSSScale","signatures":[["x","y","?z"]]},{"name":"CSSSkewX","signatures":[["ax"]]},{"name":"CSSSkewY","signatures":[["ay"]]},{"name":"CSSSkew","signatures":[["ax","ay"]]},{"name":"CSSTransformValue","signatures":[["transforms"]]},{"name":"CSSTranslate","signatures":[["x","y","?z"]]},{"name":"CSSUnitValue","signatures":[["value","unit"]]},{"name":"number","signatures":[["value"]]},{"name":"percent","signatures":[["value"]]},{"name":"em","signatures":[["value"]]},{"name":"ex","signatures":[["value"]]},{"name":"ch","signatures":[["value"]]},{"name":"rem","signatures":[["value"]]},{"name":"vw","signatures":[["value"]]},{"name":"vh","signatures":[["value"]]},{"name":"vmin","signatures":[["value"]]},{"name":"vmax","signatures":[["value"]]},{"name":"cm","signatures":[["value"]]},{"name":"mm","signatures":[["value"]]},{"name":"in","signatures":[["value"]]},{"name":"pt","signatures":[["value"]]},{"name":"pc","signatures":[["value"]]},{"name":"px","signatures":[["value"]]},{"name":"Q","signatures":[["value"]]},{"name":"deg","signatures":[["value"]]},{"name":"grad","signatures":[["value"]]},{"name":"rad","signatures":[["value"]]},{"name":"turn","signatures":[["value"]]},{"name":"s","signatures":[["value"]]},{"name":"ms","signatures":[["value"]]},{"name":"Hz","signatures":[["value"]]},{"name":"kHz","signatures":[["value"]]},{"name":"dpi","signatures":[["value"]]},{"name":"dpcm","signatures":[["value"]]},{"name":"dppx","signatures":[["value"]]},{"name":"fr","signatures":[["value"]]},{"name":"CSSUnparsedValue","signatures":[["members"]]},{"name":"CSSVariableReferenceValue","signatures":[["variable","?fallback"]]},{"name":"getAll","signatures":[["property"]],"receiver":"StylePropertyMapReadOnly"},{"name":"getAll","signatures":[["name"]],"receiver":"FormData"},{"name":"getAll","signatures":[["name"]],"receiver":"URLSearchParams"},{"name":"getAll","signatures":[["name"],["?options"]],"receiver":"CookieStore"},{"name":"getAll","signatures":[["?query","?count"]],"receiver":"IDBIndex"},{"name":"getAll","signatures":[["?query","?count"]],"receiver":"IDBObjectStore"},{"name":"append","signatures":[["property","...values"]],"receiver":"StylePropertyMap"},{"name":"append","signatures":[["...nodes"]],"receiver":"ParentNode"},{"name":"append","signatures":[["name","value"]],"receiver":"Headers"},{"name":"append","signatures":[["name","value","?filename"]],"receiver":"FormData"},{"name":"append","signatures":[["name","value"]],"receiver":"URLSearchParams"},{"name":"FontFaceSetLoadEvent","signatures":[["type","?eventInitDict"]]},{"name":"check","signatures":[["font","?text"]]},{"name":"FontFace","signatures":[["family","source","?descriptors"]]},{"name":"appendMedium","signatures":[["medium"]]},{"name":"deleteMedium","signatures":[["medium"]]},{"name":"MediaQueryListEvent","signatures":[["type","?eventInitDict"]]},{"name":"addListener","signatures":[["listener"]]},{"name":"removeListener","signatures":[["listener"]]},{"name":"registerProperty","signatures":[["descriptor"]]},{"name":"matchMedium","signatures":[["?mediaquery"]]},{"name":"acquire","signatures":[["?options"]]},{"name":"update","signatures":[["response"]],"receiver":"MediaKeySession"},{"name":"update","signatures":[["value"]],"receiver":"IDBCursor"},{"name":"abort","signatures":[["?reason"]],"receiver":"WritableStream"},{"name":"substringData","signatures":[["offset","count"]]},{"name":"appendData","signatures":[["data"]]},{"name":"insertData","signatures":[["offset","data"]]},{"name":"deleteData","signatures":[["offset","count"]]},{"name":"replaceData","signatures":[["offset","count","data"]]},{"name":"before","signatures":[["...nodes"]]},{"name":"after","signatures":[["...nodes"]]},{"name":"replaceWith","signatures":[["...nodes"]]},{"name":"elementFromPoint","signatures":[["x","y"]]},{"name":"elementsFromPoint","signatures":[["x","y"]]},{"name":"getElementsByTagName","signatures":[["localName"]]},{"name":"getElementsByTagNameNS","signatures":[["namespaceURI","localName"]]},{"name":"getElementsByClassName","signatures":[["classNames"]]},{"name":"createElement","signatures":[["localName","?options"]]},{"name":"createElementNS","signatures":[["namespaceURI","qualifiedName","?options"]]},{"name":"createTextNode","signatures":[["data"]]},{"name":"createCDATASection","signatures":[["data"]]},{"name":"createComment","signatures":[["data"]]},{"name":"createProcessingInstruction","signatures":[["target","data"]]},{"name":"importNode","signatures":[["node","?deep"]]},{"name":"adoptNode","signatures":[["node"]]},{"name":"createAttribute","signatures":[["localName"]]},{"name":"createAttributeNS","signatures":[["namespaceURI","qualifiedName"]]},{"name":"createEvent","signatures":[["eventType"]]},{"name":"createNodeIterator","signatures":[["root","?whatToShow","?filter"]]},{"name":"createTreeWalker","signatures":[["root","?whatToShow","?filter"]]},{"name":"getElementsByName","signatures":[["elementName"]]},{"name":"open","signatures":[["?type","?replace"],["url","name","features"]],"receiver":"Document"},{"name":"open","signatures":[["?url","?target","?features"]],"receiver":"Window"},{"name":"open","signatures":[["method","url","?async","?username","?password"]],"receiver":"XMLHttpRequest"},{"name":"open","signatures":[["cacheName"]],"receiver":"CacheStorage"},{"name":"open","signatures":[["name","?version"]],"receiver":"IDBFactory"},{"name":"open","signatures":[["options"]],"receiver":"SerialPort"},{"name":"close","signatures":[["?returnValue"]],"receiver":"HTMLDialogElement"},{"name":"close","signatures":[["?code","?reason"]],"receiver":"WebSocket"},{"name":"write","signatures":[["...text"],["text"]],"receiver":"Document"},{"name":"write","signatures":[["data"]],"receiver":"Clipboard"},{"name":"write","signatures":[["position","data"]],"receiver":"FileSystemWriter"},{"name":"write","signatures":[["data"]],"receiver":"FileWriterSync"},{"name":"write","signatures":[["data"]],"receiver":"FileWriter"},{"name":"write","signatures":[["data"]],"receiver":"RTCQuicStream"},{"name":"writeln","signatures":[["...text"],["text"]]},{"name":"execCommand","signatures":[["commandId","?showUI","?value"]]},{"name":"queryCommandEnabled","signatures":[["commandId"]]},{"name":"queryCommandIndeterm","signatures":[["commandId"]]},{"name":"queryCommandState","signatures":[["commandId"]]},{"name":"queryCommandSupported","signatures":[["commandId"]]},{"name":"queryCommandValue","signatures":[["commandId"]]},{"name":"registerElement","signatures":[["type","?options"]]},{"name":"caretRangeFromPoint","signatures":[["?x","?y"]]},{"name":"DOMException","signatures":[["?message","?name"]]},{"name":"createDocumentType","signatures":[["qualifiedName","publicId","systemId"]]},{"name":"createDocument","signatures":[["namespaceURI","qualifiedName","?doctype"]]},{"name":"createHTMLDocument","signatures":[["?title"]]},{"name":"contains","signatures":[["string"]],"receiver":"DOMStringList"},{"name":"contains","signatures":[["token"]],"receiver":"DOMTokenList"},{"name":"contains","signatures":[["other"]],"receiver":"Node"},{"name":"toggle","signatures":[["token","?force"]]},{"name":"setPointerCapture","signatures":[["pointerId"]]},{"name":"releasePointerCapture","signatures":[["pointerId"]]},{"name":"hasPointerCapture","signatures":[["pointerId"]]},{"name":"getAttribute","signatures":[["name"]]},{"name":"getAttributeNS","signatures":[["namespaceURI","localName"]]},{"name":"setAttribute","signatures":[["name","value"]]},{"name":"setAttributeNS","signatures":[["namespaceURI","name","value"]]},{"name":"removeAttribute","signatures":[["name"]]},{"name":"removeAttributeNS","signatures":[["namespaceURI","localName"]]},{"name":"hasAttribute","signatures":[["name"]]},{"name":"hasAttributeNS","signatures":[["namespaceURI","localName"]]},{"name":"toggleAttribute","signatures":[["qualifiedName","?force"]]},{"name":"getAttributeNode","signatures":[["name"]]},{"name":"getAttributeNodeNS","signatures":[["namespaceURI","localName"]]},{"name":"setAttributeNode","signatures":[["attr"]]},{"name":"setAttributeNodeNS","signatures":[["attr"]]},{"name":"removeAttributeNode","signatures":[["attr"]]},{"name":"closest","signatures":[["selectors"]]},{"name":"matches","signatures":[["selectors"]]},{"name":"webkitMatchesSelector","signatures":[["selectors"]]},{"name":"attachShadow","signatures":[["shadowRootInitDict"]]},{"name":"insertAdjacentElement","signatures":[["where","element"]]},{"name":"insertAdjacentText","signatures":[["where","data"]]},{"name":"insertAdjacentHTML","signatures":[["position","text"]]},{"name":"scrollIntoView","signatures":[["?arg"]]},{"name":"scroll","signatures":[["?options"],["x","y"]]},{"name":"scrollTo","signatures":[["?options"],["x","y"]]},{"name":"scrollBy","signatures":[["?options"],["x","y"]]},{"name":"scrollIntoViewIfNeeded","signatures":[["?centerIfNeeded"]]},{"name":"CustomEvent","signatures":[["type","?eventInitDict"]]},{"name":"initCustomEvent","signatures":[["type","?bubbles","?cancelable","?detail"]]},{"name":"handleEvent","signatures":[["event"]],"receiver":"EventListener"},{"name":"handleEvent","signatures":[["entries"]],"receiver":"EntriesCallback"},{"name":"handleEvent","signatures":[["entry"]],"receiver":"EntryCallback"},{"name":"handleEvent","signatures":[["error"]],"receiver":"ErrorCallback"},{"name":"handleEvent","signatures":[["file"]],"receiver":"FileCallback"},{"name":"handleEvent","signatures":[["fileSystem"]],"receiver":"FileSystemCallback"},{"name":"handleEvent","signatures":[["fileWriter"]],"receiver":"FileWriterCallback"},{"name":"handleEvent","signatures":[["metadata"]],"receiver":"MetadataCallback"},{"name":"handleEvent","signatures":[["transaction","resultSet"]],"receiver":"SQLStatementCallback"},{"name":"handleEvent","signatures":[["transaction","error"]],"receiver":"SQLStatementErrorCallback"},{"name":"handleEvent","signatures":[["transaction"]],"receiver":"SQLTransactionCallback"},{"name":"handleEvent","signatures":[["error"]],"receiver":"SQLTransactionErrorCallback"},{"name":"addEventListener","signatures":[["type","listener","?options"]]},{"name":"removeEventListener","signatures":[["type","listener","?options"]]},{"name":"dispatchEvent","signatures":[["event"]]},{"name":"Event","signatures":[["type","?eventInitDict"]]},{"name":"initEvent","signatures":[["type","?bubbles","?cancelable"]]},{"name":"MutationObserver","signatures":[["callback"]]},{"name":"observe","signatures":[["target","?options"]],"receiver":"MutationObserver"},{"name":"observe","signatures":[["target"]],"receiver":"IntersectionObserver"},{"name":"observe","signatures":[["target"]],"receiver":"ResizeObserver"},{"name":"observe","signatures":[["options"]],"receiver":"PerformanceObserver"},{"name":"observe","signatures":[["db","tx","options"]],"receiver":"IDBObserver"},{"name":"disconnect","signatures":[["?output"],["destination","?output","?input"]],"receiver":"AudioNode"},{"name":"getNamedItem","signatures":[["name"]]},{"name":"getNamedItemNS","signatures":[["namespaceURI","localName"]]},{"name":"setNamedItem","signatures":[["attr"]]},{"name":"setNamedItemNS","signatures":[["attr"]]},{"name":"removeNamedItem","signatures":[["name"]]},{"name":"removeNamedItemNS","signatures":[["namespaceURI","localName"]]},{"name":"acceptNode","signatures":[["node"]]},{"name":"setApplyScroll","signatures":[["scrollStateCallback","nativeScrollBehavior"]]},{"name":"setDistributeScroll","signatures":[["scrollStateCallback","nativeScrollBehavior"]]},{"name":"getRootNode","signatures":[["?options"]]},{"name":"cloneNode","signatures":[["?deep"]]},{"name":"isEqualNode","signatures":[["otherNode"]]},{"name":"isSameNode","signatures":[["otherNode"]]},{"name":"compareDocumentPosition","signatures":[["other"]]},{"name":"lookupPrefix","signatures":[["namespaceURI"]]},{"name":"lookupNamespaceURI","signatures":[["prefix"]],"receiver":"Node"},{"name":"lookupNamespaceURI","signatures":[["?prefix"]],"receiver":"XPathNSResolver"},{"name":"isDefaultNamespace","signatures":[["namespaceURI"]]},{"name":"insertBefore","signatures":[["node","child"]]},{"name":"replaceChild","signatures":[["node","child"]]},{"name":"getElementById","signatures":[["elementId"]]},{"name":"prepend","signatures":[["...nodes"]]},{"name":"querySelector","signatures":[["selectors"]]},{"name":"querySelectorAll","signatures":[["selectors"]]},{"name":"setStart","signatures":[["node","offset"]]},{"name":"setEnd","signatures":[["node","offset"]]},{"name":"setStartBefore","signatures":[["node"]]},{"name":"setStartAfter","signatures":[["node"]]},{"name":"setEndBefore","signatures":[["node"]]},{"name":"setEndAfter","signatures":[["node"]]},{"name":"collapse","signatures":[["?toStart"]],"receiver":"Range"},{"name":"collapse","signatures":[["node","?offset"]],"receiver":"Selection"},{"name":"selectNode","signatures":[["node"]]},{"name":"selectNodeContents","signatures":[["node"]]},{"name":"compareBoundaryPoints","signatures":[["how","sourceRange"]]},{"name":"insertNode","signatures":[["node"]]},{"name":"surroundContents","signatures":[["newParent"]]},{"name":"isPointInRange","signatures":[["node","offset"]]},{"name":"comparePoint","signatures":[["node","offset"]]},{"name":"intersectsNode","signatures":[["node"]]},{"name":"default","signatures":[["queue_type"]]},{"name":"postTask","signatures":[["callback","?signal"]],"receiver":"ScriptedTaskQueue"},{"name":"postTask","signatures":[["task","...arguments"]],"receiver":"TaskWorklet"},{"name":"postTask","signatures":[["task","...arguments"]],"receiver":"WorkerTaskQueue"},{"name":"splitText","signatures":[["offset"]]},{"name":"getRangeAt","signatures":[["index"]]},{"name":"addRange","signatures":[["range"]]},{"name":"removeRange","signatures":[["range"]]},{"name":"setPosition","signatures":[["node","?offset"]],"receiver":"Selection"},{"name":"setPosition","signatures":[["x","y","z"]],"receiver":"AudioListener"},{"name":"setPosition","signatures":[["x","y","z"]],"receiver":"PannerNode"},{"name":"extend","signatures":[["node","?offset"]]},{"name":"setBaseAndExtent","signatures":[["baseNode","baseOffset","extentNode","extentOffset"]]},{"name":"selectAllChildren","signatures":[["node"]]},{"name":"containsNode","signatures":[["node","?allowPartialContainment"]]},{"name":"ClipboardEvent","signatures":[["type","?eventInitDict"]]},{"name":"CompositionEvent","signatures":[["type","?eventInitDict"]]},{"name":"initCompositionEvent","signatures":[["?type","?bubbles","?cancelable","?view","?data"]]},{"name":"DragEvent","signatures":[["type","?eventInitDict"]]},{"name":"ErrorEvent","signatures":[["type","?eventInitDict"]]},{"name":"FocusEvent","signatures":[["type","?eventInitDict"]]},{"name":"HashChangeEvent","signatures":[["type","?eventInitDict"]]},{"name":"InputEvent","signatures":[["type","?eventInitDict"]]},{"name":"KeyboardEvent","signatures":[["type","?eventInitDict"]]},{"name":"getModifierState","signatures":[["keyArg"]]},{"name":"initKeyboardEvent","signatures":[["type","?bubbles","?cancelable","?view","?keyIdentifier","?location","?ctrlKey","?altKey","?shiftKey","?metaKey"]]},{"name":"MessageEvent","signatures":[["type","?eventInitDict"]]},{"name":"initMessageEvent","signatures":[["typeArg","?canBubbleArg","?cancelableArg","?dataArg","?originArg","?lastEventIdArg","?sourceArg","?portsArg"]]},{"name":"MouseEvent","signatures":[["type","?eventInitDict"]]},{"name":"initMouseEvent","signatures":[["?type","?bubbles","?cancelable","?view","?detail","?screenX","?screenY","?clientX","?clientY","?ctrlKey","?altKey","?shiftKey","?metaKey","?button","?relatedTarget"]]},{"name":"initMutationEvent","signatures":[["?type","?bubbles","?cancelable","?relatedNode","?prevValue","?newValue","?attrName","?attrChange"]]},{"name":"PageTransitionEvent","signatures":[["type","?eventInitDict"]]},{"name":"PopStateEvent","signatures":[["type","?eventInitDict"]]},{"name":"ProgressEvent","signatures":[["type","?eventInitDict"]]},{"name":"PromiseRejectionEvent","signatures":[["type","eventInitDict"]]},{"name":"initTextEvent","signatures":[["?type","?bubbles","?cancelable","?view","?data"]]},{"name":"TouchEvent","signatures":[["type","?eventInitDict"]]},{"name":"UIEvent","signatures":[["type","?eventInitDict"]]},{"name":"initUIEvent","signatures":[["?type","?bubbles","?cancelable","?view","?detail"]]},{"name":"WheelEvent","signatures":[["type","?eventInitDict"]]},{"name":"allowsFeature","signatures":[["feature","?url"]]},{"name":"getAllowlistForFeature","signatures":[["feature"]]},{"name":"Headers","signatures":[["?init"]]},{"name":"Request","signatures":[["input","?requestInitDict"]]},{"name":"Response","signatures":[["?body","?init"]]},{"name":"error","signatures":[["?e"]],"receiver":"ReadableStreamDefaultController"},{"name":"error","signatures":[["?e"]],"receiver":"WritableStreamDefaultController"},{"name":"error","signatures":[["...data"]],"receiver":"console"},{"name":"redirect","signatures":[["url","?status"]]},{"name":"fetch","signatures":[["input","?init"]],"receiver":"Window"},{"name":"fetch","signatures":[["input","?init"]],"receiver":"WorkerGlobalScope"},{"name":"fetch","signatures":[["id","requests","?options"]],"receiver":"BackgroundFetchManager"},{"name":"fetch","signatures":[["input","?init"]],"receiver":"ServiceWorkerGlobalScope"},{"name":"Blob","signatures":[["?blobParts","?options"]]},{"name":"readAsArrayBuffer","signatures":[["blob"]]},{"name":"readAsBinaryString","signatures":[["blob"]]},{"name":"readAsText","signatures":[["blob","?label"]]},{"name":"readAsDataURL","signatures":[["blob"]]},{"name":"File","signatures":[["fileBits","fileName","?options"]]},{"name":"createObjectURL","signatures":[["blob"],["source"]]},{"name":"revokeObjectURL","signatures":[["url"]]},{"name":"go","signatures":[["?delta"]]},{"name":"pushState","signatures":[["data","title","?url"]]},{"name":"replaceState","signatures":[["data","title","?url"]]},{"name":"isInputPending","signatures":[["?inputTypes"]]},{"name":"btoa","signatures":[["btoa"]]},{"name":"atob","signatures":[["atob"]]},{"name":"setTimeout","signatures":[["handler","?timeout","...arguments"]]},{"name":"clearTimeout","signatures":[["?handle"]]},{"name":"setInterval","signatures":[["handler","?timeout","...arguments"]]},{"name":"clearInterval","signatures":[["?handle"]]},{"name":"createImageBitmap","signatures":[["imageBitmap","?options"],["imageBitmap","sx","sy","sw","sh","?options"]]},{"name":"stop","signatures":[["?when"]],"receiver":"AudioScheduledSourceNode"},{"name":"focus","signatures":[["?options"]],"receiver":"HTMLElement"},{"name":"alert","signatures":[["?message"]]},{"name":"confirm","signatures":[["?message"]]},{"name":"prompt","signatures":[["?message","?defaultValue"]],"receiver":"Window"},{"name":"postMessage","signatures":[["message","?options"],["message","targetOrigin","?transfer"]],"receiver":"Window"},{"name":"postMessage","signatures":[["message","transfer"],["message","?options"]],"receiver":"MessagePort"},{"name":"postMessage","signatures":[["message","transfer"],["message","?options"]],"receiver":"DedicatedWorkerGlobalScope"},{"name":"postMessage","signatures":[["message","transfer"],["message","?options"]],"receiver":"Worker"},{"name":"postMessage","signatures":[["message"]],"receiver":"BroadcastChannel"},{"name":"postMessage","signatures":[["message","transfer"],["message","?options"]],"receiver":"Client"},{"name":"postMessage","signatures":[["message","transfer"],["message","?options"]],"receiver":"ServiceWorker"},{"name":"queueMicrotask","signatures":[["callback"]]},{"name":"requestAnimationFrame","signatures":[["callback"]]},{"name":"cancelAnimationFrame","signatures":[["handle"]]},{"name":"requestIdleCallback","signatures":[["callback","?options"]]},{"name":"cancelIdleCallback","signatures":[["handle"]]},{"name":"getComputedStyle","signatures":[["elt","?pseudoElt"]]},{"name":"matchMedia","signatures":[["query"]]},{"name":"moveTo","signatures":[["x","y"]],"receiver":"Window"},{"name":"moveTo","signatures":[["x","y"]],"receiver":"CanvasPath"},{"name":"moveTo","signatures":[["parent","name"]],"receiver":"EntrySync"},{"name":"moveTo","signatures":[["parent","?name","?successCallback","?errorCallback"]],"receiver":"Entry"},{"name":"moveTo","signatures":[["parent","?name"]],"receiver":"FileSystemBaseHandle"},{"name":"moveBy","signatures":[["x","y"]]},{"name":"resizeTo","signatures":[["x","y"]]},{"name":"resizeBy","signatures":[["x","y"]]},{"name":"getComputedAccessibleNode","signatures":[["element"]]},{"name":"webkitRequestAnimationFrame","signatures":[["callback"]]},{"name":"webkitCancelAnimationFrame","signatures":[["id"]]},{"name":"requestFullscreen","signatures":[["?options"]]},{"name":"webkitRequestFullScreen","signatures":[["?options"]]},{"name":"webkitRequestFullscreen","signatures":[["?options"]]},{"name":"DOMMatrixReadOnly","signatures":[["?init"]]},{"name":"fromMatrix","signatures":[["?other"]]},{"name":"fromFloat32Array","signatures":[["array32"]]},{"name":"fromFloat64Array","signatures":[["array64"]]},{"name":"translate","signatures":[["?tx","?ty","?tz"]],"receiver":"DOMMatrixReadOnly"},{"name":"translate","signatures":[["x","y"]],"receiver":"SVGMatrix"},{"name":"translate","signatures":[["x","y"]],"receiver":"CanvasRenderingContext2D"},{"name":"translate","signatures":[["x","y"]],"receiver":"OffscreenCanvasRenderingContext2D"},{"name":"translate","signatures":[["x","y"]],"receiver":"PaintRenderingContext2D"},{"name":"scale","signatures":[["?scaleX","?scaleY","?scaleZ","?originX","?originY","?originZ"]],"receiver":"DOMMatrixReadOnly"},{"name":"scale","signatures":[["scaleFactor"]],"receiver":"SVGMatrix"},{"name":"scale","signatures":[["x","y"]],"receiver":"CanvasRenderingContext2D"},{"name":"scale","signatures":[["x","y"]],"receiver":"OffscreenCanvasRenderingContext2D"},{"name":"scale","signatures":[["x","y"]],"receiver":"PaintRenderingContext2D"},{"name":"scaleNonUniform","signatures":[["?scaleX","?scaleY"]],"receiver":"DOMMatrixReadOnly"},{"name":"scaleNonUniform","signatures":[["scaleFactorX","scaleFactorY"]],"receiver":"SVGMatrix"},{"name":"scale3d","signatures":[["?scale","?originX","?originY","?originZ"]]},{"name":"rotate","signatures":[["?rotX","?rotY","?rotZ"]],"receiver":"DOMMatrixReadOnly"},{"name":"rotate","signatures":[["angle"]],"receiver":"SVGMatrix"},{"name":"rotate","signatures":[["angle"]],"receiver":"CanvasRenderingContext2D"},{"name":"rotate","signatures":[["angle"]],"receiver":"OffscreenCanvasRenderingContext2D"},{"name":"rotate","signatures":[["angle"]],"receiver":"PaintRenderingContext2D"},{"name":"rotateFromVector","signatures":[["?x","?y"]],"receiver":"DOMMatrixReadOnly"},{"name":"rotateFromVector","signatures":[["x","y"]],"receiver":"SVGMatrix"},{"name":"rotateAxisAngle","signatures":[["?x","?y","?z","?angle"]]},{"name":"skewX","signatures":[["?sx"]],"receiver":"DOMMatrixReadOnly"},{"name":"skewX","signatures":[["angle"]],"receiver":"SVGMatrix"},{"name":"skewY","signatures":[["?sy"]],"receiver":"DOMMatrixReadOnly"},{"name":"skewY","signatures":[["angle"]],"receiver":"SVGMatrix"},{"name":"multiply","signatures":[["?other"]],"receiver":"DOMMatrixReadOnly"},{"name":"multiply","signatures":[["secondMatrix"]],"receiver":"SVGMatrix"},{"name":"transformPoint","signatures":[["?point"]]},{"name":"DOMMatrix","signatures":[["?init"]]},{"name":"multiplySelf","signatures":[["?other"]]},{"name":"preMultiplySelf","signatures":[["?other"]]},{"name":"translateSelf","signatures":[["?tx","?ty","?tz"]]},{"name":"scaleSelf","signatures":[["?scaleX","?scaleY","?scaleZ","?originX","?originY","?originZ"]]},{"name":"scale3dSelf","signatures":[["?scale","?originX","?originY","?originZ"]]},{"name":"rotateSelf","signatures":[["?rotX","?rotY","?rotZ"]]},{"name":"rotateFromVectorSelf","signatures":[["?x","?y"]]},{"name":"rotateAxisAngleSelf","signatures":[["?x","?y","?z","?angle"]]},{"name":"skewXSelf","signatures":[["?sx"]]},{"name":"skewYSelf","signatures":[["?sy"]]},{"name":"setMatrixValue","signatures":[["transformList"]]},{"name":"DOMPointReadOnly","signatures":[["?x","?y","?z","?w"]]},{"name":"fromPoint","signatures":[["?other"]]},{"name":"matrixTransform","signatures":[["?matrix"]],"receiver":"DOMPointReadOnly"},{"name":"matrixTransform","signatures":[["matrix"]],"receiver":"SVGPoint"},{"name":"DOMPoint","signatures":[["?x","?y","?z","?w"]]},{"name":"DOMQuad","signatures":[["?p1","?p2","?p3","?p4"]]},{"name":"fromRect","signatures":[["?other"]]},{"name":"fromQuad","signatures":[["?other"]]},{"name":"DOMRectReadOnly","signatures":[["?x","?y","?width","?height"]]},{"name":"DOMRect","signatures":[["?x","?y","?width","?height"]]},{"name":"toDataURL","signatures":[["?type","?arguments"]]},{"name":"toBlob","signatures":[["callback","?type","?arguments"]]},{"name":"convertToBlob","signatures":[["?options"]]},{"name":"ImageData","signatures":[["sw","sh"]]},{"name":"define","signatures":[["name","constructor","?options"]]},{"name":"whenDefined","signatures":[["name"]]},{"name":"upgrade","signatures":[["root"]]},{"name":"setFormValue","signatures":[["value","?entrySource"]]},{"name":"setValidity","signatures":[["flags","?message"]]},{"name":"FormDataEvent","signatures":[["type","?eventInitDict"]]},{"name":"FormData","signatures":[["?form"]]},{"name":"setCustomValidity","signatures":[["error"]]},{"name":"namedItem","signatures":[["name"]],"receiver":"HTMLFormControlsCollection"},{"name":"namedItem","signatures":[["name"]],"receiver":"HTMLSelectElement"},{"name":"namedItem","signatures":[["name"]],"receiver":"HTMLAllCollection"},{"name":"namedItem","signatures":[["name"]],"receiver":"HTMLCollection"},{"name":"namedItem","signatures":[["?name"]],"receiver":"RTCStatsResponse"},{"name":"namedItem","signatures":[["name"]],"receiver":"MimeTypeArray"},{"name":"namedItem","signatures":[["name"]],"receiver":"PluginArray"},{"name":"namedItem","signatures":[["name"]],"receiver":"Plugin"},{"name":"stepUp","signatures":[["?n"]]},{"name":"stepDown","signatures":[["?n"]]},{"name":"select","signatures":[["options"]],"receiver":"ContactsManager"},{"name":"setRangeText","signatures":[["replacement","?start","?end","?selectionMode"]]},{"name":"setSelectionRange","signatures":[["start","end","?direction"]]},{"name":"Option","signatures":[["?data","?value","?defaultSelected","?selected"]]},{"name":"Image","signatures":[["?width","?height"]]},{"name":"decode","signatures":[["?input","?options"]],"receiver":"TextDecoder"},{"name":"start","signatures":[["index"]],"receiver":"TimeRanges"},{"name":"start","signatures":[["stream"]],"receiver":"UnderlyingSourceBase"},{"name":"start","signatures":[["?timeslice"]],"receiver":"MediaRecorder"},{"name":"start","signatures":[["remoteParameters","?role"]],"receiver":"RTCIceTransport"},{"name":"start","signatures":[["?when","?grainOffset","?grainDuration"]],"receiver":"AudioBufferSourceNode"},{"name":"start","signatures":[["?when"]],"receiver":"AudioScheduledSourceNode"},{"name":"assignedNodes","signatures":[["?options"]]},{"name":"assignedElements","signatures":[["?options"]]},{"name":"insertRow","signatures":[["?index"]]},{"name":"deleteRow","signatures":[["index"]]},{"name":"insertCell","signatures":[["?index"]]},{"name":"deleteCell","signatures":[["index"]]},{"name":"Audio","signatures":[["?src"]]},{"name":"canPlayType","signatures":[["type"]]},{"name":"addTextTrack","signatures":[["kind","?label","?language"]]},{"name":"end","signatures":[["index"]],"receiver":"TimeRanges"},{"name":"getTrackById","signatures":[["id"]],"receiver":"AudioTrackList"},{"name":"getTrackById","signatures":[["id"]],"receiver":"TextTrackList"},{"name":"getTrackById","signatures":[["id"]],"receiver":"VideoTrackList"},{"name":"getTrackById","signatures":[["trackId"]],"receiver":"MediaStream"},{"name":"getCueById","signatures":[["id"]]},{"name":"addCue","signatures":[["cue"]]},{"name":"removeCue","signatures":[["cue"]]},{"name":"Touch","signatures":[["initDict"]]},{"name":"copyText","signatures":[["text"]]},{"name":"showContextMenuAtPoint","signatures":[["x","y","items","?document"]]},{"name":"sendMessageToEmbedder","signatures":[["message"]]},{"name":"unobserve","signatures":[["target"]],"receiver":"IntersectionObserver"},{"name":"unobserve","signatures":[["target"]],"receiver":"ResizeObserver"},{"name":"unobserve","signatures":[["db"]],"receiver":"IDBObserver"},{"name":"layoutNextFragment","signatures":[["?options"]]},{"name":"registerLayout","signatures":[["name","layoutCtor"]]},{"name":"watch","signatures":[["signals","callback"]],"receiver":"MojoHandle"},{"name":"watch","signatures":[["callback","?options"]],"receiver":"NFC"},{"name":"writeMessage","signatures":[["buffer","handles"]]},{"name":"readMessage","signatures":[["?flags"]]},{"name":"writeData","signatures":[["buffer","?options"]]},{"name":"discardData","signatures":[["numBytes","?options"]]},{"name":"readData","signatures":[["buffer","?options"]]},{"name":"mapBuffer","signatures":[["offset","numBytes"]]},{"name":"duplicateBufferHandle","signatures":[["?options"]]},{"name":"createDataPipe","signatures":[["options"]]},{"name":"createSharedBuffer","signatures":[["numBytes"]]},{"name":"bindInterface","signatures":[["interfaceName","request_handle","?scope"]]},{"name":"replaceDocumentInterfaceBrokerForTesting","signatures":[["test_broker_handle"]]},{"name":"MojoInterfaceInterceptor","signatures":[["interfaceName","?scope"]]},{"name":"MojoInterfaceRequestEvent","signatures":[["type","?eventInitDict"]]},{"name":"OffscreenCanvas","signatures":[["width","height"]]},{"name":"setValueAndClosePopup","signatures":[["numberValue","stringValue"]]},{"name":"setValue","signatures":[["value"]]},{"name":"selectFontsFromOwnerDocument","signatures":[["targetDocument"]]},{"name":"localizeNumberString","signatures":[["numberString"]]},{"name":"formatMonth","signatures":[["year","zeroBaseMonth"]]},{"name":"formatShortMonth","signatures":[["year","zeroBaseMonth"]]},{"name":"formatWeek","signatures":[["year","weekNumber","localizedStartDate"]]},{"name":"setWindowRect","signatures":[["x","y","width","height"]]},{"name":"consumeDelta","signatures":[["x","y"]]},{"name":"enqueue","signatures":[["?chunk"]]},{"name":"getReader","signatures":[["?mode"]]},{"name":"pipeThrough","signatures":[["transformStream","?options"]]},{"name":"pipeTo","signatures":[["destination","?option"]]},{"name":"TransformStream","signatures":[["?transformer","?writableStrategy","?readableStrategy"]]},{"name":"newValueSpecifiedUnits","signatures":[["unitType","valueInSpecifiedUnits"]]},{"name":"convertToSpecifiedUnits","signatures":[["unitType"]]},{"name":"beginElementAt","signatures":[["offset"]]},{"name":"endElementAt","signatures":[["offset"]]},{"name":"setStdDeviation","signatures":[["stdDeviationX","stdDeviationY"]]},{"name":"isPointInFill","signatures":[["point"]]},{"name":"isPointInStroke","signatures":[["point"]],"receiver":"SVGGeometryElement"},{"name":"isPointInStroke","signatures":[["x","y"],["path","x","y"]],"receiver":"CanvasRenderingContext2D"},{"name":"isPointInStroke","signatures":[["x","y"],["path","x","y"]],"receiver":"OffscreenCanvasRenderingContext2D"},{"name":"isPointInStroke","signatures":[["x","y"],["path","x","y"]],"receiver":"PaintRenderingContext2D"},{"name":"getPointAtLength","signatures":[["distance"]]},{"name":"initialize","signatures":[["newItem"]]},{"name":"getItem","signatures":[["index"]],"receiver":"SVGLengthList"},{"name":"getItem","signatures":[["index"]],"receiver":"SVGNumberList"},{"name":"getItem","signatures":[["index"]],"receiver":"SVGPointList"},{"name":"getItem","signatures":[["index"]],"receiver":"SVGStringList"},{"name":"getItem","signatures":[["index"]],"receiver":"SVGTransformList"},{"name":"getItem","signatures":[["key"]],"receiver":"Storage"},{"name":"insertItemBefore","signatures":[["newItem","index"]],"receiver":"SVGLengthList"},{"name":"insertItemBefore","signatures":[["newItem","index"]],"receiver":"SVGNumberList"},{"name":"insertItemBefore","signatures":[["newItem","index"]],"receiver":"SVGPointList"},{"name":"insertItemBefore","signatures":[["item","index"]],"receiver":"SVGStringList"},{"name":"insertItemBefore","signatures":[["newItem","index"]],"receiver":"SVGTransformList"},{"name":"replaceItem","signatures":[["newItem","index"]]},{"name":"removeItem","signatures":[["index"]],"receiver":"SVGLengthList"},{"name":"removeItem","signatures":[["index"]],"receiver":"SVGNumberList"},{"name":"removeItem","signatures":[["index"]],"receiver":"SVGPointList"},{"name":"removeItem","signatures":[["index"]],"receiver":"SVGStringList"},{"name":"removeItem","signatures":[["index"]],"receiver":"SVGTransformList"},{"name":"removeItem","signatures":[["key"]],"receiver":"Storage"},{"name":"appendItem","signatures":[["newItem"]]},{"name":"setOrientToAngle","signatures":[["angle"]]},{"name":"getIntersectionList","signatures":[["rect","referenceElement"]]},{"name":"getEnclosureList","signatures":[["rect","referenceElement"]]},{"name":"checkIntersection","signatures":[["element","rect"]]},{"name":"checkEnclosure","signatures":[["element","rect"]]},{"name":"createSVGTransformFromMatrix","signatures":[["matrix"]]},{"name":"suspendRedraw","signatures":[["maxWaitMilliseconds"]]},{"name":"unsuspendRedraw","signatures":[["suspendHandleId"]]},{"name":"setCurrentTime","signatures":[["seconds"]]},{"name":"getSubStringLength","signatures":[["charnum","nchars"]]},{"name":"getStartPositionOfChar","signatures":[["charnum"]]},{"name":"getEndPositionOfChar","signatures":[["charnum"]]},{"name":"getExtentOfChar","signatures":[["charnum"]]},{"name":"getRotationOfChar","signatures":[["charnum"]]},{"name":"getCharNumAtPosition","signatures":[["point"]]},{"name":"selectSubString","signatures":[["charnum","nchars"]]},{"name":"setMatrix","signatures":[["matrix"]]},{"name":"setTranslate","signatures":[["tx","ty"]]},{"name":"setScale","signatures":[["sx","sy"]]},{"name":"setRotate","signatures":[["angle","cx","cy"]]},{"name":"setSkewX","signatures":[["angle"]]},{"name":"setSkewY","signatures":[["angle"]]},{"name":"getEntriesByType","signatures":[["entryType"]]},{"name":"getEntriesByName","signatures":[["name","?entryType"]]},{"name":"PerformanceObserver","signatures":[["callback"]]},{"name":"setResourceTimingBufferSize","signatures":[["maxSize"]]},{"name":"setElementTimingBufferMaxSize","signatures":[["maxSize"]]},{"name":"setEventTimingBufferMaxSize","signatures":[["maxSize"]]},{"name":"mark","signatures":[["markName","?markOptions"]]},{"name":"clearMarks","signatures":[["?markName"]]},{"name":"measure","signatures":[["measureName","?startOrOptions","?end"]]},{"name":"clearMeasures","signatures":[["?measureName"]]},{"name":"createPolicy","signatures":[["policyName","policyOptions","?exposed"]]},{"name":"getExposedPolicy","signatures":[["policyName"]]},{"name":"isHTML","signatures":[["checkedObject"]]},{"name":"isScript","signatures":[["checkedObject"]]},{"name":"isScriptURL","signatures":[["checkedObject"]]},{"name":"isURL","signatures":[["checkedObject"]]},{"name":"createHTML","signatures":[["input"]]},{"name":"createScript","signatures":[["input"]]},{"name":"createScriptURL","signatures":[["input"]]},{"name":"createURL","signatures":[["input"]]},{"name":"URLSearchParams","signatures":[["?init"]]},{"name":"URL","signatures":[["url","?base"]]},{"name":"registerTask","signatures":[["name","taskConstructor"]]},{"name":"WorkerTaskQueue","signatures":[["queue_type"]]},{"name":"postFunction","signatures":[["task","?signal","...arguments"]]},{"name":"importScripts","signatures":[["...urls"]]},{"name":"Worker","signatures":[["scriptURL","?options"]]},{"name":"addModule","signatures":[["moduleURL","?options"]]},{"name":"createExpression","signatures":[["expression","?resolver"]]},{"name":"createNSResolver","signatures":[["nodeResolver"]]},{"name":"evaluate","signatures":[["expression","contextNode","?resolver","?type","?inResult"]],"receiver":"Document"},{"name":"evaluate","signatures":[["expression","contextNode","?resolver","?type","?inResult"]],"receiver":"XPathEvaluator"},{"name":"evaluate","signatures":[["contextNode","?type","?inResult"]],"receiver":"XPathExpression"},{"name":"parseFromString","signatures":[["str","type"]]},{"name":"serializeToString","signatures":[["root"]]},{"name":"snapshotItem","signatures":[["index"]]},{"name":"importStylesheet","signatures":[["style"]]},{"name":"transformToFragment","signatures":[["source","output"]]},{"name":"transformToDocument","signatures":[["source"]]},{"name":"setParameter","signatures":[["namespaceURI","localName","value"]]},{"name":"getParameter","signatures":[["namespaceURI","localName"]],"receiver":"XSLTProcessor"},{"name":"getParameter","signatures":[["pname"]],"receiver":"WebGLRenderingContextBase"},{"name":"removeParameter","signatures":[["namespaceURI","localName"]]},{"name":"setRequestHeader","signatures":[["name","value"]]},{"name":"send","signatures":[["?body"]],"receiver":"XMLHttpRequest"},{"name":"send","signatures":[["data"]],"receiver":"RTCDataChannel"},{"name":"send","signatures":[["message"],["data"]],"receiver":"PresentationConnection"},{"name":"send","signatures":[["data","?timestamp"]],"receiver":"MIDIOutput"},{"name":"send","signatures":[["data"]],"receiver":"WebSocket"},{"name":"getResponseHeader","signatures":[["name"]]},{"name":"overrideMimeType","signatures":[["mime"]]},{"name":"registerAnimator","signatures":[["name","animatorConstructor"]]},{"name":"setSinkId","signatures":[["sinkId"]]},{"name":"BackgroundFetchEvent","signatures":[["type","init"]]},{"name":"matchAll","signatures":[["?request","?options"]],"receiver":"BackgroundFetchRegistration"},{"name":"matchAll","signatures":[["?request","?options"]],"receiver":"Cache"},{"name":"matchAll","signatures":[["?options"]],"receiver":"Clients"},{"name":"BackgroundFetchUpdateUIEvent","signatures":[["type","init"]]},{"name":"updateUI","signatures":[["options"]]},{"name":"SyncEvent","signatures":[["type","init"]]},{"name":"register","signatures":[["tag"]],"receiver":"SyncManager"},{"name":"register","signatures":[["url","?options"]],"receiver":"ServiceWorkerContainer"},{"name":"sendBeacon","signatures":[["url","?data"]]},{"name":"getDescriptor","signatures":[["descriptor"]],"receiver":"BluetoothRemoteGATTCharacteristic"},{"name":"getDescriptor","signatures":[["name"]],"static":true,"receiver":"BluetoothUUID"},{"name":"getDescriptors","signatures":[["?descriptor"]]},{"name":"writeValue","signatures":[["value"]]},{"name":"connect","signatures":[["destination","?output","?input"]],"receiver":"AudioNode"},{"name":"getPrimaryService","signatures":[["service"]]},{"name":"getPrimaryServices","signatures":[["?service"]]},{"name":"getCharacteristic","signatures":[["characteristic"]],"receiver":"BluetoothRemoteGATTService"},{"name":"getCharacteristic","signatures":[["name"]],"static":true,"receiver":"BluetoothUUID"},{"name":"getCharacteristics","signatures":[["?characteristic"]]},{"name":"getService","signatures":[["name"]]},{"name":"canonicalUUID","signatures":[["alias"]]},{"name":"requestDevice","signatures":[["?options"]],"receiver":"Bluetooth"},{"name":"requestDevice","signatures":[["options"]],"receiver":"USB"},{"name":"requestLEScan","signatures":[["?options"]]},{"name":"BroadcastChannel","signatures":[["name"]]},{"name":"addAll","signatures":[["requests"]]},{"name":"put","signatures":[["request","response"]],"receiver":"Cache"},{"name":"put","signatures":[["value","?key"]],"receiver":"IDBObjectStore"},{"name":"addColorStop","signatures":[["offset","color"]]},{"name":"lineTo","signatures":[["x","y"]]},{"name":"quadraticCurveTo","signatures":[["cpx","cpy","x","y"]]},{"name":"bezierCurveTo","signatures":[["cp1x","cp1y","cp2x","cp2y","x","y"]]},{"name":"arcTo","signatures":[["x1","y1","x2","y2","radius"]]},{"name":"rect","signatures":[["x","y","width","height"]]},{"name":"arc","signatures":[["x","y","radius","startAngle","endAngle","?anticlockwise"]]},{"name":"ellipse","signatures":[["x","y","radiusX","radiusY","rotation","startAngle","endAngle","?anticlockwise"]]},{"name":"setTransform","signatures":[["?transform"]],"receiver":"CanvasPattern"},{"name":"setTransform","signatures":[["?transform"],["a","b","c","d","e","f"]],"receiver":"CanvasRenderingContext2D"},{"name":"setTransform","signatures":[["a","b","c","d","e","f"]],"receiver":"OffscreenCanvasRenderingContext2D"},{"name":"setTransform","signatures":[["?transform"],["a","b","c","d","e","f"]],"receiver":"PaintRenderingContext2D"},{"name":"transform","signatures":[["a","b","c","d","e","f"]]},{"name":"createLinearGradient","signatures":[["x0","y0","x1","y1"]]},{"name":"createRadialGradient","signatures":[["x0","y0","r0","x1","y1","r1"]]},{"name":"createPattern","signatures":[["image","repetitionType"]]},{"name":"clearRect","signatures":[["x","y","width","height"]]},{"name":"fillRect","signatures":[["x","y","width","height"]]},{"name":"strokeRect","signatures":[["x","y","width","height"]]},{"name":"stroke","signatures":[["?path"]]},{"name":"drawFocusIfNeeded","signatures":[["element"],["path","element"]]},{"name":"scrollPathIntoView","signatures":[["?path"]]},{"name":"clip","signatures":[["?winding"],["path","?winding"]],"receiver":"CanvasRenderingContext2D"},{"name":"clip","signatures":[["?path"]],"receiver":"OffscreenCanvasRenderingContext2D"},{"name":"clip","signatures":[["?winding"],["path","?winding"]],"receiver":"PaintRenderingContext2D"},{"name":"isPointInPath","signatures":[["x","y","?winding"],["path","x","y","?winding"]]},{"name":"fillText","signatures":[["text","x","y","?maxWidth"]]},{"name":"strokeText","signatures":[["text","x","y","?maxWidth"]]},{"name":"measureText","signatures":[["text"]]},{"name":"drawImage","signatures":[["image","x","y","?width","?height"],["image","sx","sy","sw","sh","dx","dy","dw","dh"]]},{"name":"addHitRegion","signatures":[["?options"]]},{"name":"removeHitRegion","signatures":[["id"]]},{"name":"createImageData","signatures":[["imagedata"],["sw","sh","?imageDataColorSettings"],["data","sw","sh","?imageDataColorSettings"]],"receiver":"CanvasRenderingContext2D"},{"name":"createImageData","signatures":[["imagedata"],["sw","sh","?imageDataColorSettings"],["data","sw","sh","imageDataColorSettings"]],"receiver":"OffscreenCanvasRenderingContext2D"},{"name":"getImageData","signatures":[["sx","sy","sw","sh"]]},{"name":"putImageData","signatures":[["imagedata","dx","dy","?dirtyX","?dirtyY","?dirtyWidth","?dirtyHeight"]]},{"name":"setLineDash","signatures":[["dash"]]},{"name":"Path2D","signatures":[["?path"]]},{"name":"addPath","signatures":[["path","?transform"]]},{"name":"getContext","signatures":[["contextId","?attributes"]],"receiver":"HTMLCanvasElement"},{"name":"getContext","signatures":[["contextType","?attributes"]],"receiver":"OffscreenCanvas"},{"name":"transferFromImageBitmap","signatures":[["bitmap"]]},{"name":"writeText","signatures":[["data"]]},{"name":"CookieChangeEvent","signatures":[["type","?eventInitDict"]]},{"name":"subscribeToChanges","signatures":[["subscriptions"]]},{"name":"ExtendableCookieChangeEvent","signatures":[["type","?eventInitDict"]]},{"name":"FederatedCredential","signatures":[["data"]]},{"name":"PasswordCredential","signatures":[["data"]]},{"name":"getRandomValues","signatures":[["array"]]},{"name":"encrypt","signatures":[["algorithm","key","data"]]},{"name":"decrypt","signatures":[["algorithm","key","data"]]},{"name":"verify","signatures":[["algorithm","key","signature","data"]]},{"name":"digest","signatures":[["algorithm","data"]]},{"name":"generateKey","signatures":[["algorithm","extractable","keyUsages"]]},{"name":"deriveKey","signatures":[["algorithm","baseKey","derivedKeyType","extractable","keyUsages"]]},{"name":"deriveBits","signatures":[["algorithm","baseKey","length"]]},{"name":"importKey","signatures":[["format","keyData","algorithm","extractable","keyUsages"]]},{"name":"exportKey","signatures":[["format","key"]]},{"name":"wrapKey","signatures":[["format","key","wrappingKey","wrapAlgorithm"]]},{"name":"unwrapKey","signatures":[["format","wrappedKey","unwrappingKey","unwrapAlgorithm","unwrappedKeyAlgorithm","extractable","keyUsages"]]},{"name":"registerPaint","signatures":[["name","paintCtor"]]},{"name":"DeviceMotionEvent","signatures":[["type","?eventInitDict"]]},{"name":"DeviceOrientationEvent","signatures":[["type","?eventInitDict"]]},{"name":"TextDecoderStream","signatures":[["?label","?options"]]},{"name":"TextDecoder","signatures":[["?label","?options"]]},{"name":"encode","signatures":[["?input"]]},{"name":"encodeInto","signatures":[["source","destination"]]},{"name":"setMediaKeys","signatures":[["mediaKeys"]]},{"name":"generateRequest","signatures":[["initDataType","initData"]]},{"name":"getStatusForPolicy","signatures":[["policy"]]},{"name":"createSession","signatures":[["?sessionType"]]},{"name":"setServerCertificate","signatures":[["serverCertificate"]]},{"name":"requestMediaKeySystemAccess","signatures":[["keySystem","supportedConfigurations"]]},{"name":"EventSource","signatures":[["url","?eventSourceInitDict"]]},{"name":"webkitRequestFileSystem","signatures":[["type","size","?successCallback","?errorCallback"]],"receiver":"DedicatedWorkerGlobalScope"},{"name":"webkitRequestFileSystem","signatures":[["type","size","?successCallback","?errorCallback"]],"receiver":"SharedWorkerGlobalScope"},{"name":"webkitRequestFileSystem","signatures":[["type","size","successCallback","?errorCallback"]],"receiver":"Window"},{"name":"webkitRequestFileSystemSync","signatures":[["type","size"]]},{"name":"webkitResolveLocalFileSystemURL","signatures":[["url","successCallback","?errorCallback"]]},{"name":"webkitResolveLocalFileSystemSyncURL","signatures":[["url"]]},{"name":"isolatedFileSystem","signatures":[["fileSystemId","registeredName"]]},{"name":"upgradeDraggedFileSystemPermissions","signatures":[["domFileSystem"]]},{"name":"getFile","signatures":[["path","flags"]],"receiver":"DirectoryEntrySync"},{"name":"getFile","signatures":[["path","?options","?successCallback","?errorCallback"]],"receiver":"DirectoryEntry"},{"name":"getFile","signatures":[["name","?options"]],"receiver":"FileSystemDirectoryHandle"},{"name":"getDirectory","signatures":[["path","flags"]],"receiver":"DirectoryEntrySync"},{"name":"getDirectory","signatures":[["path","?options","?successCallback","?errorCallback"]],"receiver":"DirectoryEntry"},{"name":"getDirectory","signatures":[["name","?options"]],"receiver":"FileSystemDirectoryHandle"},{"name":"removeRecursively","signatures":[["successCallback","?errorCallback"]],"receiver":"DirectoryEntry"},{"name":"readEntries","signatures":[["successCallback","?errorCallback"]],"receiver":"DirectoryReader"},{"name":"getMetadata","signatures":[["successCallback","?errorCallback"]],"receiver":"Entry"},{"name":"copyTo","signatures":[["parent","name"]],"receiver":"EntrySync"},{"name":"copyTo","signatures":[["parent","?name","?successCallback","?errorCallback"]],"receiver":"Entry"},{"name":"copyTo","signatures":[["parent","?name"]],"receiver":"FileSystemBaseHandle"},{"name":"getParent","signatures":[["?successCallback","?errorCallback"]],"receiver":"Entry"},{"name":"file","signatures":[["successCallback","?errorCallback"]],"receiver":"FileEntry"},{"name":"createWriter","signatures":[["successCallback","?errorCallback"]],"receiver":"FileEntry"},{"name":"getSystemDirectory","signatures":[["options"]]},{"name":"truncate","signatures":[["size"]]},{"name":"seek","signatures":[["position"]]},{"name":"chooseFileSystemEntries","signatures":[["?options"]]},{"name":"playEffect","signatures":[["type","params"]]},{"name":"getCurrentPosition","signatures":[["successCallback","?errorCallback","?options"]]},{"name":"watchPosition","signatures":[["successCallback","?errorCallback","?options"]]},{"name":"clearWatch","signatures":[["watchID"]]},{"name":"query","signatures":[["?options"]],"receiver":"IdleManager"},{"name":"query","signatures":[["permission"]],"receiver":"Permissions"},{"name":"ImageCapture","signatures":[["track"]]},{"name":"takePhoto","signatures":[["?photoSettings"]]},{"name":"advance","signatures":[["count"]]},{"name":"continue","signatures":[["?key"]]},{"name":"continuePrimaryKey","signatures":[["key","primaryKey"]]},{"name":"transaction","signatures":[["storeNames","?mode"]],"receiver":"IDBDatabase"},{"name":"transaction","signatures":[["callback","?errorCallback","?successCallback"]],"receiver":"Database"},{"name":"createObjectStore","signatures":[["name","?options"]]},{"name":"deleteObjectStore","signatures":[["name"]]},{"name":"deleteDatabase","signatures":[["name"]]},{"name":"cmp","signatures":[["first","second"]]},{"name":"getKey","signatures":[["key"]],"receiver":"IDBIndex"},{"name":"getKey","signatures":[["key"]],"receiver":"IDBObjectStore"},{"name":"getKey","signatures":[["name"]],"receiver":"PushSubscription"},{"name":"getAllKeys","signatures":[["?query","?count"]]},{"name":"count","signatures":[["?key"]],"receiver":"IDBIndex"},{"name":"count","signatures":[["?key"]],"receiver":"IDBObjectStore"},{"name":"count","signatures":[["?label"]],"receiver":"console"},{"name":"openCursor","signatures":[["?range","?direction"]]},{"name":"openKeyCursor","signatures":[["?range","?direction"]]},{"name":"only","signatures":[["value"]]},{"name":"lowerBound","signatures":[["bound","?open"]]},{"name":"upperBound","signatures":[["bound","?open"]]},{"name":"bound","signatures":[["lower","upper","?lowerOpen","?upperOpen"]]},{"name":"index","signatures":[["name"]]},{"name":"createIndex","signatures":[["name","keyPath","?options"]]},{"name":"deleteIndex","signatures":[["name"]]},{"name":"IDBObserver","signatures":[["callback"]]},{"name":"objectStore","signatures":[["name"]]},{"name":"IDBVersionChangeEvent","signatures":[["type","?eventInitDict"]]},{"name":"lock","signatures":[["?keyCodes"]],"receiver":"Keyboard"},{"name":"lock","signatures":[["orientation"]],"receiver":"ScreenOrientation"},{"name":"request","signatures":[["name","callback"],["name","options","callback"]],"receiver":"LockManager"},{"name":"request","signatures":[["permissions"]],"receiver":"Permissions"},{"name":"decodingInfo","signatures":[["configuration"]]},{"name":"encodingInfo","signatures":[["configuration"]]},{"name":"captureStream","signatures":[["?frameRate"]],"receiver":"HTMLCanvasElement"},{"name":"BlobEvent","signatures":[["type","eventInitDict"]]},{"name":"MediaRecorder","signatures":[["stream","?options"]]},{"name":"isTypeSupported","signatures":[["type"]]},{"name":"setActionHandler","signatures":[["action","handler"]]},{"name":"addSourceBuffer","signatures":[["type"]]},{"name":"removeSourceBuffer","signatures":[["buffer"]]},{"name":"endOfStream","signatures":[["?error"]]},{"name":"setLiveSeekableRange","signatures":[["start","end"]]},{"name":"appendBuffer","signatures":[["data"]]},{"name":"changeType","signatures":[["type"]]},{"name":"getCapabilities","signatures":[["kind"]],"static":true,"receiver":"RTCRtpReceiver"},{"name":"getCapabilities","signatures":[["kind"]],"static":true,"receiver":"RTCRtpSender"},{"name":"getUserMedia","signatures":[["?constraints"]],"receiver":"MediaDevices"},{"name":"getUserMedia","signatures":[["constraints","successCallback","errorCallback"]],"receiver":"Navigator"},{"name":"getDisplayMedia","signatures":[["?constraints"]]},{"name":"MediaStreamTrackEvent","signatures":[["type","eventInitDict"]]},{"name":"applyConstraints","signatures":[["?constraints"]]},{"name":"addTrack","signatures":[["track"]],"receiver":"MediaStream"},{"name":"addTrack","signatures":[["track","...streams"]],"receiver":"RTCPeerConnection"},{"name":"removeTrack","signatures":[["track"]],"receiver":"MediaStream"},{"name":"removeTrack","signatures":[["sender"]],"receiver":"RTCPeerConnection"},{"name":"webkitGetUserMedia","signatures":[["constraints","successCallback","errorCallback"]]},{"name":"registerProtocolHandler","signatures":[["scheme","url","title"]]},{"name":"unregisterProtocolHandler","signatures":[["scheme","url"]]},{"name":"cancelPush","signatures":[["?target"]]},{"name":"cancelWatch","signatures":[["?id"]]},{"name":"NotificationEvent","signatures":[["type","eventInitDict"]]},{"name":"Notification","signatures":[["title","?options"]]},{"name":"requestPermission","signatures":[["?deprecatedCallback"]]},{"name":"showNotification","signatures":[["title","?options"]]},{"name":"getNotifications","signatures":[["?filter"]]},{"name":"TimestampTrigger","signatures":[["timestamp"]]},{"name":"AbortPaymentEvent","signatures":[["type","eventInitDict"]]},{"name":"respondWith","signatures":[["paymentAbortedResponse"]],"receiver":"AbortPaymentEvent"},{"name":"respondWith","signatures":[["canMakePaymentResponse"]],"receiver":"CanMakePaymentEvent"},{"name":"respondWith","signatures":[["response"]],"receiver":"PaymentRequestEvent"},{"name":"respondWith","signatures":[["r"]],"receiver":"FetchEvent"},{"name":"CanMakePaymentEvent","signatures":[["type","eventInitDict"]]},{"name":"MerchantValidationEvent","signatures":[["type","?eventInitDict"]]},{"name":"complete","signatures":[["merchantSessionPromise"]],"receiver":"MerchantValidationEvent"},{"name":"complete","signatures":[["?paymentResult"]],"receiver":"PaymentResponse"},{"name":"PaymentMethodChangeEvent","signatures":[["type","?eventInitDict"]]},{"name":"PaymentRequestEvent","signatures":[["type","eventInitDict"]]},{"name":"openWindow","signatures":[["url"]]},{"name":"PaymentRequestUpdateEvent","signatures":[["type","?eventInitDict"]]},{"name":"updateWith","signatures":[["detailsPromise"]]},{"name":"PaymentRequest","signatures":[["methodData","details","?options"]]},{"name":"retry","signatures":[["?errorFields"]]},{"name":"RTCDataChannelEvent","signatures":[["type","eventInitDict"]]},{"name":"insertDTMF","signatures":[["tones","?duration","?interToneGap"]]},{"name":"RTCErrorEvent","signatures":[["type","eventInitDict"]]},{"name":"RTCIceCandidate","signatures":[["?candidateInitDict"]]},{"name":"gather","signatures":[["options"]]},{"name":"addRemoteCandidate","signatures":[["remoteCandidate"]]},{"name":"stat","signatures":[["name"]]},{"name":"RTCPeerConnectionIceEvent","signatures":[["type","?eventInitDict"]]},{"name":"RTCPeerConnection","signatures":[["?configuration","?mediaConstraints"]]},{"name":"createOffer","signatures":[["?options"],["successCallback","failureCallback","?rtcOfferOptions"]]},{"name":"createAnswer","signatures":[["?options"],["successCallback","failureCallback","?mediaConstraints"]]},{"name":"setLocalDescription","signatures":[["description","?successCallback","?failureCallback"]]},{"name":"setRemoteDescription","signatures":[["description","?successCallback","?failureCallback"]]},{"name":"addIceCandidate","signatures":[["candidate","?successCallback","?failureCallback"]]},{"name":"setConfiguration","signatures":[["configuration"]]},{"name":"getStats","signatures":[["?callbackOrSelector"],["successCallback","selector"]],"receiver":"RTCPeerConnection"},{"name":"addTransceiver","signatures":[["track_or_kind","?init"]]},{"name":"createDataChannel","signatures":[["label","?dataChannelDict"]]},{"name":"generateCertificate","signatures":[["keygenAlgorithm"]]},{"name":"addStream","signatures":[["stream","?mediaConstraints"]]},{"name":"removeStream","signatures":[["stream"]]},{"name":"createDTMFSender","signatures":[["track"]]},{"name":"RTCQuicStreamEvent","signatures":[["type","?eventInitDict"]]},{"name":"readInto","signatures":[["data"]]},{"name":"waitForWriteBufferedAmountBelow","signatures":[["amount"]]},{"name":"waitForReadable","signatures":[["amount"]]},{"name":"RTCQuicTransport","signatures":[["transport"]]},{"name":"listen","signatures":[["remote_key"]]},{"name":"setParameters","signatures":[["?parameters"]]},{"name":"replaceTrack","signatures":[["withTrack"]]},{"name":"RTCSessionDescription","signatures":[["?descriptionInitDict"]]},{"name":"revoke","signatures":[["permission"]]},{"name":"requestAll","signatures":[["permissions"]]},{"name":"EnterPictureInPictureEvent","signatures":[["type","eventInitDict"]]},{"name":"refresh","signatures":[["?reload"]]},{"name":"PresentationConnectionAvailableEvent","signatures":[["type","eventInitDict"]]},{"name":"PresentationConnectionCloseEvent","signatures":[["type","eventInitDict"]]},{"name":"PresentationRequest","signatures":[["url"]]},{"name":"reconnect","signatures":[["id"]]},{"name":"PushEvent","signatures":[["type","?eventInitDict"]]},{"name":"queryUsageAndQuota","signatures":[["storageType","?usageCallback","?errorCallback"]],"receiver":"DeprecatedStorageInfo"},{"name":"queryUsageAndQuota","signatures":[["usageCallback","?errorCallback"]],"receiver":"DeprecatedStorageQuota"},{"name":"requestQuota","signatures":[["storageType","newQuotaInBytes","?quotaCallback","?errorCallback"]],"receiver":"DeprecatedStorageInfo"},{"name":"requestQuota","signatures":[["newQuotaInBytes","?quotaCallback","?errorCallback"]],"receiver":"DeprecatedStorageQuota"},{"name":"watchAvailability","signatures":[["callback"]]},{"name":"cancelWatchAvailability","signatures":[["?id"]]},{"name":"AbsoluteOrientationSensor","signatures":[["?sensorOptions"]]},{"name":"Accelerometer","signatures":[["?sensorOptions"]]},{"name":"AmbientLightSensor","signatures":[["?sensorOptions"]]},{"name":"Gyroscope","signatures":[["?sensorOptions"]]},{"name":"LinearAccelerationSensor","signatures":[["?sensorOptions"]]},{"name":"populateMatrix","signatures":[["targetBuffer"]]},{"name":"RelativeOrientationSensor","signatures":[["?sensorOptions"]]},{"name":"SensorErrorEvent","signatures":[["type","eventInitDict"]]},{"name":"requestPort","signatures":[["options"]]},{"name":"ExtendableEvent","signatures":[["type","?eventInitDict"]]},{"name":"waitUntil","signatures":[["f"]]},{"name":"FetchEvent","signatures":[["type","eventInitDict"]]},{"name":"InstallEvent","signatures":[["type","?eventInitDict"]]},{"name":"enable","signatures":[["cap"]],"receiver":"WebGLRenderingContextBase"},{"name":"disable","signatures":[["cap"]],"receiver":"WebGLRenderingContextBase"},{"name":"setHeaderValue","signatures":[["value"]]},{"name":"getRegistration","signatures":[["?documentURL"]]},{"name":"navigate","signatures":[["url"]]},{"name":"detect","signatures":[["image"]]},{"name":"FaceDetector","signatures":[["?faceDetectorOptions"]]},{"name":"addFromUri","signatures":[["src","?weight"]]},{"name":"addFromString","signatures":[["string","?weight"]]},{"name":"SpeechSynthesisErrorEvent","signatures":[["type","eventInitDict"]]},{"name":"speak","signatures":[["utterance"]]},{"name":"StorageEvent","signatures":[["type","?eventInitDict"]]},{"name":"initStorageEvent","signatures":[["type","?bubbles","?cancelable","?key","?oldValue","?newValue","?url","?storageArea"]]},{"name":"key","signatures":[["index"]]},{"name":"setItem","signatures":[["key","value"]]},{"name":"vibrate","signatures":[["pattern"]]},{"name":"getFrameData","signatures":[["frameData"]]},{"name":"getEyeParameters","signatures":[["whichEye"]]},{"name":"requestPresent","signatures":[["layers"]]},{"name":"getWakeLock","signatures":[["type"]]},{"name":"getFloatFrequencyData","signatures":[["array"]]},{"name":"getByteFrequencyData","signatures":[["array"]]},{"name":"getFloatTimeDomainData","signatures":[["array"]]},{"name":"getByteTimeDomainData","signatures":[["array"]]},{"name":"getChannelData","signatures":[["channelIndex"]]},{"name":"copyFromChannel","signatures":[["destination","channelNumber","?startInChannel"]]},{"name":"copyToChannel","signatures":[["source","channelNumber","?startInChannel"]]},{"name":"suspend","signatures":[["suspendTime"]],"receiver":"OfflineAudioContext"},{"name":"createMediaElementSource","signatures":[["mediaElement"]]},{"name":"createMediaStreamSource","signatures":[["mediaStream"]]},{"name":"setOrientation","signatures":[["x","y","z","xUp","yUp","zUp"]],"receiver":"AudioListener"},{"name":"setOrientation","signatures":[["x","y","z"]],"receiver":"PannerNode"},{"name":"setValueAtTime","signatures":[["value","time"]]},{"name":"linearRampToValueAtTime","signatures":[["value","time"]]},{"name":"exponentialRampToValueAtTime","signatures":[["value","time"]]},{"name":"setTargetAtTime","signatures":[["target","time","timeConstant"]]},{"name":"setValueCurveAtTime","signatures":[["values","time","duration"]]},{"name":"cancelScheduledValues","signatures":[["startTime"]]},{"name":"cancelAndHoldAtTime","signatures":[["startTime"]]},{"name":"registerProcessor","signatures":[["name","processorConstructor"]]},{"name":"createBuffer","signatures":[["numberOfChannels","numberOfFrames","sampleRate"]],"receiver":"BaseAudioContext"},{"name":"decodeAudioData","signatures":[["audioData","?successCallback","?errorCallback"]]},{"name":"createDelay","signatures":[["?maxDelayTime"]]},{"name":"createIIRFilter","signatures":[["feedForward","feedBack"]]},{"name":"createScriptProcessor","signatures":[["?bufferSize","?numberOfInputChannels","?numberOfOutputChannels"]]},{"name":"createPeriodicWave","signatures":[["real","imag","?options"]]},{"name":"createChannelSplitter","signatures":[["?numberOfOutputs"]]},{"name":"createChannelMerger","signatures":[["?numberOfInputs"]]},{"name":"getFrequencyResponse","signatures":[["frequencyHz","magResponse","phaseResponse"]]},{"name":"setPeriodicWave","signatures":[["periodicWave"]]},{"name":"changeVersion","signatures":[["oldVersion","newVersion","?callback","?errorCallback","?successCallback"]]},{"name":"readTransaction","signatures":[["callback","?errorCallback","?successCallback"]]},{"name":"executeSql","signatures":[["sqlStatement","?arguments","?callback","?errorCallback"]]},{"name":"openDatabase","signatures":[["name","version","displayName","estimatedSize","?creationCallback"]]},{"name":"drawArraysInstancedANGLE","signatures":[["mode","first","count","primcount"]]},{"name":"drawElementsInstancedANGLE","signatures":[["mode","count","type","offset","primcount"]]},{"name":"vertexAttribDivisorANGLE","signatures":[["index","divisor"]]},{"name":"queryCounterEXT","signatures":[["query","target"]]},{"name":"deleteQueryEXT","signatures":[["query"]]},{"name":"isQueryEXT","signatures":[["query"]]},{"name":"beginQueryEXT","signatures":[["target","query"]]},{"name":"endQueryEXT","signatures":[["target"]]},{"name":"getQueryEXT","signatures":[["target","pname"]]},{"name":"getQueryObjectEXT","signatures":[["query","pname"]]},{"name":"deleteVertexArrayOES","signatures":[["?arrayObject"]]},{"name":"isVertexArrayOES","signatures":[["?arrayObject"]]},{"name":"bindVertexArrayOES","signatures":[["?arrayObject"]]},{"name":"getTranslatedShaderSource","signatures":[["shader"]]},{"name":"drawBuffersWEBGL","signatures":[["buffers"]]},{"name":"multiDrawArraysInstancedWEBGL","signatures":[["mode","firstsList","firstsOffset","countsList","countsOffset","instanceCountsList","instanceCountsOffset","drawcount"]]},{"name":"multiDrawElementsInstancedWEBGL","signatures":[["mode","countsList","countsOffset","type","offsetsList","offsetsOffset","instanceCountsList","instanceCountsOffset","drawcount"]]},{"name":"multiDrawArraysWEBGL","signatures":[["mode","firstsList","firstsOffset","countsList","countsOffset","drawcount"]]},{"name":"multiDrawElementsWEBGL","signatures":[["mode","countsList","countsOffset","type","offsetsList","offsetsOffset","drawcount"]]},{"name":"framebufferTextureMultiviewWEBGL","signatures":[["target","attachment","texture","level","baseViewIndex","numViews"]]},{"name":"activeTexture","signatures":[["texture"]]},{"name":"attachShader","signatures":[["program","shader"]]},{"name":"bindAttribLocation","signatures":[["program","index","name"]]},{"name":"bindBuffer","signatures":[["target","buffer"]]},{"name":"bindFramebuffer","signatures":[["target","framebuffer"]]},{"name":"bindRenderbuffer","signatures":[["target","renderbuffer"]]},{"name":"bindTexture","signatures":[["target","texture"]]},{"name":"blendColor","signatures":[["red","green","blue","alpha"]]},{"name":"blendEquation","signatures":[["mode"]]},{"name":"blendEquationSeparate","signatures":[["modeRGB","modeAlpha"]]},{"name":"blendFunc","signatures":[["sfactor","dfactor"]]},{"name":"blendFuncSeparate","signatures":[["srcRGB","dstRGB","srcAlpha","dstAlpha"]]},{"name":"bufferData","signatures":[["target","size","usage"],["target","data","usage"]],"receiver":"WebGLRenderingContextBase"},{"name":"bufferData","signatures":[["target","srcData","usage","srcOffset","?length"]],"receiver":"WebGL2RenderingContextBase"},{"name":"bufferSubData","signatures":[["target","offset","data"]],"receiver":"WebGLRenderingContextBase"},{"name":"bufferSubData","signatures":[["target","dstByteOffset","srcData","srcOffset","?length"]],"receiver":"WebGL2RenderingContextBase"},{"name":"checkFramebufferStatus","signatures":[["target"]]},{"name":"clearColor","signatures":[["red","green","blue","alpha"]]},{"name":"clearDepth","signatures":[["depth"]]},{"name":"clearStencil","signatures":[["s"]]},{"name":"colorMask","signatures":[["red","green","blue","alpha"]]},{"name":"compileShader","signatures":[["shader"]]},{"name":"compressedTexImage2D","signatures":[["target","level","internalformat","width","height","border","data"]],"receiver":"WebGLRenderingContextBase"},{"name":"compressedTexImage2D","signatures":[["target","level","internalformat","width","height","border","imageSize","offset"],["target","level","internalformat","width","height","border","data","srcOffset","?srcLengthOverride"]],"receiver":"WebGL2RenderingContextBase"},{"name":"compressedTexSubImage2D","signatures":[["target","level","xoffset","yoffset","width","height","format","data"]],"receiver":"WebGLRenderingContextBase"},{"name":"compressedTexSubImage2D","signatures":[["target","level","xoffset","yoffset","width","height","format","imageSize","offset"],["target","level","xoffset","yoffset","width","height","format","data","srcOffset","?srcLengthOverride"]],"receiver":"WebGL2RenderingContextBase"},{"name":"copyTexImage2D","signatures":[["target","level","internalformat","x","y","width","height","border"]]},{"name":"copyTexSubImage2D","signatures":[["target","level","xoffset","yoffset","x","y","width","height"]]},{"name":"createShader","signatures":[["type"]]},{"name":"cullFace","signatures":[["mode"]]},{"name":"deleteBuffer","signatures":[["buffer"]]},{"name":"deleteFramebuffer","signatures":[["framebuffer"]]},{"name":"deleteProgram","signatures":[["program"]]},{"name":"deleteRenderbuffer","signatures":[["renderbuffer"]]},{"name":"deleteShader","signatures":[["shader"]]},{"name":"deleteTexture","signatures":[["texture"]]},{"name":"depthFunc","signatures":[["func"]]},{"name":"depthMask","signatures":[["flag"]]},{"name":"depthRange","signatures":[["zNear","zFar"]]},{"name":"detachShader","signatures":[["program","shader"]]},{"name":"disableVertexAttribArray","signatures":[["index"]]},{"name":"drawArrays","signatures":[["mode","first","count"]]},{"name":"drawElements","signatures":[["mode","count","type","offset"]]},{"name":"enableVertexAttribArray","signatures":[["index"]]},{"name":"framebufferRenderbuffer","signatures":[["target","attachment","renderbuffertarget","renderbuffer"]]},{"name":"framebufferTexture2D","signatures":[["target","attachment","textarget","texture","level"]]},{"name":"frontFace","signatures":[["mode"]]},{"name":"generateMipmap","signatures":[["target"]]},{"name":"getActiveAttrib","signatures":[["program","index"]]},{"name":"getActiveUniform","signatures":[["program","index"]]},{"name":"getAttachedShaders","signatures":[["program"]]},{"name":"getAttribLocation","signatures":[["program","name"]]},{"name":"getBufferParameter","signatures":[["target","pname"]]},{"name":"getExtension","signatures":[["name"]]},{"name":"getFramebufferAttachmentParameter","signatures":[["target","attachment","pname"]]},{"name":"getProgramParameter","signatures":[["program","pname"]]},{"name":"getProgramInfoLog","signatures":[["program"]]},{"name":"getRenderbufferParameter","signatures":[["target","pname"]]},{"name":"getShaderParameter","signatures":[["shader","pname"]]},{"name":"getShaderInfoLog","signatures":[["shader"]]},{"name":"getShaderPrecisionFormat","signatures":[["shadertype","precisiontype"]]},{"name":"getShaderSource","signatures":[["shader"]]},{"name":"getTexParameter","signatures":[["target","pname"]]},{"name":"getUniform","signatures":[["program","location"]]},{"name":"getUniformLocation","signatures":[["program","name"]]},{"name":"getVertexAttrib","signatures":[["index","pname"]]},{"name":"getVertexAttribOffset","signatures":[["index","pname"]]},{"name":"hint","signatures":[["target","mode"]]},{"name":"isBuffer","signatures":[["buffer"]]},{"name":"isEnabled","signatures":[["cap"]]},{"name":"isFramebuffer","signatures":[["framebuffer"]]},{"name":"isProgram","signatures":[["program"]]},{"name":"isRenderbuffer","signatures":[["renderbuffer"]]},{"name":"isShader","signatures":[["shader"]]},{"name":"isTexture","signatures":[["texture"]]},{"name":"lineWidth","signatures":[["width"]]},{"name":"linkProgram","signatures":[["program"]]},{"name":"pixelStorei","signatures":[["pname","param"]]},{"name":"polygonOffset","signatures":[["factor","units"]]},{"name":"readPixels","signatures":[["x","y","width","height","format","type","pixels"]],"receiver":"WebGLRenderingContextBase"},{"name":"readPixels","signatures":[["x","y","width","height","format","type","offset"],["x","y","width","height","format","type","dstData","offset"]],"receiver":"WebGL2RenderingContextBase"},{"name":"renderbufferStorage","signatures":[["target","internalformat","width","height"]]},{"name":"sampleCoverage","signatures":[["value","invert"]]},{"name":"scissor","signatures":[["x","y","width","height"]]},{"name":"shaderSource","signatures":[["shader","string"]]},{"name":"stencilFunc","signatures":[["func","ref","mask"]]},{"name":"stencilFuncSeparate","signatures":[["face","func","ref","mask"]]},{"name":"stencilMask","signatures":[["mask"]]},{"name":"stencilMaskSeparate","signatures":[["face","mask"]]},{"name":"stencilOp","signatures":[["fail","zfail","zpass"]]},{"name":"stencilOpSeparate","signatures":[["face","fail","zfail","zpass"]]},{"name":"texParameterf","signatures":[["target","pname","param"]]},{"name":"texParameteri","signatures":[["target","pname","param"]]},{"name":"texImage2D","signatures":[["target","level","internalformat","format","type","pixels"],["target","level","internalformat","format","type","image"],["target","level","internalformat","format","type","canvas"],["target","level","internalformat","format","type","offscreenCanvas"],["target","level","internalformat","format","type","video"],["target","level","internalformat","format","type","bitmap"],["target","level","internalformat","width","height","border","format","type","pixels"]],"receiver":"WebGLRenderingContextBase"},{"name":"texImage2D","signatures":[["target","level","internalformat","width","height","border","format","type","offset"],["target","level","internalformat","width","height","border","format","type","data"],["target","level","internalformat","width","height","border","format","type","image"],["target","level","internalformat","width","height","border","format","type","canvas"],["target","level","internalformat","width","height","border","format","type","offscreenCanvas"],["target","level","internalformat","width","height","border","format","type","video"],["target","level","internalformat","width","height","border","format","type","bitmap"],["target","level","internalformat","width","height","border","format","type","srcData","srcOffset"]],"receiver":"WebGL2RenderingContextBase"},{"name":"texSubImage2D","signatures":[["target","level","xoffset","yoffset","format","type","pixels"],["target","level","xoffset","yoffset","format","type","image"],["target","level","xoffset","yoffset","format","type","canvas"],["target","level","xoffset","yoffset","format","type","offscreenCanvas"],["target","level","xoffset","yoffset","format","type","video"],["target","level","xoffset","yoffset","format","type","bitmap"],["target","level","xoffset","yoffset","width","height","format","type","pixels"]],"receiver":"WebGLRenderingContextBase"},{"name":"texSubImage2D","signatures":[["target","level","xoffset","yoffset","width","height","format","type","offset"],["target","level","xoffset","yoffset","width","height","format","type","data"],["target","level","xoffset","yoffset","width","height","format","type","image"],["target","level","xoffset","yoffset","width","height","format","type","canvas"],["target","level","xoffset","yoffset","width","height","format","type","offscreenCanvas"],["target","level","xoffset","yoffset","width","height","format","type","video"],["target","level","xoffset","yoffset","width","height","format","type","bitmap"],["target","level","xoffset","yoffset","width","height","format","type","srcData","srcOffset"]],"receiver":"WebGL2RenderingContextBase"},{"name":"uniform1f","signatures":[["location","x"]]},{"name":"uniform1fv","signatures":[["location","v"]],"receiver":"WebGLRenderingContextBase"},{"name":"uniform1fv","signatures":[["location","v","srcOffset","?srcLength"]],"receiver":"WebGL2RenderingContextBase"},{"name":"uniform1i","signatures":[["location","x"]]},{"name":"uniform1iv","signatures":[["location","v"]],"receiver":"WebGLRenderingContextBase"},{"name":"uniform1iv","signatures":[["location","v","srcOffset","?srcLength"]],"receiver":"WebGL2RenderingContextBase"},{"name":"uniform2f","signatures":[["location","x","y"]]},{"name":"uniform2fv","signatures":[["location","v"]],"receiver":"WebGLRenderingContextBase"},{"name":"uniform2fv","signatures":[["location","v","srcOffset","?srcLength"]],"receiver":"WebGL2RenderingContextBase"},{"name":"uniform2i","signatures":[["location","x","y"]]},{"name":"uniform2iv","signatures":[["location","v"]],"receiver":"WebGLRenderingContextBase"},{"name":"uniform2iv","signatures":[["location","v","srcOffset","?srcLength"]],"receiver":"WebGL2RenderingContextBase"},{"name":"uniform3f","signatures":[["location","x","y","z"]]},{"name":"uniform3fv","signatures":[["location","v"]],"receiver":"WebGLRenderingContextBase"},{"name":"uniform3fv","signatures":[["location","v","srcOffset","?srcLength"]],"receiver":"WebGL2RenderingContextBase"},{"name":"uniform3i","signatures":[["location","x","y","z"]]},{"name":"uniform3iv","signatures":[["location","v"]],"receiver":"WebGLRenderingContextBase"},{"name":"uniform3iv","signatures":[["location","v","srcOffset","?srcLength"]],"receiver":"WebGL2RenderingContextBase"},{"name":"uniform4f","signatures":[["location","x","y","z","w"]]},{"name":"uniform4fv","signatures":[["location","v"]],"receiver":"WebGLRenderingContextBase"},{"name":"uniform4fv","signatures":[["location","v","srcOffset","?srcLength"]],"receiver":"WebGL2RenderingContextBase"},{"name":"uniform4i","signatures":[["location","x","y","z","w"]]},{"name":"uniform4iv","signatures":[["location","v"]],"receiver":"WebGLRenderingContextBase"},{"name":"uniform4iv","signatures":[["location","v","srcOffset","?srcLength"]],"receiver":"WebGL2RenderingContextBase"},{"name":"uniformMatrix2fv","signatures":[["location","transpose","array"]],"receiver":"WebGLRenderingContextBase"},{"name":"uniformMatrix2fv","signatures":[["location","transpose","array","srcOffset","?srcLength"]],"receiver":"WebGL2RenderingContextBase"},{"name":"uniformMatrix3fv","signatures":[["location","transpose","array"]],"receiver":"WebGLRenderingContextBase"},{"name":"uniformMatrix3fv","signatures":[["location","transpose","array","srcOffset","?srcLength"]],"receiver":"WebGL2RenderingContextBase"},{"name":"uniformMatrix4fv","signatures":[["location","transpose","array"]],"receiver":"WebGLRenderingContextBase"},{"name":"uniformMatrix4fv","signatures":[["location","transpose","array","srcOffset","?srcLength"]],"receiver":"WebGL2RenderingContextBase"},{"name":"useProgram","signatures":[["program"]]},{"name":"validateProgram","signatures":[["program"]]},{"name":"vertexAttrib1f","signatures":[["indx","x"]]},{"name":"vertexAttrib1fv","signatures":[["indx","values"]]},{"name":"vertexAttrib2f","signatures":[["indx","x","y"]]},{"name":"vertexAttrib2fv","signatures":[["indx","values"]]},{"name":"vertexAttrib3f","signatures":[["indx","x","y","z"]]},{"name":"vertexAttrib3fv","signatures":[["indx","values"]]},{"name":"vertexAttrib4f","signatures":[["indx","x","y","z","w"]]},{"name":"vertexAttrib4fv","signatures":[["indx","values"]]},{"name":"vertexAttribPointer","signatures":[["indx","size","type","normalized","stride","offset"]]},{"name":"viewport","signatures":[["x","y","width","height"]]},{"name":"dispatchCompute","signatures":[["numGroupsX","numGroupsY","numGroupsZ"]]},{"name":"getProgramInterfaceParameter","signatures":[["program","programInterface","pname"]]},{"name":"getProgramResourceIndex","signatures":[["program","programInterface","name"]]},{"name":"getProgramResourceName","signatures":[["program","programInterface","index"]]},{"name":"getProgramResource","signatures":[["program","programInterface","index","props"]]},{"name":"getProgramResourceLocation","signatures":[["program","programInterface","name"]]},{"name":"bindImageTexture","signatures":[["unit","texture","level","layered","layer","access","format"]]},{"name":"memoryBarrier","signatures":[["barriers"]]},{"name":"memoryBarrierByRegion","signatures":[["barriers"]]},{"name":"copyBufferSubData","signatures":[["readTarget","writeTarget","readOffset","writeOffset","size"]]},{"name":"getBufferSubData","signatures":[["target","srcByteOffset","dstData","?dstOffset","?length"]]},{"name":"blitFramebuffer","signatures":[["srcX0","srcY0","srcX1","srcY1","dstX0","dstY0","dstX1","dstY1","mask","filter"]]},{"name":"framebufferTextureLayer","signatures":[["target","attachment","texture","level","layer"]]},{"name":"getInternalformatParameter","signatures":[["target","internalformat","pname"]]},{"name":"invalidateFramebuffer","signatures":[["target","attachments"]]},{"name":"invalidateSubFramebuffer","signatures":[["target","attachments","x","y","width","height"]]},{"name":"readBuffer","signatures":[["mode"]]},{"name":"renderbufferStorageMultisample","signatures":[["target","samples","internalformat","width","height"]]},{"name":"texStorage2D","signatures":[["target","levels","internalformat","width","height"]]},{"name":"texStorage3D","signatures":[["target","levels","internalformat","width","height","depth"]]},{"name":"texImage3D","signatures":[["target","level","internalformat","width","height","depth","border","format","type","offset"],["target","level","internalformat","width","height","depth","border","format","type","data"],["target","level","internalformat","width","height","depth","border","format","type","image"],["target","level","internalformat","width","height","depth","border","format","type","canvas"],["target","level","internalformat","width","height","depth","border","format","type","offscreenCanvas"],["target","level","internalformat","width","height","depth","border","format","type","video"],["target","level","internalformat","width","height","depth","border","format","type","bitmap"],["target","level","internalformat","width","height","depth","border","format","type","pixels","?srcOffset"]]},{"name":"texSubImage3D","signatures":[["target","level","xoffset","yoffset","zoffset","width","height","depth","format","type","offset"],["target","level","xoffset","yoffset","zoffset","width","height","depth","format","type","data"],["target","level","xoffset","yoffset","zoffset","width","height","depth","format","type","image"],["target","level","xoffset","yoffset","zoffset","width","height","depth","format","type","canvas"],["target","level","xoffset","yoffset","zoffset","width","height","depth","format","type","offscreenCanvas"],["target","level","xoffset","yoffset","zoffset","width","height","depth","format","type","video"],["target","level","xoffset","yoffset","zoffset","width","height","depth","format","type","bitmap"],["target","level","xoffset","yoffset","zoffset","width","height","depth","format","type","pixels","?srcOffset"]]},{"name":"copyTexSubImage3D","signatures":[["target","level","xoffset","yoffset","zoffset","x","y","width","height"]]},{"name":"compressedTexImage3D","signatures":[["target","level","internalformat","width","height","depth","border","imageSize","offset"],["target","level","internalformat","width","height","depth","border","data","?srcOffset","?srcLengthOverride"]]},{"name":"compressedTexSubImage3D","signatures":[["target","level","xoffset","yoffset","zoffset","width","height","depth","format","imageSize","offset"],["target","level","xoffset","yoffset","zoffset","width","height","depth","format","data","?srcOffset","?srcLengthOverride"]]},{"name":"getFragDataLocation","signatures":[["program","name"]]},{"name":"uniform1ui","signatures":[["location","v0"]]},{"name":"uniform2ui","signatures":[["location","v0","v1"]]},{"name":"uniform3ui","signatures":[["location","v0","v1","v2"]]},{"name":"uniform4ui","signatures":[["location","v0","v1","v2","v3"]]},{"name":"uniform1uiv","signatures":[["location","v","?srcOffset","?srcLength"]]},{"name":"uniform2uiv","signatures":[["location","v","?srcOffset","?srcLength"]]},{"name":"uniform3uiv","signatures":[["location","v","?srcOffset","?srcLength"]]},{"name":"uniform4uiv","signatures":[["location","v","?srcOffset","?srcLength"]]},{"name":"uniformMatrix2x3fv","signatures":[["location","transpose","value","?srcOffset","?srcLength"]]},{"name":"uniformMatrix3x2fv","signatures":[["location","transpose","value","?srcOffset","?srcLength"]]},{"name":"uniformMatrix2x4fv","signatures":[["location","transpose","value","?srcOffset","?srcLength"]]},{"name":"uniformMatrix4x2fv","signatures":[["location","transpose","value","?srcOffset","?srcLength"]]},{"name":"uniformMatrix3x4fv","signatures":[["location","transpose","value","?srcOffset","?srcLength"]]},{"name":"uniformMatrix4x3fv","signatures":[["location","transpose","value","?srcOffset","?srcLength"]]},{"name":"vertexAttribI4i","signatures":[["index","x","y","z","w"]]},{"name":"vertexAttribI4iv","signatures":[["index","v"]]},{"name":"vertexAttribI4ui","signatures":[["index","x","y","z","w"]]},{"name":"vertexAttribI4uiv","signatures":[["index","v"]]},{"name":"vertexAttribIPointer","signatures":[["index","size","type","stride","offset"]]},{"name":"vertexAttribDivisor","signatures":[["index","divisor"]]},{"name":"drawArraysInstanced","signatures":[["mode","first","count","instanceCount"]]},{"name":"drawElementsInstanced","signatures":[["mode","count","type","offset","instanceCount"]]},{"name":"drawRangeElements","signatures":[["mode","start","end","count","type","offset"]]},{"name":"drawBuffers","signatures":[["buffers"]]},{"name":"clearBufferiv","signatures":[["buffer","drawbuffer","value","?srcOffset"]]},{"name":"clearBufferuiv","signatures":[["buffer","drawbuffer","value","?srcOffset"]]},{"name":"clearBufferfv","signatures":[["buffer","drawbuffer","value","?srcOffset"]]},{"name":"clearBufferfi","signatures":[["buffer","drawbuffer","depth","stencil"]]},{"name":"deleteQuery","signatures":[["query"]]},{"name":"isQuery","signatures":[["query"]]},{"name":"beginQuery","signatures":[["target","query"]]},{"name":"endQuery","signatures":[["target"]]},{"name":"getQuery","signatures":[["target","pname"]]},{"name":"getQueryParameter","signatures":[["query","pname"]]},{"name":"deleteSampler","signatures":[["sampler"]]},{"name":"isSampler","signatures":[["sampler"]]},{"name":"bindSampler","signatures":[["unit","sampler"]]},{"name":"samplerParameteri","signatures":[["sampler","pname","param"]]},{"name":"samplerParameterf","signatures":[["sampler","pname","param"]]},{"name":"getSamplerParameter","signatures":[["sampler","pname"]]},{"name":"fenceSync","signatures":[["condition","flags"]]},{"name":"isSync","signatures":[["sync"]]},{"name":"deleteSync","signatures":[["sync"]]},{"name":"clientWaitSync","signatures":[["sync","flags","timeout"]]},{"name":"waitSync","signatures":[["sync","flags","timeout"]]},{"name":"getSyncParameter","signatures":[["sync","pname"]]},{"name":"deleteTransformFeedback","signatures":[["feedback"]]},{"name":"isTransformFeedback","signatures":[["feedback"]]},{"name":"bindTransformFeedback","signatures":[["target","feedback"]]},{"name":"beginTransformFeedback","signatures":[["primitiveMode"]]},{"name":"transformFeedbackVaryings","signatures":[["program","varyings","bufferMode"]]},{"name":"getTransformFeedbackVarying","signatures":[["program","index"]]},{"name":"bindBufferBase","signatures":[["target","index","buffer"]]},{"name":"bindBufferRange","signatures":[["target","index","buffer","offset","size"]]},{"name":"getIndexedParameter","signatures":[["target","index"]]},{"name":"getUniformIndices","signatures":[["program","uniformNames"]]},{"name":"getActiveUniforms","signatures":[["program","uniformIndices","pname"]]},{"name":"getUniformBlockIndex","signatures":[["program","uniformBlockName"]]},{"name":"getActiveUniformBlockParameter","signatures":[["program","uniformBlockIndex","pname"]]},{"name":"getActiveUniformBlockName","signatures":[["program","uniformBlockIndex"]]},{"name":"uniformBlockBinding","signatures":[["program","uniformBlockIndex","uniformBlockBinding"]]},{"name":"deleteVertexArray","signatures":[["vertexArray"]]},{"name":"isVertexArray","signatures":[["vertexArray"]]},{"name":"bindVertexArray","signatures":[["vertexArray"]]},{"name":"getAdapter","signatures":[["descriptor"]]},{"name":"requestMIDIAccess","signatures":[["?options"]]},{"name":"canShare","signatures":[["?data"]]},{"name":"share","signatures":[["?data"]]},{"name":"CloseEvent","signatures":[["type","?eventInitDict"]]},{"name":"WebSocket","signatures":[["url","?protocols"]]},{"name":"USBAlternateInterface","signatures":[["deviceInterface","alternateSetting"]]},{"name":"USBConfiguration","signatures":[["device","configurationValue"]]},{"name":"USBConnectionEvent","signatures":[["type","eventInitDict"]]},{"name":"selectConfiguration","signatures":[["configurationValue"]]},{"name":"claimInterface","signatures":[["interfaceNumber"]]},{"name":"releaseInterface","signatures":[["interfaceNumber"]]},{"name":"selectAlternateInterface","signatures":[["interfaceNumber","alternateSetting"]]},{"name":"controlTransferIn","signatures":[["setup","length"]]},{"name":"controlTransferOut","signatures":[["setup","?data"]]},{"name":"clearHalt","signatures":[["direction","endpointNumber"]]},{"name":"transferIn","signatures":[["endpointNumber","length"]]},{"name":"transferOut","signatures":[["endpointNumber","data"]]},{"name":"isochronousTransferIn","signatures":[["endpointNumber","packetLengths"]]},{"name":"isochronousTransferOut","signatures":[["endpointNumber","data","packetLengths"]]},{"name":"USBEndpoint","signatures":[["alternate","endpointNumber","direction"]]},{"name":"USBInTransferResult","signatures":[["status","?data"]]},{"name":"USBInterface","signatures":[["configuration","interfaceNumber"]]},{"name":"USBIsochronousInTransferPacket","signatures":[["status","?data"]]},{"name":"USBIsochronousInTransferResult","signatures":[["packets","?data"]]},{"name":"USBIsochronousOutTransferPacket","signatures":[["status","?bytesWritten"]]},{"name":"USBIsochronousOutTransferResult","signatures":[["packets"]]},{"name":"USBOutTransferResult","signatures":[["status","?bytesWritten"]]},{"name":"getViewerPose","signatures":[["referenceSpace"]]},{"name":"getInputPose","signatures":[["inputSource","referenceSpace"]]},{"name":"XRInputSourceEvent","signatures":[["type","eventInitDict"]]},{"name":"XRRigidTransform","signatures":[["?position","?orientation"]]},{"name":"XRSessionEvent","signatures":[["type","eventInitDict"]]},{"name":"updateRenderState","signatures":[["init"]]},{"name":"requestReferenceSpace","signatures":[["options"]]},{"name":"requestHitTest","signatures":[["ray","space"]]},{"name":"getTransformTo","signatures":[["other"]]},{"name":"XRWebGLLayer","signatures":[["session","context","?layerInit"]]},{"name":"getViewport","signatures":[["view"]]},{"name":"requestViewportScaling","signatures":[["viewportScaleFactor"]]},{"name":"getNativeFramebufferScaleFactor","signatures":[["session"]]},{"name":"supportsSessionMode","signatures":[["mode"]]},{"name":"requestSession","signatures":[["?options"]]},{"name":"assert","signatures":[["?condition","...data"]]},{"name":"debug","signatures":[["...data"]]},{"name":"dir","signatures":[["item","?options"]]},{"name":"dirxml","signatures":[["...data"]]},{"name":"group","signatures":[["...data"]]},{"name":"groupCollapsed","signatures":[["...data"]]},{"name":"info","signatures":[["...data"]]},{"name":"profile","signatures":[["?title"]]},{"name":"profileEnd","signatures":[["?title"]]},{"name":"table","signatures":[["...tabularData"]]},{"name":"time","signatures":[["?label"]]},{"name":"timeEnd","signatures":[["?label"]]},{"name":"timeStamp","signatures":[["?name"]]},{"name":"trace","signatures":[["...data"]]},{"name":"warn","signatures":[["...data"]]}];
\ No newline at end of file
diff --git a/third_party/blink/renderer/devtools/front_end/perf_ui/LineLevelProfile.js b/third_party/blink/renderer/devtools/front_end/perf_ui/LineLevelProfile.js
index 831a97a..50700e5 100644
--- a/third_party/blink/renderer/devtools/front_end/perf_ui/LineLevelProfile.js
+++ b/third_party/blink/renderer/devtools/front_end/perf_ui/LineLevelProfile.js
@@ -1,15 +1,21 @@
 // Copyright 2016 The Chromium Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
-/**
- * @unrestricted
- */
+
 PerfUI.LineLevelProfile = class {
   constructor() {
     this._locationPool = new Bindings.LiveLocationPool();
+    this._updateTimer = null;
     this.reset();
   }
 
+  reset() {
+    // The second map uses string keys for script URLs and numbers for scriptId.
+    /** @type {!Map<?SDK.Target, !Map<string|number, !Map<number, number>>>} */
+    this._lineData = new Map();
+    this._scheduleUpdate();
+  }
+
   /**
    * @return {!PerfUI.LineLevelProfile}
    */
@@ -23,6 +29,12 @@
    * @param {!SDK.CPUProfileDataModel} profile
    */
   _appendLegacyCPUProfile(profile) {
+    const target = profile.target();
+    let dataByTarget = this._lineData.get(target);
+    if (!dataByTarget) {
+      dataByTarget = new Map();
+      this._lineData.set(target, dataByTarget);
+    }
     const nodesToGo = [profile.profileHead];
     const sampleDuration = (profile.profileEndTime - profile.profileStartTime) / profile.totalHitCount;
     while (nodesToGo.length) {
@@ -32,10 +44,10 @@
         nodesToGo.push(node);
         if (!node.url || !node.positionTicks)
           continue;
-        let fileInfo = this._files.get(node.url);
+        let fileInfo = dataByTarget.get(node.url);
         if (!fileInfo) {
           fileInfo = new Map();
-          this._files.set(node.url, fileInfo);
+          dataByTarget.set(node.url, fileInfo);
         }
         for (let j = 0; j < node.positionTicks.length; ++j) {
           const lineInfo = node.positionTicks[j];
@@ -53,29 +65,31 @@
   appendCPUProfile(profile) {
     if (!profile.lines) {
       this._appendLegacyCPUProfile(profile);
-    } else {
-      for (let i = 1; i < profile.samples.length; ++i) {
-        const line = profile.lines[i];
-        if (!line)
-          continue;
-        const node = profile.nodeByIndex(i);
-        if (!node.url)
-          continue;
-        let fileInfo = this._files.get(node.url);
-        if (!fileInfo) {
-          fileInfo = new Map();
-          this._files.set(node.url, fileInfo);
-        }
-        const time = profile.timestamps[i] - profile.timestamps[i - 1];
-        fileInfo.set(line, (fileInfo.get(line) || 0) + time);
-      }
+      this._scheduleUpdate();
+      return;
     }
-    this._scheduleUpdate();
-  }
-
-  reset() {
-    /** @type {!Map<string, !Map<number, number>>} */
-    this._files = new Map();
+    const target = profile.target();
+    let dataByTarget = this._lineData.get(target);
+    if (!dataByTarget) {
+      dataByTarget = new Map();
+      this._lineData.set(target, dataByTarget);
+    }
+    for (let i = 1; i < profile.samples.length; ++i) {
+      const line = profile.lines[i];
+      if (!line)
+        continue;
+      const node = profile.nodeByIndex(i);
+      const scriptIdOrUrl = node.scriptId || node.url;
+      if (!scriptIdOrUrl)
+        continue;
+      let dataByScript = dataByTarget.get(scriptIdOrUrl);
+      if (!dataByScript) {
+        dataByScript = new Map();
+        dataByTarget.set(scriptIdOrUrl, dataByScript);
+      }
+      const time = profile.timestamps[i] - profile.timestamps[i - 1];
+      dataByScript.set(line, (dataByScript.get(line) || 0) + time);
+    }
     this._scheduleUpdate();
   }
 
@@ -89,33 +103,41 @@
   }
 
   _doUpdate() {
-    // TODO(alph): use scriptId instead of urls for the target.
     this._locationPool.disposeAll();
     Workspace.workspace.uiSourceCodes().forEach(
         uiSourceCode => uiSourceCode.removeDecorationsForType(PerfUI.LineLevelProfile.LineDecorator.type));
-    for (const fileInfo of this._files) {
-      const url = /** @type {string} */ (fileInfo[0]);
-      const uiSourceCode = Workspace.workspace.uiSourceCodeForURL(url);
-      if (!uiSourceCode)
-        continue;
-      const target = Bindings.NetworkProject.targetForUISourceCode(uiSourceCode) || SDK.targetManager.mainTarget();
+    for (const targetToScript of this._lineData) {
+      const target = /** @type {?SDK.Target} */ (targetToScript[0]);
       const debuggerModel = target ? target.model(SDK.DebuggerModel) : null;
-      if (!debuggerModel)
-        continue;
-      for (const lineInfo of fileInfo[1]) {
-        const line = lineInfo[0] - 1;
-        const time = lineInfo[1];
-        const rawLocation = debuggerModel.createRawLocationByURL(url, line, 0);
-        if (rawLocation)
-          new PerfUI.LineLevelProfile.Presentation(rawLocation, time, this._locationPool);
-        else if (uiSourceCode)
-          uiSourceCode.addLineDecoration(line, PerfUI.LineLevelProfile.LineDecorator.type, time);
+      const scriptToLineMap = /** @type {!Map<string|number, !Map<number, number>>} */ (targetToScript[1]);
+      for (const scriptToLine of scriptToLineMap) {
+        const scriptIdOrUrl = /** @type {string|number} */ (scriptToLine[0]);
+        const lineToDataMap = /** @type {!Map<number, number>} */ (scriptToLine[1]);
+        // debuggerModel is null when the profile is loaded from file.
+        // Try to get UISourceCode by the URL in this case.
+        const uiSourceCode = !debuggerModel && typeof scriptIdOrUrl === 'string' ?
+            Workspace.workspace.uiSourceCodeForURL(scriptIdOrUrl) :
+            null;
+        if (!debuggerModel && !uiSourceCode)
+          continue;
+        for (const lineToData of lineToDataMap) {
+          const line = /** @type {number} */ (lineToData[0]) - 1;
+          const data = /** @type {number} */ (lineToData[1]);
+          if (uiSourceCode) {
+            uiSourceCode.addLineDecoration(line, PerfUI.LineLevelProfile.LineDecorator.type, data);
+            continue;
+          }
+          const rawLocation = typeof scriptIdOrUrl === 'string' ?
+              debuggerModel.createRawLocationByURL(scriptIdOrUrl, line, 0) :
+              debuggerModel.createRawLocationByScriptId(String(scriptIdOrUrl), line, 0);
+          if (rawLocation)
+            new PerfUI.LineLevelProfile.Presentation(rawLocation, data, this._locationPool);
+        }
       }
     }
   }
 };
 
-
 /**
  * @unrestricted
  */
diff --git a/third_party/blink/renderer/devtools/front_end/profiler/CPUProfileView.js b/third_party/blink/renderer/devtools/front_end/profiler/CPUProfileView.js
index a901ccf..a3e73271 100644
--- a/third_party/blink/renderer/devtools/front_end/profiler/CPUProfileView.js
+++ b/third_party/blink/renderer/devtools/front_end/profiler/CPUProfileView.js
@@ -242,7 +242,8 @@
    * @param {!Protocol.Profiler.Profile} profile
    */
   setProfile(profile) {
-    this._profileModel = new SDK.CPUProfileDataModel(profile);
+    const target = this._cpuProfilerModel && this._cpuProfilerModel.target() || null;
+    this._profileModel = new SDK.CPUProfileDataModel(profile, target);
   }
 };
 
diff --git a/third_party/blink/renderer/devtools/front_end/sdk/CPUProfileDataModel.js b/third_party/blink/renderer/devtools/front_end/sdk/CPUProfileDataModel.js
index f168267..20d6058c 100644
--- a/third_party/blink/renderer/devtools/front_end/sdk/CPUProfileDataModel.js
+++ b/third_party/blink/renderer/devtools/front_end/sdk/CPUProfileDataModel.js
@@ -33,9 +33,10 @@
 SDK.CPUProfileDataModel = class extends SDK.ProfileTreeModel {
   /**
    * @param {!Protocol.Profiler.Profile} profile
+   * @param {?SDK.Target} target
    */
-  constructor(profile) {
-    super();
+  constructor(profile, target) {
+    super(target);
     const isLegacyFormat = !!profile['head'];
     if (isLegacyFormat) {
       // Legacy format contains raw timestamps and start/stop times are in seconds.
diff --git a/third_party/blink/renderer/devtools/front_end/sdk/ProfileTreeModel.js b/third_party/blink/renderer/devtools/front_end/sdk/ProfileTreeModel.js
index 94a5d4c..4f646b2 100644
--- a/third_party/blink/renderer/devtools/front_end/sdk/ProfileTreeModel.js
+++ b/third_party/blink/renderer/devtools/front_end/sdk/ProfileTreeModel.js
@@ -66,6 +66,13 @@
  */
 SDK.ProfileTreeModel = class {
   /**
+   * @param {?SDK.Target=} target
+   */
+  constructor(target) {
+    this._target = target || null;
+  }
+
+  /**
    * @param {!SDK.ProfileNode} root
    * @protected
    */
@@ -117,4 +124,11 @@
     }
     return root.total;
   }
+
+  /**
+   * @return {?SDK.Target}
+   */
+  target() {
+    return this._target;
+  }
 };
diff --git a/third_party/blink/renderer/devtools/front_end/sdk/ResourceTreeModel.js b/third_party/blink/renderer/devtools/front_end/sdk/ResourceTreeModel.js
index 4f13075d..1f38cd9 100644
--- a/third_party/blink/renderer/devtools/front_end/sdk/ResourceTreeModel.js
+++ b/third_party/blink/renderer/devtools/front_end/sdk/ResourceTreeModel.js
@@ -419,6 +419,7 @@
   _executionContextComparator(a, b) {
     /**
      * @param {!SDK.ResourceTreeFrame} frame
+     * @return {!Array<!SDK.ResourceTreeFrame>}
      */
     function framePath(frame) {
       let currentFrame = frame;
diff --git a/third_party/blink/renderer/devtools/front_end/sdk/RuntimeModel.js b/third_party/blink/renderer/devtools/front_end/sdk/RuntimeModel.js
index f92c374..b37799397 100644
--- a/third_party/blink/renderer/devtools/front_end/sdk/RuntimeModel.js
+++ b/third_party/blink/renderer/devtools/front_end/sdk/RuntimeModel.js
@@ -708,9 +708,43 @@
       return 1;
     }
 
-    const weightDiff = targetWeight(a.target()) - targetWeight(b.target());
-    if (weightDiff)
-      return -weightDiff;
+    /**
+     * @param {!SDK.Target} target
+     * @return {!Array<!SDK.Target>}
+     */
+    function targetPath(target) {
+      let currentTarget = target;
+      const parents = [];
+      while (currentTarget) {
+        parents.push(currentTarget);
+        currentTarget = currentTarget.parentTarget();
+      }
+      return parents.reverse();
+    }
+
+    const tagetsA = targetPath(a.target());
+    const targetsB = targetPath(b.target());
+    let targetA;
+    let targetB;
+    for (let i = 0;; i++) {
+      if (!tagetsA[i] || !targetsB[i] || (tagetsA[i] !== targetsB[i])) {
+        targetA = tagetsA[i];
+        targetB = targetsB[i];
+        break;
+      }
+    }
+    if (!targetA && targetB)
+      return -1;
+
+    if (!targetB && targetA)
+      return 1;
+
+    if (targetA && targetB) {
+      const weightDiff = targetWeight(targetA) - targetWeight(targetB);
+      if (weightDiff)
+        return -weightDiff;
+      return targetA.id().localeCompare(targetB.id());
+    }
 
     // Main world context should always go first.
     if (a.isDefault)
diff --git a/third_party/blink/renderer/devtools/front_end/timeline_model/TimelineModel.js b/third_party/blink/renderer/devtools/front_end/timeline_model/TimelineModel.js
index c0a9a2c..d0665b7 100644
--- a/third_party/blink/renderer/devtools/front_end/timeline_model/TimelineModel.js
+++ b/third_party/blink/renderer/devtools/front_end/timeline_model/TimelineModel.js
@@ -420,18 +420,21 @@
   _extractCpuProfile(tracingModel, thread) {
     const events = thread.events();
     let cpuProfile;
+    let target = null;
 
     // Check for legacy CpuProfile event format first.
     let cpuProfileEvent = events.peekLast();
     if (cpuProfileEvent && cpuProfileEvent.name === TimelineModel.TimelineModel.RecordType.CpuProfile) {
       const eventData = cpuProfileEvent.args['data'];
       cpuProfile = /** @type {?Protocol.Profiler.Profile} */ (eventData && eventData['cpuProfile']);
+      target = this.targetByEvent(cpuProfileEvent);
     }
 
     if (!cpuProfile) {
       cpuProfileEvent = events.find(e => e.name === TimelineModel.TimelineModel.RecordType.Profile);
       if (!cpuProfileEvent)
         return null;
+      target = this.targetByEvent(cpuProfileEvent);
       const profileGroup = tracingModel.profileGroup(cpuProfileEvent);
       if (!profileGroup) {
         Common.console.error('Invalid CPU profile format.');
@@ -468,7 +471,7 @@
     }
 
     try {
-      const jsProfileModel = new SDK.CPUProfileDataModel(cpuProfile);
+      const jsProfileModel = new SDK.CPUProfileDataModel(cpuProfile, target);
       this._cpuProfiles.push(jsProfileModel);
       return jsProfileModel;
     } catch (e) {
diff --git a/third_party/blink/renderer/devtools/front_end/ui/SearchableView.js b/third_party/blink/renderer/devtools/front_end/ui/SearchableView.js
index f03af0f..b03b61f 100644
--- a/third_party/blink/renderer/devtools/front_end/ui/SearchableView.js
+++ b/third_party/blink/renderer/devtools/front_end/ui/SearchableView.js
@@ -374,7 +374,7 @@
    * @param {boolean=} isBackwardSearch
    */
   _jumpToNextSearchResult(isBackwardSearch) {
-    if (!this._currentQuery || !this._searchNavigationPrevElement.classList.contains('enabled'))
+    if (!this._currentQuery)
       return;
 
     if (isBackwardSearch)
diff --git a/third_party/blink/renderer/devtools/front_end/ui/TabbedPane.js b/third_party/blink/renderer/devtools/front_end/ui/TabbedPane.js
index f51f82d6..7e8514c 100644
--- a/third_party/blink/renderer/devtools/front_end/ui/TabbedPane.js
+++ b/third_party/blink/renderer/devtools/front_end/ui/TabbedPane.js
@@ -300,8 +300,8 @@
   }
 
   _viewHasFocus() {
-    if (this.visibleView)
-      return this.visibleView.hasFocus();
+    if (this.visibleView && this.visibleView.hasFocus())
+      return true;
     return this.contentElement === this.contentElement.getComponentRoot().activeElement;
   }
 
diff --git a/third_party/blink/renderer/devtools/scripts/javascript_natives/index.js b/third_party/blink/renderer/devtools/scripts/javascript_natives/index.js
index 349642b..2dd1ee7 100644
--- a/third_party/blink/renderer/devtools/scripts/javascript_natives/index.js
+++ b/third_party/blink/renderer/devtools/scripts/javascript_natives/index.js
@@ -178,7 +178,7 @@
     } else {
       for (const parent in methods[name]) {
         if (parent.endsWith('Constructor'))
-          functions.push({name, signatures: methods[name][parent], static: true, receiver: constructor});
+          functions.push({name, signatures: methods[name][parent], static: true, receiver: parent.substring(0, parent.length - 'Constructor'.length)});
         else
           functions.push({name, signatures: methods[name][parent], receiver: parent});
       }
diff --git a/third_party/blink/renderer/modules/animationworklet/animation_worklet_global_scope.cc b/third_party/blink/renderer/modules/animationworklet/animation_worklet_global_scope.cc
index e45132d..b1c46ed2 100644
--- a/third_party/blink/renderer/modules/animationworklet/animation_worklet_global_scope.cc
+++ b/third_party/blink/renderer/modules/animationworklet/animation_worklet_global_scope.cc
@@ -24,13 +24,13 @@
 
 namespace {
 
-void UpdateAnimation(Animator* animator,
-                     ScriptState* script_state,
+void UpdateAnimation(v8::Isolate* isolate,
+                     Animator* animator,
                      WorkletAnimationId id,
                      double current_time,
                      AnimationWorkletDispatcherOutput* result) {
   AnimationWorkletDispatcherOutput::AnimationState animation_output(id);
-  if (animator->Animate(script_state, current_time, &animation_output)) {
+  if (animator->Animate(isolate, current_time, &animation_output)) {
     result->animations.push_back(std::move(animation_output));
   }
 }
@@ -112,6 +112,7 @@
   DCHECK(IsContextThread());
 
   ScriptState* script_state = ScriptController()->GetScriptState();
+  v8::Isolate* isolate = script_state->GetIsolate();
   ScriptState::Scope scope(script_state);
 
   for (const auto& animation : input.added_and_updated_animations) {
@@ -123,7 +124,7 @@
     if (!animator || !predicate(animator))
       continue;
 
-    UpdateAnimation(animator, script_state, animation.worklet_animation_id,
+    UpdateAnimation(isolate, animator, animation.worklet_animation_id,
                     animation.current_time, output);
   }
 
@@ -134,7 +135,7 @@
     if (!animator || !predicate(animator))
       continue;
 
-    UpdateAnimation(animator, script_state, animation.worklet_animation_id,
+    UpdateAnimation(isolate, animator, animation.worklet_animation_id,
                     animation.current_time, output);
   }
 
diff --git a/third_party/blink/renderer/modules/animationworklet/animator.cc b/third_party/blink/renderer/modules/animationworklet/animator.cc
index d19a82b5a..c79ff47 100644
--- a/third_party/blink/renderer/modules/animationworklet/animator.cc
+++ b/third_party/blink/renderer/modules/animationworklet/animator.cc
@@ -31,11 +31,9 @@
 }
 
 bool Animator::Animate(
-    ScriptState* script_state,
+    v8::Isolate* isolate,
     double current_time,
     AnimationWorkletDispatcherOutput::AnimationState* output) {
-  v8::Isolate* isolate = script_state->GetIsolate();
-
   v8::Local<v8::Value> instance = instance_.NewLocal(isolate);
   if (IsUndefinedOrNull(instance))
     return false;
diff --git a/third_party/blink/renderer/modules/animationworklet/animator.h b/third_party/blink/renderer/modules/animationworklet/animator.h
index 1d29a39..478d765 100644
--- a/third_party/blink/renderer/modules/animationworklet/animator.h
+++ b/third_party/blink/renderer/modules/animationworklet/animator.h
@@ -17,7 +17,6 @@
 namespace blink {
 
 class AnimatorDefinition;
-class ScriptState;
 
 // Represents an animator instance. It owns the underlying |v8::Object| for the
 // instance and knows how to invoke the |animate| function on it.
@@ -36,9 +35,9 @@
   // Returns true if it successfully invoked animate callback in JS. It receives
   // latest state coming from |AnimationHost| as input and fills
   // the output state with new updates.
-  bool Animate(ScriptState*,
+  bool Animate(v8::Isolate* isolate,
                double current_time,
-               AnimationWorkletDispatcherOutput::AnimationState*);
+               AnimationWorkletDispatcherOutput::AnimationState* output);
   std::vector<base::Optional<TimeDelta>> GetLocalTimes() const;
   bool IsStateful() const;
 
diff --git a/third_party/blink/renderer/modules/device_orientation/device_motion_controller.cc b/third_party/blink/renderer/modules/device_orientation/device_motion_controller.cc
index f9e8906..1f386d5 100644
--- a/third_party/blink/renderer/modules/device_orientation/device_motion_controller.cc
+++ b/third_party/blink/renderer/modules/device_orientation/device_motion_controller.cc
@@ -46,7 +46,7 @@
   LocalFrame* frame = GetDocument().GetFrame();
   if (frame) {
     if (GetDocument().IsSecureContext()) {
-      UseCounter::Count(frame, WebFeature::kDeviceMotionSecureOrigin);
+      UseCounter::Count(GetDocument(), WebFeature::kDeviceMotionSecureOrigin);
     } else {
       Deprecation::CountDeprecation(frame,
                                     WebFeature::kDeviceMotionInsecureOrigin);
diff --git a/third_party/blink/renderer/modules/device_orientation/device_orientation_absolute_controller.cc b/third_party/blink/renderer/modules/device_orientation/device_orientation_absolute_controller.cc
index 28c5d1b..9191d83 100644
--- a/third_party/blink/renderer/modules/device_orientation/device_orientation_absolute_controller.cc
+++ b/third_party/blink/renderer/modules/device_orientation/device_orientation_absolute_controller.cc
@@ -42,7 +42,7 @@
   LocalFrame* frame = GetDocument().GetFrame();
   if (frame) {
     if (GetDocument().IsSecureContext()) {
-      UseCounter::Count(frame->GetDocument(),
+      UseCounter::Count(GetDocument(),
                         WebFeature::kDeviceOrientationAbsoluteSecureOrigin);
     } else {
       Deprecation::CountDeprecation(
diff --git a/third_party/blink/renderer/modules/device_orientation/device_orientation_controller.cc b/third_party/blink/renderer/modules/device_orientation/device_orientation_controller.cc
index eeec6f75..2c427ce86 100644
--- a/third_party/blink/renderer/modules/device_orientation/device_orientation_controller.cc
+++ b/third_party/blink/renderer/modules/device_orientation/device_orientation_controller.cc
@@ -57,7 +57,7 @@
   LocalFrame* frame = GetDocument().GetFrame();
   if (frame) {
     if (GetDocument().IsSecureContext()) {
-      UseCounter::Count(frame->GetDocument(),
+      UseCounter::Count(GetDocument(),
                         WebFeature::kDeviceOrientationSecureOrigin);
     } else {
       Deprecation::CountDeprecation(
diff --git a/third_party/blink/renderer/modules/eventsource/event_source.h b/third_party/blink/renderer/modules/eventsource/event_source.h
index f21e6652..815cd3e 100644
--- a/third_party/blink/renderer/modules/eventsource/event_source.h
+++ b/third_party/blink/renderer/modules/eventsource/event_source.h
@@ -74,7 +74,7 @@
   String url() const;
   bool withCredentials() const;
 
-  enum State : short { kConnecting = 0, kOpen = 1, kClosed = 2 };
+  enum State : int16_t { kConnecting = 0, kOpen = 1, kClosed = 2 };
 
   State readyState() const;
 
diff --git a/third_party/blink/renderer/modules/filesystem/dom_window_file_system.cc b/third_party/blink/renderer/modules/filesystem/dom_window_file_system.cc
index fdb1431..3da7c46e 100644
--- a/third_party/blink/renderer/modules/filesystem/dom_window_file_system.cc
+++ b/third_party/blink/renderer/modules/filesystem/dom_window_file_system.cc
@@ -199,7 +199,7 @@
     ScriptState* script_state,
     LocalDOMWindow& window,
     const ChooseFileSystemEntriesOptions* options) {
-  if (!base::FeatureList::IsEnabled(blink::features::kWritableFilesAPI)) {
+  if (!base::FeatureList::IsEnabled(blink::features::kNativeFilesystemAPI)) {
     return ScriptPromise::RejectWithDOMException(
         script_state, DOMException::Create(DOMExceptionCode::kAbortError));
   }
diff --git a/third_party/blink/renderer/modules/filesystem/file_system_writer.cc b/third_party/blink/renderer/modules/filesystem/file_system_writer.cc
index 04c1556..3d3e0b68 100644
--- a/third_party/blink/renderer/modules/filesystem/file_system_writer.cc
+++ b/third_party/blink/renderer/modules/filesystem/file_system_writer.cc
@@ -153,11 +153,10 @@
   }
   DCHECK(!stream_loader_);
 
-  auto reader = stream->getReader(script_state, exception_state);
+  auto* consumer = MakeGarbageCollected<ReadableStreamBytesConsumer>(
+      script_state, stream, exception_state);
   if (exception_state.HadException())
     return ScriptPromise();
-  auto* consumer =
-      MakeGarbageCollected<ReadableStreamBytesConsumer>(script_state, reader);
 
   stream_loader_ = FetchDataLoader::CreateLoaderAsDataPipe(
       ExecutionContext::From(script_state)
diff --git a/third_party/blink/renderer/modules/media_controls/media_controls_rotate_to_fullscreen_delegate.cc b/third_party/blink/renderer/modules/media_controls/media_controls_rotate_to_fullscreen_delegate.cc
index 1ff0a8c7..bb12a4a 100644
--- a/third_party/blink/renderer/modules/media_controls/media_controls_rotate_to_fullscreen_delegate.cc
+++ b/third_party/blink/renderer/modules/media_controls/media_controls_rotate_to_fullscreen_delegate.cc
@@ -7,13 +7,14 @@
 #include "third_party/blink/public/platform/platform.h"
 #include "third_party/blink/public/platform/web_screen_info.h"
 #include "third_party/blink/renderer/bindings/core/v8/v8_binding_for_core.h"
-#include "third_party/blink/renderer/core/dom/element_visibility_observer.h"
 #include "third_party/blink/renderer/core/dom/events/event.h"
 #include "third_party/blink/renderer/core/dom/user_gesture_indicator.h"
 #include "third_party/blink/renderer/core/frame/local_dom_window.h"
 #include "third_party/blink/renderer/core/fullscreen/fullscreen.h"
 #include "third_party/blink/renderer/core/html/media/html_media_element_controls_list.h"
 #include "third_party/blink/renderer/core/html/media/html_video_element.h"
+#include "third_party/blink/renderer/core/intersection_observer/intersection_observer.h"
+#include "third_party/blink/renderer/core/intersection_observer/intersection_observer_entry.h"
 #include "third_party/blink/renderer/core/page/chrome_client.h"
 #include "third_party/blink/renderer/modules/device_orientation/device_orientation_data.h"
 #include "third_party/blink/renderer/modules/device_orientation/device_orientation_event.h"
@@ -28,7 +29,7 @@
 constexpr unsigned kMinVideoSize = 200;
 
 // At least this fraction of the video must be visible.
-constexpr float kVisibilityThreshold = 0.75;
+constexpr float kIntersectionThreshold = 0.75;
 
 }  // anonymous namespace
 
@@ -73,10 +74,10 @@
 void MediaControlsRotateToFullscreenDelegate::Detach() {
   DCHECK(!video_element_->isConnected());
 
-  if (visibility_observer_) {
-    // TODO(johnme): Should I also call Stop in a prefinalizer?
-    visibility_observer_->Stop();
-    visibility_observer_ = nullptr;
+  if (intersection_observer_) {
+    // TODO(johnme): Should I also call disconnect in a prefinalizer?
+    intersection_observer_->disconnect();
+    intersection_observer_ = nullptr;
     is_visible_ = false;
   }
 
@@ -126,27 +127,29 @@
 void MediaControlsRotateToFullscreenDelegate::OnStateChange() {
   // TODO(johnme): Check this aggressive disabling doesn't lead to race
   // conditions where we briefly don't know if the video is visible.
-  bool needs_visibility_observer =
+  bool needs_intersection_observer =
       !video_element_->paused() && !video_element_->IsFullscreen();
-  DVLOG(3) << __func__ << " " << !!visibility_observer_ << " -> "
-           << needs_visibility_observer;
+  DVLOG(3) << __func__ << " " << !!intersection_observer_ << " -> "
+           << needs_intersection_observer;
 
-  if (needs_visibility_observer && !visibility_observer_) {
-    visibility_observer_ = MakeGarbageCollected<ElementVisibilityObserver>(
-        video_element_,
+  if (needs_intersection_observer && !intersection_observer_) {
+    intersection_observer_ = IntersectionObserver::Create(
+        {}, {kIntersectionThreshold}, &video_element_->GetDocument(),
         WTF::BindRepeating(
-            &MediaControlsRotateToFullscreenDelegate::OnVisibilityChange,
+            &MediaControlsRotateToFullscreenDelegate::OnIntersectionChange,
             WrapWeakPersistent(this)));
-    visibility_observer_->Start(kVisibilityThreshold);
-  } else if (!needs_visibility_observer && visibility_observer_) {
-    visibility_observer_->Stop();
-    visibility_observer_ = nullptr;
+    intersection_observer_->observe(video_element_);
+  } else if (!needs_intersection_observer && intersection_observer_) {
+    intersection_observer_->disconnect();
+    intersection_observer_ = nullptr;
     is_visible_ = false;
   }
 }
 
-void MediaControlsRotateToFullscreenDelegate::OnVisibilityChange(
-    bool is_visible) {
+void MediaControlsRotateToFullscreenDelegate::OnIntersectionChange(
+    const HeapVector<Member<IntersectionObserverEntry>>& entries) {
+  bool is_visible =
+      (entries.back()->intersectionRatio() > kIntersectionThreshold);
   DVLOG(3) << __func__ << " " << is_visible_ << " -> " << is_visible;
   is_visible_ = is_visible;
 }
@@ -289,7 +292,7 @@
 void MediaControlsRotateToFullscreenDelegate::Trace(blink::Visitor* visitor) {
   NativeEventListener::Trace(visitor);
   visitor->Trace(video_element_);
-  visitor->Trace(visibility_observer_);
+  visitor->Trace(intersection_observer_);
 }
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/modules/media_controls/media_controls_rotate_to_fullscreen_delegate.h b/third_party/blink/renderer/modules/media_controls/media_controls_rotate_to_fullscreen_delegate.h
index e30dcb3..41138d4ab 100644
--- a/third_party/blink/renderer/modules/media_controls/media_controls_rotate_to_fullscreen_delegate.h
+++ b/third_party/blink/renderer/modules/media_controls/media_controls_rotate_to_fullscreen_delegate.h
@@ -13,7 +13,8 @@
 
 class DeviceOrientationEvent;
 class HTMLVideoElement;
-class ElementVisibilityObserver;
+class IntersectionObserver;
+class IntersectionObserverEntry;
 
 // MediaControlsRotateToFullscreenDelegate automatically enters and exits
 // fullscreen when the device is rotated whilst watching a <video>. It is meant
@@ -45,7 +46,8 @@
   enum class SimpleOrientation { kPortrait, kLandscape, kUnknown };
 
   void OnStateChange();
-  void OnVisibilityChange(bool is_visible);
+  void OnIntersectionChange(
+      const HeapVector<Member<IntersectionObserverEntry>>& entries);
   void OnDeviceOrientationAvailable(DeviceOrientationEvent*);
   void OnScreenOrientationChange();
 
@@ -56,12 +58,12 @@
 
   SimpleOrientation current_screen_orientation_ = SimpleOrientation::kUnknown;
 
-  // Only valid when visibility_observer_ is active and the first
-  // OnVisibilityChanged has been received; otherwise assume video is hidden.
+  // Only valid when intersection_observer_ is active and the first
+  // OnIntersectionChanged has been received; otherwise assume video is hidden.
   bool is_visible_ = false;
 
   // This is null whenever we're not listening.
-  Member<ElementVisibilityObserver> visibility_observer_ = nullptr;
+  Member<IntersectionObserver> intersection_observer_ = nullptr;
 
   // `video_element_` owns MediaControlsImpl that owns |this|.
   Member<HTMLVideoElement> video_element_;
diff --git a/third_party/blink/renderer/modules/media_controls/media_controls_rotate_to_fullscreen_delegate_test.cc b/third_party/blink/renderer/modules/media_controls/media_controls_rotate_to_fullscreen_delegate_test.cc
index ef84bc9..c6427d0d 100644
--- a/third_party/blink/renderer/modules/media_controls/media_controls_rotate_to_fullscreen_delegate_test.cc
+++ b/third_party/blink/renderer/modules/media_controls/media_controls_rotate_to_fullscreen_delegate_test.cc
@@ -137,7 +137,7 @@
 
   bool IsObservingVisibility() const {
     return GetMediaControls()
-        .rotate_to_fullscreen_delegate_->visibility_observer_;
+        .rotate_to_fullscreen_delegate_->intersection_observer_;
   }
 
   bool ObservedVisibility() const {
diff --git a/third_party/blink/renderer/modules/mediastream/user_media_request.cc b/third_party/blink/renderer/modules/mediastream/user_media_request.cc
index 8fed2fb3..03400a12 100644
--- a/third_party/blink/renderer/modules/mediastream/user_media_request.cc
+++ b/third_party/blink/renderer/modules/mediastream/user_media_request.cc
@@ -502,8 +502,7 @@
   Document* document = OwnerDocument();
 
   if (document->IsSecureContext(error_message)) {
-    UseCounter::Count(document->GetFrame(),
-                      WebFeature::kGetUserMediaSecureOrigin);
+    UseCounter::Count(document, WebFeature::kGetUserMediaSecureOrigin);
     UseCounter::CountCrossOriginIframe(
         *document, WebFeature::kGetUserMediaSecureOriginIframe);
 
diff --git a/third_party/blink/renderer/modules/service_worker/fetch_event.cc b/third_party/blink/renderer/modules/service_worker/fetch_event.cc
index 75bcb24..6d5484ff 100644
--- a/third_party/blink/renderer/modules/service_worker/fetch_event.cc
+++ b/third_party/blink/renderer/modules/service_worker/fetch_event.cc
@@ -192,10 +192,15 @@
   resource_response.SetEncodedDataLength(encoded_data_length);
   resource_response.SetEncodedBodyLength(encoded_body_length);
   resource_response.SetDecodedBodyLength(decoded_body_length);
+
+  ResourceLoadTiming* timing = resource_response.GetResourceLoadTiming();
+  // |timing| can be null, see https://crbug.com/817691.
+  base::TimeTicks request_time =
+      timing ? timing->RequestTime() : base::TimeTicks();
   // According to the Resource Timing spec, the initiator type of
   // navigation preload request is "navigation".
-  scoped_refptr<ResourceTimingInfo> info = ResourceTimingInfo::Create(
-      "navigation", resource_response.GetResourceLoadTiming()->RequestTime());
+  scoped_refptr<ResourceTimingInfo> info =
+      ResourceTimingInfo::Create("navigation", request_time);
   info->SetNegativeAllowed(true);
   info->SetLoadFinishTime(completion_time);
   info->SetInitialURL(request_->url());
diff --git a/third_party/blink/renderer/modules/service_worker/service_worker_global_scope_proxy.cc b/third_party/blink/renderer/modules/service_worker/service_worker_global_scope_proxy.cc
index 73eeb27..300a4a1 100644
--- a/third_party/blink/renderer/modules/service_worker/service_worker_global_scope_proxy.cc
+++ b/third_party/blink/renderer/modules/service_worker/service_worker_global_scope_proxy.cc
@@ -618,8 +618,28 @@
     const String& message,
     SourceLocation* location) {
   DCHECK_CALLED_ON_VALID_THREAD(worker_thread_checker_);
-  Client().ReportConsoleMessage(source, level, message, location->LineNumber(),
-                                location->Url());
+
+  // TODO(https://crbug.com/937184): This back-and-forth conversion will be
+  // unnecessary once we converge on blink::mojom::ConsoleMessageLevel.
+  blink::mojom::ConsoleMessageLevel console_message_level =
+      blink::mojom::ConsoleMessageLevel::kInfo;
+  switch (level) {
+    case kVerboseMessageLevel:
+      console_message_level = blink::mojom::ConsoleMessageLevel::kVerbose;
+      break;
+    case kInfoMessageLevel:
+      console_message_level = blink::mojom::ConsoleMessageLevel::kInfo;
+      break;
+    case kWarningMessageLevel:
+      console_message_level = blink::mojom::ConsoleMessageLevel::kWarning;
+      break;
+    case kErrorMessageLevel:
+      console_message_level = blink::mojom::ConsoleMessageLevel::kError;
+      break;
+  }
+
+  Client().ReportConsoleMessage(source, console_message_level, message,
+                                location->LineNumber(), location->Url());
 }
 
 void ServiceWorkerGlobalScopeProxy::WillInitializeWorkerContext() {
diff --git a/third_party/blink/renderer/modules/service_worker/web_embedded_worker_impl_test.cc b/third_party/blink/renderer/modules/service_worker/web_embedded_worker_impl_test.cc
index d137869..8153e348 100644
--- a/third_party/blink/renderer/modules/service_worker/web_embedded_worker_impl_test.cc
+++ b/third_party/blink/renderer/modules/service_worker/web_embedded_worker_impl_test.cc
@@ -135,7 +135,7 @@
         url_test_helpers::ToKURL("https://www.example.com/sw.js");
     WebURLResponse response(script_url);
     response.SetMIMEType("text/javascript");
-    response.SetHTTPStatusCode(200);
+    response.SetHttpStatusCode(200);
     Platform::Current()->GetURLLoaderMockFactory()->RegisterURL(script_url,
                                                                 response, "");
 
@@ -232,7 +232,7 @@
       url_test_helpers::ToKURL("https://www.example.com/sw-404.js");
   WebURLResponse response;
   response.SetMIMEType("text/javascript");
-  response.SetHTTPStatusCode(404);
+  response.SetHttpStatusCode(404);
   ResourceError error = ResourceError::Failure(script_url);
   Platform::Current()->GetURLLoaderMockFactory()->RegisterErrorURL(
       script_url, response, error);
diff --git a/third_party/blink/renderer/modules/vibration/navigator_vibration.cc b/third_party/blink/renderer/modules/vibration/navigator_vibration.cc
index 6daee1b..a6d209d 100644
--- a/third_party/blink/renderer/modules/vibration/navigator_vibration.cc
+++ b/third_party/blink/renderer/modules/vibration/navigator_vibration.cc
@@ -70,7 +70,7 @@
   // reference to |window| or |navigator| was retained in another window.
   if (!frame)
     return false;
-  CollectHistogramMetrics(*frame);
+  CollectHistogramMetrics(navigator);
 
   DCHECK(frame->GetDocument());
   DCHECK(frame->GetPage());
@@ -101,14 +101,16 @@
 }
 
 // static
-void NavigatorVibration::CollectHistogramMetrics(const LocalFrame& frame) {
+void NavigatorVibration::CollectHistogramMetrics(const Navigator& navigator) {
   NavigatorVibrationType type;
-  bool user_gesture = frame.HasBeenActivated();
-  UseCounter::Count(frame.GetDocument(), WebFeature::kNavigatorVibrate);
-  if (!frame.IsMainFrame()) {
-    UseCounter::Count(frame.GetDocument(),
+  LocalFrame* frame = navigator.GetFrame();
+  bool user_gesture = frame->HasBeenActivated();
+  UseCounter::Count(navigator.DomWindow()->document(),
+                    WebFeature::kNavigatorVibrate);
+  if (!frame->IsMainFrame()) {
+    UseCounter::Count(navigator.DomWindow()->document(),
                       WebFeature::kNavigatorVibrateSubFrame);
-    if (frame.IsCrossOriginSubframe()) {
+    if (frame->IsCrossOriginSubframe()) {
       if (user_gesture)
         type = NavigatorVibrationType::kCrossOriginSubFrameWithUserGesture;
       else
diff --git a/third_party/blink/renderer/modules/vibration/navigator_vibration.h b/third_party/blink/renderer/modules/vibration/navigator_vibration.h
index e25083f..4c2fad0 100644
--- a/third_party/blink/renderer/modules/vibration/navigator_vibration.h
+++ b/third_party/blink/renderer/modules/vibration/navigator_vibration.h
@@ -72,7 +72,7 @@
   // Inherited from ContextLifecycleObserver.
   void ContextDestroyed(ExecutionContext*) override;
 
-  static void CollectHistogramMetrics(const LocalFrame&);
+  static void CollectHistogramMetrics(const Navigator&);
 
   Member<VibrationController> controller_;
 
diff --git a/third_party/blink/renderer/modules/webaudio/audio_context.cc b/third_party/blink/renderer/modules/webaudio/audio_context.cc
index d1d50453..75c3b2ab 100644
--- a/third_party/blink/renderer/modules/webaudio/audio_context.cc
+++ b/third_party/blink/renderer/modules/webaudio/audio_context.cc
@@ -7,7 +7,7 @@
 #include "build/build_config.h"
 #include "services/metrics/public/cpp/ukm_builders.h"
 #include "services/metrics/public/cpp/ukm_recorder.h"
-#include "services/service_manager/public/cpp/interface_provider.h"
+#include "third_party/blink/public/mojom/frame/document_interface_broker.mojom-blink.h"
 #include "third_party/blink/public/platform/web_audio_latency_hint.h"
 #include "third_party/blink/renderer/bindings/core/v8/script_promise_resolver.h"
 #include "third_party/blink/renderer/core/dom/dom_exception.h"
@@ -642,8 +642,10 @@
   if (audio_context_manager_ || !GetDocument())
     return;
 
-  GetDocument()->GetFrame()->GetInterfaceProvider().GetInterface(
-      mojo::MakeRequest(&audio_context_manager_));
+  GetDocument()
+      ->GetFrame()
+      ->GetDocumentInterfaceBroker()
+      .GetAudioContextManager(mojo::MakeRequest(&audio_context_manager_));
   audio_context_manager_.set_connection_error_handler(
       WTF::Bind(&AudioContext::OnAudioContextManagerServiceConnectionError,
                 WrapWeakPersistent(this)));
diff --git a/third_party/blink/renderer/modules/webaudio/deferred_task_handler.cc b/third_party/blink/renderer/modules/webaudio/deferred_task_handler.cc
index 763b9548..1035bdd 100644
--- a/third_party/blink/renderer/modules/webaudio/deferred_task_handler.cc
+++ b/third_party/blink/renderer/modules/webaudio/deferred_task_handler.cc
@@ -332,8 +332,16 @@
 void DeferredTaskHandler::RequestToDeleteHandlersOnMainThread() {
   DCHECK(IsAudioThread());
   AssertGraphOwner();
-  if (rendering_orphan_handlers_.IsEmpty())
+
+  // Quick exit if there are no handlers that need to be deleted so that we
+  // don't unecessarily post a task.  Be onsistent with
+  // |DeleteHandlersOnMainThread()| so we don't accidentally return early when
+  // there are handlers that could be deleted.
+  if (rendering_orphan_handlers_.IsEmpty() &&
+      finished_tail_processing_handlers_.size()) {
     return;
+  }
+
   deletable_orphan_handlers_.AppendVector(rendering_orphan_handlers_);
   rendering_orphan_handlers_.clear();
   PostCrossThreadTask(
diff --git a/third_party/blink/renderer/platform/exported/web_runtime_features.cc b/third_party/blink/renderer/platform/exported/web_runtime_features.cc
index 19966a9..5818469 100644
--- a/third_party/blink/renderer/platform/exported/web_runtime_features.cc
+++ b/third_party/blink/renderer/platform/exported/web_runtime_features.cc
@@ -217,8 +217,16 @@
   RuntimeEnabledFeatures::SetJankTrackingSweepLineEnabled(enable);
 }
 
-void WebRuntimeFeatures::EnableLayeredAPI(bool enable) {
-  RuntimeEnabledFeatures::SetLayeredAPIEnabled(enable);
+void WebRuntimeFeatures::EnableBuiltInModuleAll(bool enable) {
+  RuntimeEnabledFeatures::SetBuiltInModuleAllEnabled(enable);
+}
+
+void WebRuntimeFeatures::EnableBuiltInModuleInfra(bool enable) {
+  RuntimeEnabledFeatures::SetBuiltInModuleInfraEnabled(enable);
+}
+
+void WebRuntimeFeatures::EnableBuiltInModuleKvStorage(bool enable) {
+  RuntimeEnabledFeatures::SetBuiltInModuleKvStorageEnabled(enable);
 }
 
 void WebRuntimeFeatures::EnableLayoutNG(bool enable) {
@@ -586,10 +594,6 @@
   RuntimeEnabledFeatures::SetAutomationControlledEnabled(enable);
 }
 
-void WebRuntimeFeatures::EnableScheduledScriptStreaming(bool enable) {
-  RuntimeEnabledFeatures::SetScheduledScriptStreamingEnabled(enable);
-}
-
 void WebRuntimeFeatures::EnableScriptStreamingOnPreload(bool enable) {
   RuntimeEnabledFeatures::SetScriptStreamingOnPreloadEnabled(enable);
 }
diff --git a/third_party/blink/renderer/platform/exported/web_url_response.cc b/third_party/blink/renderer/platform/exported/web_url_response.cc
index fe41bd1f..5cacf47 100644
--- a/third_party/blink/renderer/platform/exported/web_url_response.cc
+++ b/third_party/blink/renderer/platform/exported/web_url_response.cc
@@ -154,8 +154,8 @@
   return resource_response_->HttpStatusCode();
 }
 
-void WebURLResponse::SetHTTPStatusCode(int http_status_code) {
-  resource_response_->SetHTTPStatusCode(http_status_code);
+void WebURLResponse::SetHttpStatusCode(int http_status_code) {
+  resource_response_->SetHttpStatusCode(http_status_code);
 }
 
 WebString WebURLResponse::HttpStatusText() const {
diff --git a/third_party/blink/renderer/platform/graphics/canvas_resource.cc b/third_party/blink/renderer/platform/graphics/canvas_resource.cc
index 46048212..1a2d4d4 100644
--- a/third_party/blink/renderer/platform/graphics/canvas_resource.cc
+++ b/third_party/blink/renderer/platform/graphics/canvas_resource.cc
@@ -680,7 +680,8 @@
       context_provider_wrapper_->ContextProvider()->GetSharedImageInterface();
   DCHECK(shared_image_interface);
 
-  uint32_t flags = gpu::SHARED_IMAGE_USAGE_GLES2 |
+  uint32_t flags = gpu::SHARED_IMAGE_USAGE_DISPLAY |
+                   gpu::SHARED_IMAGE_USAGE_GLES2 |
                    gpu::SHARED_IMAGE_USAGE_GLES2_FRAMEBUFFER_HINT;
   if (is_overlay_candidate_)
     flags |= gpu::SHARED_IMAGE_USAGE_SCANOUT;
diff --git a/third_party/blink/renderer/platform/graphics/compositing/paint_artifact_compositor.cc b/third_party/blink/renderer/platform/graphics/compositing/paint_artifact_compositor.cc
index 149a4304..2e38732 100644
--- a/third_party/blink/renderer/platform/graphics/compositing/paint_artifact_compositor.cc
+++ b/third_party/blink/renderer/platform/graphics/compositing/paint_artifact_compositor.cc
@@ -34,16 +34,28 @@
 
 namespace {
 
+// Returns true if the given element namespace is one of the ones needed for
+// running animations on the compositor. These are the only element_ids the
+// compositor needs to track existence of in the element id set.
+bool IsAnimationNamespace(CompositorElementIdNamespace element_namespace) {
+  return element_namespace == CompositorElementIdNamespace::kPrimaryTransform ||
+         element_namespace == CompositorElementIdNamespace::kPrimaryEffect ||
+         element_namespace == CompositorElementIdNamespace::kEffectFilter;
+}
+
 // Inserts the element ids of the given node and all of its ancestors into the
-// given |composited_element_ids| set. Returns once it finds an id which already
+// given |composited_element_ids| set. Filters out specifically element ids
+// which are needed for animations. Returns once it finds an id which already
 // exists as this implies that all of those ancestor nodes have already been
 // inserted.
 template <typename NodeType>
-void InsertAncestorElementIds(const NodeType& node,
-                              CompositorElementIdSet& composited_element_ids) {
+void InsertAncestorElementIdsForAnimation(
+    const NodeType& node,
+    CompositorElementIdSet& composited_element_ids) {
   for (const auto* n = &node; n; n = SafeUnalias(n->Parent())) {
     const CompositorElementId& element_id = n->GetCompositorElementId();
-    if (element_id) {
+    if (element_id &&
+        IsAnimationNamespace(NamespaceFromCompositorElementId(element_id))) {
       if (composited_element_ids.count(element_id)) {
         // Once we reach a node already counted we can stop traversing the
         // parent chain.
@@ -502,12 +514,23 @@
     const PaintArtifact& paint_artifact,
     const EffectPaintPropertyNode& unaliased_group,
     Vector<PaintChunk>::const_iterator& chunk_it) {
+  // In pre-CompositeAfterPaint, existence of composited layers is decided
+  // during compositing update before paint. Each chunk contains a foreign layer
+  // corresponding a composited layer. We should not skip any of them to ensure
+  // correct composited hit testing and animation.
+  if (!RuntimeEnabledFeatures::CompositeAfterPaintEnabled())
+    return false;
+
   // The lower bound of visibility is considered to be 0.0004f < 1/2048. With
   // 10-bit color channels (only available on the newest Macs as of 2016;
   // otherwise it's 8-bit), we see that an alpha of 1/2048 or less leads to a
   // color output of less than 0.5 in all channels, hence not visible.
   static const float kMinimumVisibleOpacity = 0.0004f;
   if (unaliased_group.Opacity() >= kMinimumVisibleOpacity ||
+      // TODO(crbug.com/937573): We should disable the optimization for all
+      // cases that the invisible group will be composited, to ensure correct
+      // composited hit testing and animation. Checking the effect node's
+      // HasDirectCompositingReasons() is not enough.
       unaliased_group.HasDirectCompositingReasons()) {
     return false;
   }
@@ -886,8 +909,9 @@
         pending_layer.FirstPaintChunk(*paint_artifact).id.client.OwnerNodeId());
     // TODO(wangxianzhu): cc_picture_layer_->set_compositing_reasons(...);
 
-    InsertAncestorElementIds(property_state.Effect(), composited_element_ids);
-    InsertAncestorElementIds(transform, composited_element_ids);
+    InsertAncestorElementIdsForAnimation(property_state.Effect(),
+                                         composited_element_ids);
+    InsertAncestorElementIdsForAnimation(transform, composited_element_ids);
     if (layer->scrollable())
       composited_element_ids.insert(layer->element_id());
 
diff --git a/third_party/blink/renderer/platform/graphics/compositing/paint_artifact_compositor.h b/third_party/blink/renderer/platform/graphics/compositing/paint_artifact_compositor.h
index 9d0f650..69fcc67 100644
--- a/third_party/blink/renderer/platform/graphics/compositing/paint_artifact_compositor.h
+++ b/third_party/blink/renderer/platform/graphics/compositing/paint_artifact_compositor.h
@@ -134,6 +134,7 @@
 
   void SetNeedsUpdate() { needs_update_ = true; }
   bool NeedsUpdate() const { return needs_update_; }
+  void ClearNeedsUpdateForTesting() { needs_update_ = false; }
 
  private:
   // A pending layer is a collection of paint chunks that will end up in
diff --git a/third_party/blink/renderer/platform/graphics/compositing/paint_artifact_compositor_test.cc b/third_party/blink/renderer/platform/graphics/compositing/paint_artifact_compositor_test.cc
index 83f5513..8162b9d 100644
--- a/third_party/blink/renderer/platform/graphics/compositing/paint_artifact_compositor_test.cc
+++ b/third_party/blink/renderer/platform/graphics/compositing/paint_artifact_compositor_test.cc
@@ -1954,7 +1954,8 @@
   state.output_clip = &c0();
   state.opacity = 2.0 / 255.0;
   state.direct_compositing_reasons = CompositingReason::kActiveOpacityAnimation;
-  state.compositor_element_id = CompositorElementId(2);
+  state.compositor_element_id = CompositorElementIdFromUniqueObjectId(
+      2, CompositorElementIdNamespace::kPrimaryEffect);
   return EffectPaintPropertyNode::Create(e0(), std::move(state));
 }
 
@@ -1964,7 +1965,8 @@
   state.matrix.Rotate(90);
   state.origin = FloatPoint3D(100, 100, 0);
   state.direct_compositing_reasons = CompositingReason::k3DTransform;
-  state.compositor_element_id = CompositorElementId(3);
+  state.compositor_element_id = CompositorElementIdFromUniqueObjectId(
+      3, CompositorElementIdNamespace::kPrimaryTransform);
   return TransformPaintPropertyNode::Create(t0(), std::move(state));
 }
 
@@ -2337,7 +2339,10 @@
 
 TEST_P(PaintArtifactCompositorTest, SkipChunkWithOpacityZero) {
   UpdateWithArtifactWithOpacity(0, false, false);
-  ASSERT_EQ(0u, ContentLayerCount());
+  if (RuntimeEnabledFeatures::CompositeAfterPaintEnabled())
+    EXPECT_EQ(0u, ContentLayerCount());
+  else
+    EXPECT_EQ(1u, ContentLayerCount());
 }
 
 TEST_P(PaintArtifactCompositorTest,
@@ -2359,7 +2364,10 @@
 
 TEST_P(PaintArtifactCompositorTest, SkipChunkWithTinyOpacity) {
   UpdateWithArtifactWithOpacity(0.0003f, false, false);
-  ASSERT_EQ(0u, ContentLayerCount());
+  if (RuntimeEnabledFeatures::CompositeAfterPaintEnabled())
+    EXPECT_EQ(0u, ContentLayerCount());
+  else
+    EXPECT_EQ(1u, ContentLayerCount());
 }
 
 TEST_P(PaintArtifactCompositorTest,
@@ -2445,7 +2453,10 @@
   artifact.Chunk(t0(), c0(), *visible_effect)
       .RectDrawing(FloatRect(0, 0, 100, 100), Color::kBlack);
   Update(artifact.Build());
-  ASSERT_EQ(0u, ContentLayerCount());
+  if (RuntimeEnabledFeatures::CompositeAfterPaintEnabled())
+    EXPECT_EQ(0u, ContentLayerCount());
+  else
+    EXPECT_EQ(1u, ContentLayerCount());
 }
 
 TEST_P(
@@ -2470,7 +2481,10 @@
   artifact.Chunk(t0(), c0(), *visible_effect)
       .RectDrawing(FloatRect(0, 0, 100, 100), Color::kBlack);
   Update(artifact.Build());
-  ASSERT_EQ(0u, ContentLayerCount());
+  if (RuntimeEnabledFeatures::CompositeAfterPaintEnabled())
+    EXPECT_EQ(0u, ContentLayerCount());
+  else
+    EXPECT_EQ(1u, ContentLayerCount());
 }
 
 TEST_P(PaintArtifactCompositorTest, UpdateManagesLayerElementIds) {
diff --git a/third_party/blink/renderer/platform/graphics/paint/clip_paint_property_node.h b/third_party/blink/renderer/platform/graphics/paint/clip_paint_property_node.h
index 12252d0..b6c65514 100644
--- a/third_party/blink/renderer/platform/graphics/paint/clip_paint_property_node.h
+++ b/third_party/blink/renderer/platform/graphics/paint/clip_paint_property_node.h
@@ -5,6 +5,7 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_PAINT_CLIP_PAINT_PROPERTY_NODE_H_
 #define THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_PAINT_CLIP_PAINT_PROPERTY_NODE_H_
 
+#include <algorithm>
 #include "base/memory/scoped_refptr.h"
 #include "base/optional.h"
 #include "third_party/blink/renderer/platform/geometry/float_rounded_rect.h"
@@ -39,17 +40,19 @@
 
     // Returns true if the states are equal, ignoring the clip rect excluding
     // overlay scrollbars which is only used for hit testing.
-    bool EqualIgnoringHitTestRects(const State& o) const {
-      return local_transform_space == o.local_transform_space &&
-             clip_rect == o.clip_rect && clip_path == o.clip_path &&
-             direct_compositing_reasons == o.direct_compositing_reasons;
+    bool EqualIgnoringHitTestRects(const State& other) const {
+      return local_transform_space == other.local_transform_space &&
+             clip_rect == other.clip_rect && clip_path == other.clip_path &&
+             direct_compositing_reasons == other.direct_compositing_reasons;
     }
 
-    bool operator==(const State& o) const {
-      if (!EqualIgnoringHitTestRects(o))
-        return false;
-      return clip_rect_excluding_overlay_scrollbars ==
-             o.clip_rect_excluding_overlay_scrollbars;
+    PaintPropertyChangeType CheckChange(const State& other) const {
+      if (!EqualIgnoringHitTestRects(other) ||
+          clip_rect_excluding_overlay_scrollbars !=
+              other.clip_rect_excluding_overlay_scrollbars) {
+        return PaintPropertyChangeType::kChangedOnlyValues;
+      }
+      return PaintPropertyChangeType::kUnchanged;
     }
   };
 
@@ -70,15 +73,16 @@
         true /* is_parent_alias */));
   }
 
-  bool Update(const ClipPaintPropertyNode& parent, State&& state) {
-    bool parent_changed = SetParent(&parent);
-    if (state == state_)
-      return parent_changed;
-
-    DCHECK(!IsParentAlias()) << "Changed the state of an alias node.";
-    state_ = std::move(state);
-    SetChanged();
-    return true;
+  PaintPropertyChangeType Update(const ClipPaintPropertyNode& parent,
+                                 State&& state) {
+    auto parent_changed = SetParent(&parent);
+    auto state_changed = state_.CheckChange(state);
+    if (state_changed != PaintPropertyChangeType::kUnchanged) {
+      DCHECK(!IsParentAlias()) << "Changed the state of an alias node.";
+      state_ = std::move(state);
+      SetChanged();
+    }
+    return std::max(parent_changed, state_changed);
   }
 
   // Checks if the accumulated clip from |this| to |relative_to_state.Clip()|
diff --git a/third_party/blink/renderer/platform/graphics/paint/effect_paint_property_node.h b/third_party/blink/renderer/platform/graphics/paint/effect_paint_property_node.h
index b9e3218..5b6d737 100644
--- a/third_party/blink/renderer/platform/graphics/paint/effect_paint_property_node.h
+++ b/third_party/blink/renderer/platform/graphics/paint/effect_paint_property_node.h
@@ -5,6 +5,7 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_PAINT_EFFECT_PAINT_PROPERTY_NODE_H_
 #define THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_PAINT_EFFECT_PAINT_PROPERTY_NODE_H_
 
+#include <algorithm>
 #include "third_party/blink/renderer/platform/graphics/compositor_element_id.h"
 #include "third_party/blink/renderer/platform/graphics/compositor_filter_operations.h"
 #include "third_party/blink/renderer/platform/graphics/graphics_types.h"
@@ -48,38 +49,52 @@
     gfx::RRectF backdrop_filter_bounds;
     SkBlendMode blend_mode = SkBlendMode::kSrcOver;
     // === End of effects ===
+    // TODO(crbug.com/937929): Put these into CompositingReasons when we can
+    // detect composited animation status changes in LayoutObject::SetStyle().
+    bool is_running_opacity_animation_on_compositor = false;
+    bool is_running_filter_animation_on_compositor = false;
+    bool is_running_backdrop_filter_animation_on_compositor = false;
     CompositingReasons direct_compositing_reasons = CompositingReason::kNone;
     CompositorElementId compositor_element_id;
     // The offset of the origin of filters in local_transform_space.
     FloatPoint filters_origin;
 
-    bool operator==(const State& o) const {
-      return EqualIgnoringAnimatingProperties(o, false);
-    }
-
-    bool EqualIgnoringAnimatingProperties(const State& o,
-                                          bool check_for_animations) const {
-      bool filters_equal =
-          (filter == o.filter) ||
-          (check_for_animations && (direct_compositing_reasons &
-                                    CompositingReason::kActiveFilterAnimation));
-      bool backdrops_equal =
-          (backdrop_filter == o.backdrop_filter) ||
-          (check_for_animations &&
-           (direct_compositing_reasons &
-            CompositingReason::kActiveBackdropFilterAnimation));
-      bool opacity_equal = (opacity == o.opacity) ||
-                           (check_for_animations &&
-                            (direct_compositing_reasons &
-                             CompositingReason::kActiveOpacityAnimation));
-      return local_transform_space == o.local_transform_space &&
-             output_clip == o.output_clip && color_filter == o.color_filter &&
-             backdrop_filter_bounds == o.backdrop_filter_bounds &&
-             blend_mode == o.blend_mode &&
-             direct_compositing_reasons == o.direct_compositing_reasons &&
-             compositor_element_id == o.compositor_element_id &&
-             filters_origin == o.filters_origin && filters_equal &&
-             backdrops_equal && opacity_equal;
+    PaintPropertyChangeType CheckChange(const State& other) {
+      if (local_transform_space != other.local_transform_space ||
+          output_clip != other.output_clip ||
+          color_filter != other.color_filter ||
+          backdrop_filter_bounds != other.backdrop_filter_bounds ||
+          blend_mode != other.blend_mode ||
+          direct_compositing_reasons != other.direct_compositing_reasons ||
+          compositor_element_id != other.compositor_element_id ||
+          filters_origin != other.filters_origin) {
+        return PaintPropertyChangeType::kChangedOnlyValues;
+      }
+      bool opacity_changed = opacity != other.opacity;
+      if (opacity_changed && !is_running_opacity_animation_on_compositor) {
+        return PaintPropertyChangeType::kChangedOnlyValues;
+      }
+      bool filter_changed = filter != other.filter;
+      if (filter_changed && !is_running_filter_animation_on_compositor) {
+        return PaintPropertyChangeType::kChangedOnlyValues;
+      }
+      bool backdrop_filter_changed = backdrop_filter != other.backdrop_filter;
+      if (backdrop_filter_changed &&
+          !is_running_backdrop_filter_animation_on_compositor) {
+        return PaintPropertyChangeType::kChangedOnlyValues;
+      }
+      if (is_running_opacity_animation_on_compositor !=
+              other.is_running_opacity_animation_on_compositor ||
+          is_running_filter_animation_on_compositor !=
+              other.is_running_filter_animation_on_compositor ||
+          is_running_backdrop_filter_animation_on_compositor !=
+              other.is_running_backdrop_filter_animation_on_compositor) {
+        return PaintPropertyChangeType::kChangedOnlyCompositedAnimationStatus;
+      }
+      if (opacity_changed || filter_changed || backdrop_filter_changed) {
+        return PaintPropertyChangeType::kChangedOnlyCompositedAnimationValues;
+      }
+      return PaintPropertyChangeType::kUnchanged;
     }
   };
 
@@ -98,21 +113,17 @@
         &parent, State{}, true /* is_parent_alias */));
   }
 
-  bool Update(const EffectPaintPropertyNode& parent, State&& state) {
-    bool parent_changed = SetParent(&parent);
-    if (state == state_)
-      return parent_changed;
-
-    DCHECK(!IsParentAlias()) << "Changed the state of an alias node.";
-    state_ = std::move(state);
-    SetChanged();
-    return true;
-  }
-
-  bool HaveNonAnimatingPropertiesChanged(const EffectPaintPropertyNode& parent,
-                                         State& state) const {
-    return !state.EqualIgnoringAnimatingProperties(state_, true) ||
-           HasParentChanged(&parent);
+  PaintPropertyChangeType Update(const EffectPaintPropertyNode& parent,
+                                 State&& state) {
+    auto parent_changed = SetParent(&parent);
+    auto state_changed = state_.CheckChange(state);
+    if (state_changed != PaintPropertyChangeType::kUnchanged) {
+      DCHECK(!IsParentAlias()) << "Changed the state of an alias node.";
+      state_ = std::move(state);
+      SetChanged();
+      Validate();
+    }
+    return std::max(parent_changed, state_changed);
   }
 
   // Checks if the accumulated effect from |this| to |relative_to_state
@@ -177,18 +188,27 @@
   bool HasDirectCompositingReasons() const {
     return DirectCompositingReasons() != CompositingReason::kNone;
   }
-  bool RequiresCompositingForAnimation() const {
-    return DirectCompositingReasons() &
-           CompositingReason::kComboActiveAnimation;
-  }
   bool HasActiveOpacityAnimation() const {
     return DirectCompositingReasons() &
            CompositingReason::kActiveOpacityAnimation;
   }
+  bool IsRunningOpacityAnimationOnCompositor() const {
+    return state_.is_running_opacity_animation_on_compositor;
+  }
   bool HasActiveFilterAnimation() const {
     return DirectCompositingReasons() &
            CompositingReason::kActiveFilterAnimation;
   }
+  bool IsRunningFilterAnimationOnCompositor() const {
+    return state_.is_running_filter_animation_on_compositor;
+  }
+  bool HasActiveBackdropFilterAnimation() const {
+    return DirectCompositingReasons() &
+           CompositingReason::kActiveBackdropFilterAnimation;
+  }
+  bool IsRunningBackdropFilterAnimationOnCompositor() const {
+    return state_.is_running_backdrop_filter_animation_on_compositor;
+  }
 
   const CompositorElementId& GetCompositorElementId() const {
     DCHECK(!Parent() || !IsParentAlias());
@@ -211,6 +231,15 @@
     return state_.direct_compositing_reasons;
   }
 
+  void Validate() const {
+    DCHECK(!IsRunningOpacityAnimationOnCompositor() ||
+           HasActiveOpacityAnimation());
+    DCHECK(!IsRunningFilterAnimationOnCompositor() ||
+           HasActiveFilterAnimation());
+    DCHECK(!IsRunningBackdropFilterAnimationOnCompositor() ||
+           HasActiveBackdropFilterAnimation());
+  }
+
   State state_;
 };
 
diff --git a/third_party/blink/renderer/platform/graphics/paint/paint_property_node.h b/third_party/blink/renderer/platform/graphics/paint/paint_property_node.h
index 0c86a04..a5df595 100644
--- a/third_party/blink/renderer/platform/graphics/paint/paint_property_node.h
+++ b/third_party/blink/renderer/platform/graphics/paint/paint_property_node.h
@@ -25,6 +25,19 @@
 class ScrollPaintPropertyNode;
 class TransformPaintPropertyNode;
 
+// Used to report whether and how paint properties have changed. The order is
+// important - it must go from no change to the most significant change.
+enum class PaintPropertyChangeType {
+  kUnchanged,
+  kChangedOnlyCompositedAnimationValues,
+  // TODO(crbug.com/937929): See ObjectPaintProperties::Update().
+  kChangedOnlyCompositedAnimationStatus,
+  kChangedOnlyValues,
+  // A paint property node is added or removed. This value is used only in
+  // renderer/core classes.
+  kNodeAddedOrRemoved,
+};
+
 // Returns the lowest common ancestor in the paint property tree.
 template <typename NodeType>
 const NodeType& LowestCommonAncestor(const NodeType& a, const NodeType& b) {
@@ -117,18 +130,15 @@
         is_parent_alias_(is_parent_alias),
         changed_(!!parent) {}
 
-  bool SetParent(const NodeType* parent) {
+  PaintPropertyChangeType SetParent(const NodeType* parent) {
     DCHECK(!IsRoot());
     DCHECK(parent != this);
     if (parent == parent_)
-      return false;
+      return PaintPropertyChangeType::kUnchanged;
 
     parent_ = parent;
     static_cast<NodeType*>(this)->SetChanged();
-    return true;
-  }
-  bool HasParentChanged(const NodeType* parent) const {
-    return parent != parent_;
+    return PaintPropertyChangeType::kChangedOnlyValues;
   }
 
   void SetChanged() {
diff --git a/third_party/blink/renderer/platform/graphics/paint/scroll_paint_property_node.h b/third_party/blink/renderer/platform/graphics/paint/scroll_paint_property_node.h
index e60dfb8..c75867ba 100644
--- a/third_party/blink/renderer/platform/graphics/paint/scroll_paint_property_node.h
+++ b/third_party/blink/renderer/platform/graphics/paint/scroll_paint_property_node.h
@@ -5,6 +5,7 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_PAINT_SCROLL_PAINT_PROPERTY_NODE_H_
 #define THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_PAINT_SCROLL_PAINT_PROPERTY_NODE_H_
 
+#include <algorithm>
 #include "base/optional.h"
 #include "cc/input/main_thread_scrolling_reason.h"
 #include "cc/input/overscroll_behavior.h"
@@ -54,19 +55,23 @@
         cc::OverscrollBehavior::kOverscrollBehaviorTypeAuto);
     base::Optional<cc::SnapContainerData> snap_container_data;
 
-    bool operator==(const State& o) const {
-      return container_rect == o.container_rect &&
-             contents_size == o.contents_size &&
-             user_scrollable_horizontal == o.user_scrollable_horizontal &&
-             user_scrollable_vertical == o.user_scrollable_vertical &&
-             scrolls_inner_viewport == o.scrolls_inner_viewport &&
-             scrolls_outer_viewport == o.scrolls_outer_viewport &&
-             max_scroll_offset_affected_by_page_scale ==
-                 o.max_scroll_offset_affected_by_page_scale &&
-             main_thread_scrolling_reasons == o.main_thread_scrolling_reasons &&
-             compositor_element_id == o.compositor_element_id &&
-             overscroll_behavior == o.overscroll_behavior &&
-             snap_container_data == o.snap_container_data;
+    PaintPropertyChangeType CheckChange(const State& other) const {
+      if (container_rect != other.container_rect ||
+          contents_size != other.contents_size ||
+          user_scrollable_horizontal != other.user_scrollable_horizontal ||
+          user_scrollable_vertical != other.user_scrollable_vertical ||
+          scrolls_inner_viewport != other.scrolls_inner_viewport ||
+          scrolls_outer_viewport != other.scrolls_outer_viewport ||
+          max_scroll_offset_affected_by_page_scale !=
+              other.max_scroll_offset_affected_by_page_scale ||
+          main_thread_scrolling_reasons !=
+              other.main_thread_scrolling_reasons ||
+          compositor_element_id != other.compositor_element_id ||
+          overscroll_behavior != other.overscroll_behavior ||
+          snap_container_data != other.snap_container_data) {
+        return PaintPropertyChangeType::kChangedOnlyValues;
+      }
+      return PaintPropertyChangeType::kUnchanged;
     }
   };
 
@@ -86,15 +91,16 @@
     return nullptr;
   }
 
-  bool Update(const ScrollPaintPropertyNode& parent, State&& state) {
-    bool parent_changed = SetParent(&parent);
-    if (state == state_)
-      return parent_changed;
-
-    state_ = std::move(state);
-    Validate();
-    SetChanged();
-    return true;
+  PaintPropertyChangeType Update(const ScrollPaintPropertyNode& parent,
+                                 State&& state) {
+    auto parent_changed = SetParent(&parent);
+    auto state_changed = state_.CheckChange(state);
+    if (state_changed != PaintPropertyChangeType::kUnchanged) {
+      state_ = std::move(state);
+      Validate();
+      SetChanged();
+    }
+    return std::max(parent_changed, state_changed);
   }
 
   cc::OverscrollBehavior::OverscrollBehaviorType OverscrollBehaviorX() const {
diff --git a/third_party/blink/renderer/platform/graphics/paint/transform_paint_property_node.h b/third_party/blink/renderer/platform/graphics/paint/transform_paint_property_node.h
index 29dd9a4a..d74c5d4 100644
--- a/third_party/blink/renderer/platform/graphics/paint/transform_paint_property_node.h
+++ b/third_party/blink/renderer/platform/graphics/paint/transform_paint_property_node.h
@@ -5,6 +5,7 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_PAINT_TRANSFORM_PAINT_PROPERTY_NODE_H_
 #define THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_PAINT_TRANSFORM_PAINT_PROPERTY_NODE_H_
 
+#include <algorithm>
 #include "cc/layers/layer_sticky_position_constraint.h"
 #include "third_party/blink/renderer/platform/geometry/float_point_3d.h"
 #include "third_party/blink/renderer/platform/graphics/compositing_reasons.h"
@@ -54,34 +55,49 @@
     // translation, or the field will be updated automatically.
     bool is_identity_or_2d_translation = false;
     bool affected_by_outer_viewport_bounds_delta = false;
+    // TODO(crbug.com/937929): Put this into CompositingReasons when we can
+    // detect composited animation status changes in LayoutObject::SetStyle().
+    bool is_running_animation_on_compositor = false;
     BackfaceVisibility backface_visibility = BackfaceVisibility::kInherited;
     unsigned rendering_context_id = 0;
     CompositingReasons direct_compositing_reasons = CompositingReason::kNone;
     CompositorElementId compositor_element_id;
     std::unique_ptr<CompositorStickyConstraint> sticky_constraint;
 
-    bool operator==(const State& o) const {
-      return EqualIgnoringAnimatingProperties(o, false);
+    PaintPropertyChangeType CheckChange(const State& other) const {
+      if (origin != other.origin ||
+          flattens_inherited_transform != other.flattens_inherited_transform ||
+          affected_by_outer_viewport_bounds_delta !=
+              other.affected_by_outer_viewport_bounds_delta ||
+          backface_visibility != other.backface_visibility ||
+          rendering_context_id != other.rendering_context_id ||
+          direct_compositing_reasons != other.direct_compositing_reasons ||
+          compositor_element_id != other.compositor_element_id ||
+          scroll != other.scroll ||
+          affected_by_outer_viewport_bounds_delta !=
+              other.affected_by_outer_viewport_bounds_delta ||
+          !StickyConstraintEquals(other)) {
+        return PaintPropertyChangeType::kChangedOnlyValues;
+      }
+      bool matrix_changed = matrix != other.matrix;
+      if (!is_running_animation_on_compositor && matrix_changed) {
+        return PaintPropertyChangeType::kChangedOnlyValues;
+      }
+      if (is_running_animation_on_compositor !=
+          other.is_running_animation_on_compositor) {
+        return PaintPropertyChangeType::kChangedOnlyCompositedAnimationStatus;
+      }
+      if (matrix_changed) {
+        return PaintPropertyChangeType::kChangedOnlyCompositedAnimationValues;
+      }
+      return PaintPropertyChangeType::kUnchanged;
     }
 
-    bool EqualIgnoringAnimatingProperties(const State& o,
-                                          bool check_for_animations) const {
-      bool matrix_equal = ((matrix == o.matrix) && (origin == o.origin)) ||
-                          (check_for_animations &&
-                           (direct_compositing_reasons &
-                            CompositingReason::kActiveTransformAnimation));
-      return flattens_inherited_transform == o.flattens_inherited_transform &&
-             backface_visibility == o.backface_visibility &&
-             rendering_context_id == o.rendering_context_id &&
-             direct_compositing_reasons == o.direct_compositing_reasons &&
-             compositor_element_id == o.compositor_element_id &&
-             scroll == o.scroll &&
-             affected_by_outer_viewport_bounds_delta ==
-                 o.affected_by_outer_viewport_bounds_delta &&
-             ((!sticky_constraint && !o.sticky_constraint) ||
-              (sticky_constraint && o.sticky_constraint &&
-               *sticky_constraint == *o.sticky_constraint)) &&
-             matrix_equal;
+    bool StickyConstraintEquals(const State& other) const {
+      if (!sticky_constraint && !other.sticky_constraint)
+        return true;
+      return sticky_constraint && other.sticky_constraint &&
+             *sticky_constraint == *other.sticky_constraint;
     }
   };
 
@@ -101,24 +117,18 @@
         &parent, State{}, true /* is_parent_alias */));
   }
 
-  bool Update(const TransformPaintPropertyNode& parent, State&& state) {
-    bool parent_changed = SetParent(&parent);
-    if (state == state_)
-      return parent_changed;
-
-    DCHECK(!IsParentAlias()) << "Changed the state of an alias node.";
-    state_ = std::move(state);
-    SetChanged();
-    CheckAndUpdateIsIdentityOr2DTranslation();
-    Validate();
-    return true;
-  }
-
-  bool HaveNonAnimatingPropertiesChanged(
-      const TransformPaintPropertyNode& parent,
-      State& state) const {
-    return !state.EqualIgnoringAnimatingProperties(state_, true) ||
-           HasParentChanged(&parent);
+  PaintPropertyChangeType Update(const TransformPaintPropertyNode& parent,
+                                 State&& state) {
+    auto parent_changed = SetParent(&parent);
+    auto state_changed = state_.CheckChange(state);
+    if (state_changed != PaintPropertyChangeType::kUnchanged) {
+      DCHECK(!IsParentAlias()) << "Changed the state of an alias node.";
+      state_ = std::move(state);
+      SetChanged();
+      CheckAndUpdateIsIdentityOr2DTranslation();
+      Validate();
+    }
+    return std::max(parent_changed, state_changed);
   }
 
   // If |relative_to_node| is an ancestor of |this|, returns true if any node is
@@ -186,14 +196,13 @@
   bool HasDirectCompositingReasons() const {
     return DirectCompositingReasons() != CompositingReason::kNone;
   }
-  bool RequiresCompositingForAnimation() const {
-    return DirectCompositingReasons() &
-           CompositingReason::kComboActiveAnimation;
-  }
   bool HasActiveTransformAnimation() const {
     return DirectCompositingReasons() &
            CompositingReason::kActiveTransformAnimation;
   }
+  bool IsRunningAnimationOnCompositor() const {
+    return state_.is_running_animation_on_compositor;
+  }
 
   bool RequiresCompositingForRootScroller() const {
     return state_.direct_compositing_reasons & CompositingReason::kRootScroller;
@@ -243,6 +252,7 @@
 
   void Validate() const {
 #if DCHECK_IS_ON()
+    DCHECK(!IsRunningAnimationOnCompositor() || HasActiveTransformAnimation());
     if (state_.scroll) {
       // If there is an associated scroll node, this can only be a 2d
       // translation for scroll offset.
diff --git a/third_party/blink/renderer/platform/heap/address_cache.h b/third_party/blink/renderer/platform/heap/address_cache.h
index 526594f..6cb252a 100644
--- a/third_party/blink/renderer/platform/heap/address_cache.h
+++ b/third_party/blink/renderer/platform/heap/address_cache.h
@@ -6,6 +6,7 @@
 #define THIRD_PARTY_BLINK_RENDERER_PLATFORM_HEAP_ADDRESS_CACHE_H_
 
 #include "third_party/blink/renderer/platform/heap/blink_gc.h"
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
 
 namespace blink {
 
@@ -17,6 +18,8 @@
 
  public:
   class PLATFORM_EXPORT EnabledScope {
+    STACK_ALLOCATED();
+
    public:
     explicit EnabledScope(AddressCache*);
     ~EnabledScope();
diff --git a/third_party/blink/renderer/platform/heap/atomic_entry_flag.h b/third_party/blink/renderer/platform/heap/atomic_entry_flag.h
index 0404e9e..c89749e6 100644
--- a/third_party/blink/renderer/platform/heap/atomic_entry_flag.h
+++ b/third_party/blink/renderer/platform/heap/atomic_entry_flag.h
@@ -7,6 +7,8 @@
 
 #include <atomic>
 
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
+
 namespace blink {
 
 // A flag which provides a fast check whether a scope may be entered on the
@@ -27,6 +29,8 @@
 // And so if a thread observes zero, it must be because it has observed an equal
 // number of exits as entries.
 class AtomicEntryFlag {
+  DISALLOW_NEW();
+
  public:
   inline void Enter() { entries_.fetch_add(1, std::memory_order_relaxed); }
   inline void Exit() { entries_.fetch_sub(1, std::memory_order_relaxed); }
diff --git a/third_party/blink/renderer/platform/heap/heap.h b/third_party/blink/renderer/platform/heap/heap.h
index ab038ce..bba0cf1d 100644
--- a/third_party/blink/renderer/platform/heap/heap.h
+++ b/third_party/blink/renderer/platform/heap/heap.h
@@ -77,6 +77,8 @@
     Worklist<CustomCallbackItem, 256 /* local entries */>;
 
 class PLATFORM_EXPORT HeapAllocHooks {
+  STATIC_ONLY(HeapAllocHooks);
+
  public:
   // TODO(hajimehoshi): Pass a type name of the allocated object.
   typedef void AllocationHook(Address, size_t, const char*);
@@ -157,6 +159,8 @@
 }  // namespace internal
 
 class PLATFORM_EXPORT ThreadHeap {
+  USING_FAST_MALLOC(ThreadHeap);
+
  public:
   explicit ThreadHeap(ThreadState*);
   ~ThreadHeap();
diff --git a/third_party/blink/renderer/platform/heap/heap_compact.cc b/third_party/blink/renderer/platform/heap/heap_compact.cc
index e093416..ee869a2 100644
--- a/third_party/blink/renderer/platform/heap/heap_compact.cc
+++ b/third_party/blink/renderer/platform/heap/heap_compact.cc
@@ -13,6 +13,7 @@
 #include "third_party/blink/renderer/platform/heap/sparse_heap_bitmap.h"
 #include "third_party/blink/renderer/platform/histogram.h"
 #include "third_party/blink/renderer/platform/runtime_enabled_features.h"
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
 #include "third_party/blink/renderer/platform/wtf/hash_map.h"
 #include "third_party/blink/renderer/platform/wtf/time.h"
 
@@ -28,6 +29,8 @@
 // The "fixups" object is created and maintained for the lifetime of one
 // heap compaction-enhanced GC.
 class HeapCompact::MovableObjectFixups final {
+  USING_FAST_MALLOC(HeapCompact::MovableObjectFixups);
+
  public:
   static std::unique_ptr<MovableObjectFixups> Create(ThreadHeap* heap) {
     return base::WrapUnique(new MovableObjectFixups(heap));
diff --git a/third_party/blink/renderer/platform/heap/heap_stats_collector.h b/third_party/blink/renderer/platform/heap/heap_stats_collector.h
index 2e8880d..15ff884 100644
--- a/third_party/blink/renderer/platform/heap/heap_stats_collector.h
+++ b/third_party/blink/renderer/platform/heap/heap_stats_collector.h
@@ -10,6 +10,7 @@
 #include "third_party/blink/renderer/platform/heap/blink_gc.h"
 #include "third_party/blink/renderer/platform/instrumentation/tracing/trace_event.h"
 #include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
 #include "third_party/blink/renderer/platform/wtf/time.h"
 
 namespace blink {
@@ -23,6 +24,8 @@
 //   stats_collector.NotifySweepingFinished();
 //   // Previous event is available using stats_collector.previous().
 class PLATFORM_EXPORT ThreadHeapStatsCollector {
+  USING_FAST_MALLOC(ThreadHeapStatsCollector);
+
  public:
   // These ids will form human readable names when used in Scopes.
   enum Id {
diff --git a/third_party/blink/renderer/platform/heap/heap_test.cc b/third_party/blink/renderer/platform/heap/heap_test.cc
index f0b33de..ab281cb 100644
--- a/third_party/blink/renderer/platform/heap/heap_test.cc
+++ b/third_party/blink/renderer/platform/heap/heap_test.cc
@@ -56,6 +56,7 @@
 #include "third_party/blink/renderer/platform/scheduler/public/post_cross_thread_task.h"
 #include "third_party/blink/renderer/platform/scheduler/public/thread.h"
 #include "third_party/blink/renderer/platform/testing/unit_test_helpers.h"
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
 #include "third_party/blink/renderer/platform/wtf/hash_traits.h"
 #include "third_party/blink/renderer/platform/wtf/linked_hash_set.h"
 
@@ -113,6 +114,8 @@
               "HeapHashMap<int, IntWrapper> must be traceable.");
 
 class KeyWithCopyingMoveConstructor final {
+  DISALLOW_NEW();
+
  public:
   struct Hash final {
     STATIC_ONLY(Hash);
@@ -163,6 +166,8 @@
               "Persistent handle should stay small");
 
 class ThreadMarker {
+  DISALLOW_NEW();
+
  public:
   ThreadMarker()
       : creating_thread_(reinterpret_cast<ThreadState*>(0)), num_(0) {}
@@ -361,6 +366,8 @@
 namespace blink {
 
 class TestGCCollectGarbageScope {
+  STACK_ALLOCATED();
+
  public:
   explicit TestGCCollectGarbageScope(BlinkGC::StackState state) {
     DCHECK(ThreadState::Current()->CheckThread());
@@ -468,6 +475,8 @@
 };
 
 class OffHeapInt : public RefCounted<OffHeapInt> {
+  USING_FAST_MALLOC(OffHeapInt);
+
  public:
   static scoped_refptr<OffHeapInt> Create(int x) {
     return base::AdoptRef(new OffHeapInt(x));
@@ -3366,6 +3375,8 @@
 }
 
 class ThingWithDestructor {
+  DISALLOW_NEW();
+
  public:
   ThingWithDestructor() : x_(kEmptyValue) { live_things_with_destructor_++; }
 
diff --git a/third_party/blink/renderer/platform/heap/thread_state.h b/third_party/blink/renderer/platform/heap/thread_state.h
index a045e5c..afe8b498 100644
--- a/third_party/blink/renderer/platform/heap/thread_state.h
+++ b/third_party/blink/renderer/platform/heap/thread_state.h
@@ -120,6 +120,8 @@
   using UsingPreFinalizerMacroNeedsTrailingSemiColon = char
 
 class PLATFORM_EXPORT BlinkGCObserver {
+  USING_FAST_MALLOC(BlinkGCObserver);
+
  public:
   // The constructor automatically register this object to ThreadState's
   // observer lists. The argument must not be null.
@@ -407,6 +409,8 @@
   // USING_PRE_FINALIZER().
   template <typename T>
   class PrefinalizerRegistration final {
+    DISALLOW_NEW();
+
    public:
     PrefinalizerRegistration(T* self) {
       static_assert(sizeof(&T::InvokePreFinalizer) > 0,
diff --git a/third_party/blink/renderer/platform/heap/thread_state_scopes.h b/third_party/blink/renderer/platform/heap/thread_state_scopes.h
index 32332521..48393b4d 100644
--- a/third_party/blink/renderer/platform/heap/thread_state_scopes.h
+++ b/third_party/blink/renderer/platform/heap/thread_state_scopes.h
@@ -92,6 +92,8 @@
 
 // Used to mark when we are in an atomic pause for GC.
 class ThreadState::AtomicPauseScope final {
+  STACK_ALLOCATED();
+
  public:
   explicit AtomicPauseScope(ThreadState* thread_state)
       : thread_state_(thread_state), gc_forbidden_scope(thread_state) {
diff --git a/third_party/blink/renderer/platform/heap/visitor.h b/third_party/blink/renderer/platform/heap/visitor.h
index 2ace73ac..68ef181 100644
--- a/third_party/blink/renderer/platform/heap/visitor.h
+++ b/third_party/blink/renderer/platform/heap/visitor.h
@@ -75,6 +75,8 @@
 
 // Visitor is used to traverse Oilpan's object graph.
 class PLATFORM_EXPORT Visitor {
+  USING_FAST_MALLOC(Visitor);
+
  public:
   explicit Visitor(ThreadState* state) : state_(state) {}
   virtual ~Visitor() = default;
diff --git a/third_party/blink/renderer/platform/heap/worklist.h b/third_party/blink/renderer/platform/heap/worklist.h
index fb5c5dd83..b0f6fa710 100644
--- a/third_party/blink/renderer/platform/heap/worklist.h
+++ b/third_party/blink/renderer/platform/heap/worklist.h
@@ -39,6 +39,8 @@
   using EntryType = _EntryType;
 
   class View {
+    DISALLOW_NEW();
+
    public:
     View(WorklistType* worklist, int task_id)
         : worklist_(worklist), task_id_(task_id) {}
@@ -259,6 +261,8 @@
   };
 
   class GlobalPool {
+    DISALLOW_NEW();
+
    public:
     GlobalPool() : top_(nullptr) {}
 
diff --git a/third_party/blink/renderer/platform/histogram.cc b/third_party/blink/renderer/platform/histogram.cc
index 72ed849..b2d3642 100644
--- a/third_party/blink/renderer/platform/histogram.cc
+++ b/third_party/blink/renderer/platform/histogram.cc
@@ -31,6 +31,11 @@
       delta.InMicroseconds()));
 }
 
+void CustomCountHistogram::CountMilliseconds(base::TimeDelta delta) {
+  Count(base::saturated_cast<base::HistogramBase::Sample>(
+      delta.InMilliseconds()));
+}
+
 BooleanHistogram::BooleanHistogram(const char* name)
     : CustomCountHistogram(base::BooleanHistogram::FactoryGet(
           name,
diff --git a/third_party/blink/renderer/platform/histogram.h b/third_party/blink/renderer/platform/histogram.h
index 37337de7..0df2eae6 100644
--- a/third_party/blink/renderer/platform/histogram.h
+++ b/third_party/blink/renderer/platform/histogram.h
@@ -27,6 +27,7 @@
                        int32_t bucket_count);
   void Count(base::HistogramBase::Sample);
   void CountMicroseconds(base::TimeDelta);
+  void CountMilliseconds(base::TimeDelta);
 
  protected:
   explicit CustomCountHistogram(base::HistogramBase*);
diff --git a/third_party/blink/renderer/platform/loader/fetch/cached_metadata.h b/third_party/blink/renderer/platform/loader/fetch/cached_metadata.h
index 474c560..2c1aeb1 100644
--- a/third_party/blink/renderer/platform/loader/fetch/cached_metadata.h
+++ b/third_party/blink/renderer/platform/loader/fetch/cached_metadata.h
@@ -34,6 +34,7 @@
 #include <stdint.h>
 #include "base/memory/scoped_refptr.h"
 #include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
 #include "third_party/blink/renderer/platform/wtf/assertions.h"
 #include "third_party/blink/renderer/platform/wtf/ref_counted.h"
 #include "third_party/blink/renderer/platform/wtf/std_lib_extras.h"
@@ -51,6 +52,8 @@
 // Serialized data is NOT portable across architectures. However, reading the
 // data type ID will reject data generated with a different byte-order.
 class PLATFORM_EXPORT CachedMetadata : public RefCounted<CachedMetadata> {
+  USING_FAST_MALLOC(CachedMetadata);
+
  public:
   static scoped_refptr<CachedMetadata> Create(uint32_t data_type_id,
                                               const uint8_t* data,
diff --git a/third_party/blink/renderer/platform/loader/fetch/cached_metadata_handler.h b/third_party/blink/renderer/platform/loader/fetch/cached_metadata_handler.h
index ed47029..44d39b99 100644
--- a/third_party/blink/renderer/platform/loader/fetch/cached_metadata_handler.h
+++ b/third_party/blink/renderer/platform/loader/fetch/cached_metadata_handler.h
@@ -11,6 +11,7 @@
 #include "third_party/blink/renderer/platform/heap/handle.h"
 #include "third_party/blink/renderer/platform/platform_export.h"
 #include "third_party/blink/renderer/platform/weborigin/security_origin.h"
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
 #include "third_party/blink/renderer/platform/wtf/forward.h"
 
 namespace blink {
@@ -22,6 +23,8 @@
 // A callback for sending the serialized data of cached metadata back to the
 // platform.
 class PLATFORM_EXPORT CachedMetadataSender {
+  USING_FAST_MALLOC(CachedMetadataSender);
+
  public:
   static std::unique_ptr<CachedMetadataSender> Create(
       const ResourceResponse&,
diff --git a/third_party/blink/renderer/platform/loader/fetch/integrity_metadata.h b/third_party/blink/renderer/platform/loader/fetch/integrity_metadata.h
index 5854670..46a96cd 100644
--- a/third_party/blink/renderer/platform/loader/fetch/integrity_metadata.h
+++ b/third_party/blink/renderer/platform/loader/fetch/integrity_metadata.h
@@ -6,6 +6,7 @@
 #define THIRD_PARTY_BLINK_RENDERER_PLATFORM_LOADER_FETCH_INTEGRITY_METADATA_H_
 
 #include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
 #include "third_party/blink/renderer/platform/wtf/hash_set.h"
 #include "third_party/blink/renderer/platform/wtf/string_hasher.h"
 #include "third_party/blink/renderer/platform/wtf/text/string_hash.h"
@@ -20,6 +21,8 @@
 using IntegrityMetadataSet = WTF::HashSet<IntegrityMetadataPair>;
 
 class PLATFORM_EXPORT IntegrityMetadata {
+  STACK_ALLOCATED();
+
  public:
   IntegrityMetadata() = default;
   IntegrityMetadata(String digest, IntegrityAlgorithm);
diff --git a/third_party/blink/renderer/platform/loader/fetch/memory_cache_correctness_test.cc b/third_party/blink/renderer/platform/loader/fetch/memory_cache_correctness_test.cc
index 6e41f59..65f9d46 100644
--- a/third_party/blink/renderer/platform/loader/fetch/memory_cache_correctness_test.cc
+++ b/third_party/blink/renderer/platform/loader/fetch/memory_cache_correctness_test.cc
@@ -140,7 +140,7 @@
 
 TEST_F(MemoryCacheCorrectnessTest, FreshFromLastModified) {
   ResourceResponse fresh200_response;
-  fresh200_response.SetHTTPStatusCode(200);
+  fresh200_response.SetHttpStatusCode(200);
   fresh200_response.SetHTTPHeaderField("Date", kOriginalRequestDateAsString);
   fresh200_response.SetHTTPHeaderField("Last-Modified",
                                        kOneDayBeforeOriginalRequest);
@@ -157,7 +157,7 @@
 
 TEST_F(MemoryCacheCorrectnessTest, FreshFromExpires) {
   ResourceResponse fresh200_response;
-  fresh200_response.SetHTTPStatusCode(200);
+  fresh200_response.SetHttpStatusCode(200);
   fresh200_response.SetHTTPHeaderField("Date", kOriginalRequestDateAsString);
   fresh200_response.SetHTTPHeaderField("Expires", kOneDayAfterOriginalRequest);
 
@@ -173,7 +173,7 @@
 
 TEST_F(MemoryCacheCorrectnessTest, FreshFromMaxAge) {
   ResourceResponse fresh200_response;
-  fresh200_response.SetHTTPStatusCode(200);
+  fresh200_response.SetHttpStatusCode(200);
   fresh200_response.SetHTTPHeaderField("Date", kOriginalRequestDateAsString);
   fresh200_response.SetHTTPHeaderField("Cache-Control", "max-age=600");
 
@@ -189,7 +189,7 @@
 
 TEST_F(MemoryCacheCorrectnessTest, ExpiredFromLastModified) {
   ResourceResponse expired200_response;
-  expired200_response.SetHTTPStatusCode(200);
+  expired200_response.SetHttpStatusCode(200);
   expired200_response.SetHTTPHeaderField("Date", kOriginalRequestDateAsString);
   expired200_response.SetHTTPHeaderField("Last-Modified",
                                          kOneDayBeforeOriginalRequest);
@@ -210,7 +210,7 @@
 
 TEST_F(MemoryCacheCorrectnessTest, ExpiredFromExpires) {
   ResourceResponse expired200_response;
-  expired200_response.SetHTTPStatusCode(200);
+  expired200_response.SetHttpStatusCode(200);
   expired200_response.SetHTTPHeaderField("Date", kOriginalRequestDateAsString);
   expired200_response.SetHTTPHeaderField("Expires",
                                          kOneDayAfterOriginalRequest);
@@ -229,7 +229,7 @@
 // shouldn't have list of available resources logic.
 TEST_F(MemoryCacheCorrectnessTest, NewMockResourceExpiredFromExpires) {
   ResourceResponse expired200_response;
-  expired200_response.SetHTTPStatusCode(200);
+  expired200_response.SetHttpStatusCode(200);
   expired200_response.SetHTTPHeaderField("Date", kOriginalRequestDateAsString);
   expired200_response.SetHTTPHeaderField("Expires",
                                          kOneDayAfterOriginalRequest);
@@ -249,7 +249,7 @@
 // bypassed.
 TEST_F(MemoryCacheCorrectnessTest, ReuseMockResourceExpiredFromExpires) {
   ResourceResponse expired200_response;
-  expired200_response.SetHTTPStatusCode(200);
+  expired200_response.SetHttpStatusCode(200);
   expired200_response.SetHTTPHeaderField("Date", kOriginalRequestDateAsString);
   expired200_response.SetHTTPHeaderField("Expires",
                                          kOneDayAfterOriginalRequest);
@@ -272,7 +272,7 @@
 
 TEST_F(MemoryCacheCorrectnessTest, ExpiredFromMaxAge) {
   ResourceResponse expired200_response;
-  expired200_response.SetHTTPStatusCode(200);
+  expired200_response.SetHttpStatusCode(200);
   expired200_response.SetHTTPHeaderField("Date", kOriginalRequestDateAsString);
   expired200_response.SetHTTPHeaderField("Cache-Control", "max-age=600");
 
@@ -288,7 +288,7 @@
 
 TEST_F(MemoryCacheCorrectnessTest, FreshButNoCache) {
   ResourceResponse fresh200_nocache_response;
-  fresh200_nocache_response.SetHTTPStatusCode(200);
+  fresh200_nocache_response.SetHttpStatusCode(200);
   fresh200_nocache_response.SetHTTPHeaderField(http_names::kDate,
                                                kOriginalRequestDateAsString);
   fresh200_nocache_response.SetHTTPHeaderField(http_names::kExpires,
@@ -319,7 +319,7 @@
 
 TEST_F(MemoryCacheCorrectnessTest, FreshButNoStore) {
   ResourceResponse fresh200_nostore_response;
-  fresh200_nostore_response.SetHTTPStatusCode(200);
+  fresh200_nostore_response.SetHttpStatusCode(200);
   fresh200_nostore_response.SetHTTPHeaderField(http_names::kDate,
                                                kOriginalRequestDateAsString);
   fresh200_nostore_response.SetHTTPHeaderField(http_names::kExpires,
@@ -352,7 +352,7 @@
 // See crbug.com/340088 .
 TEST_F(MemoryCacheCorrectnessTest, DISABLED_FreshButMustRevalidate) {
   ResourceResponse fresh200_must_revalidate_response;
-  fresh200_must_revalidate_response.SetHTTPStatusCode(200);
+  fresh200_must_revalidate_response.SetHttpStatusCode(200);
   fresh200_must_revalidate_response.SetHTTPHeaderField(
       http_names::kDate, kOriginalRequestDateAsString);
   fresh200_must_revalidate_response.SetHTTPHeaderField(
@@ -381,7 +381,7 @@
   MockResource* first_resource = MockResource::Create(request);
 
   ResourceResponse fresh301_response(redirect_url);
-  fresh301_response.SetHTTPStatusCode(301);
+  fresh301_response.SetHttpStatusCode(301);
   fresh301_response.SetHTTPHeaderField(http_names::kDate,
                                        kOriginalRequestDateAsString);
   fresh301_response.SetHTTPHeaderField(http_names::kLocation,
@@ -396,7 +396,7 @@
 
   // Add the final response to our request.
   ResourceResponse fresh200_response(redirect_target_url);
-  fresh200_response.SetHTTPStatusCode(200);
+  fresh200_response.SetHttpStatusCode(200);
   fresh200_response.SetHTTPHeaderField(http_names::kDate,
                                        kOriginalRequestDateAsString);
   fresh200_response.SetHTTPHeaderField(http_names::kExpires,
@@ -423,7 +423,7 @@
   MockResource* first_resource = MockResource::Create(request);
 
   ResourceResponse stale301_response(redirect_url);
-  stale301_response.SetHTTPStatusCode(301);
+  stale301_response.SetHttpStatusCode(301);
   stale301_response.SetHTTPHeaderField(http_names::kDate,
                                        kOriginalRequestDateAsString);
   stale301_response.SetHTTPHeaderField(http_names::kLocation,
@@ -436,7 +436,7 @@
 
   // Add the final response to our request.
   ResourceResponse fresh200_response(redirect_target_url);
-  fresh200_response.SetHTTPStatusCode(200);
+  fresh200_response.SetHttpStatusCode(200);
   fresh200_response.SetHTTPHeaderField(http_names::kDate,
                                        kOriginalRequestDateAsString);
   fresh200_response.SetHTTPHeaderField(http_names::kExpires,
@@ -478,7 +478,7 @@
       redirect_url, GetSecurityOrigin(), ResourceType::kRaw);
 
   ResourceResponse fresh302_response(redirect_url);
-  fresh302_response.SetHTTPStatusCode(302);
+  fresh302_response.SetHttpStatusCode(302);
   fresh302_response.SetHTTPHeaderField(http_names::kDate,
                                        kOriginalRequestDateAsString);
   fresh302_response.SetHTTPHeaderField(http_names::kLastModified,
@@ -493,7 +493,7 @@
 
   // Add the final response to our request.
   ResourceResponse fresh200_response(redirect_target_url);
-  fresh200_response.SetHTTPStatusCode(200);
+  fresh200_response.SetHttpStatusCode(200);
   fresh200_response.SetHTTPHeaderField(http_names::kDate,
                                        kOriginalRequestDateAsString);
   fresh200_response.SetHTTPHeaderField(http_names::kExpires,
@@ -519,7 +519,7 @@
   MockResource* first_resource = MockResource::Create(request);
 
   ResourceResponse fresh302_response(redirect_url);
-  fresh302_response.SetHTTPStatusCode(302);
+  fresh302_response.SetHttpStatusCode(302);
   fresh302_response.SetHTTPHeaderField(http_names::kDate,
                                        kOriginalRequestDateAsString);
   fresh302_response.SetHTTPHeaderField(http_names::kCacheControl,
@@ -534,7 +534,7 @@
 
   // Add the final response to our request.
   ResourceResponse fresh200_response(redirect_target_url);
-  fresh200_response.SetHTTPStatusCode(200);
+  fresh200_response.SetHttpStatusCode(200);
   fresh200_response.SetHTTPHeaderField(http_names::kDate,
                                        kOriginalRequestDateAsString);
   fresh200_response.SetHTTPHeaderField(http_names::kExpires,
@@ -560,7 +560,7 @@
   MockResource* first_resource = MockResource::Create(request);
 
   ResourceResponse fresh302_response(redirect_url);
-  fresh302_response.SetHTTPStatusCode(302);
+  fresh302_response.SetHttpStatusCode(302);
   fresh302_response.SetHTTPHeaderField(http_names::kDate,
                                        kOriginalRequestDateAsString);
   fresh302_response.SetHTTPHeaderField(http_names::kExpires,
@@ -574,7 +574,7 @@
 
   // Add the final response to our request.
   ResourceResponse fresh200_response(redirect_target_url);
-  fresh200_response.SetHTTPStatusCode(200);
+  fresh200_response.SetHttpStatusCode(200);
   fresh200_response.SetHTTPHeaderField(http_names::kDate,
                                        kOriginalRequestDateAsString);
   fresh200_response.SetHTTPHeaderField(http_names::kExpires,
diff --git a/third_party/blink/renderer/platform/loader/fetch/raw_resource.h b/third_party/blink/renderer/platform/loader/fetch/raw_resource.h
index c425b48f..8326e38 100644
--- a/third_party/blink/renderer/platform/loader/fetch/raw_resource.h
+++ b/third_party/blink/renderer/platform/loader/fetch/raw_resource.h
@@ -30,6 +30,7 @@
 #include "third_party/blink/renderer/platform/loader/fetch/resource_client.h"
 #include "third_party/blink/renderer/platform/loader/fetch/resource_loader_options.h"
 #include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
 
 namespace blink {
 class BytesConsumer;
@@ -202,6 +203,8 @@
 // Checks the sequence of callbacks of RawResourceClient. This can be used only
 // when a RawResourceClient is added as a client to at most one RawResource.
 class PLATFORM_EXPORT RawResourceClientStateChecker final {
+  DISALLOW_NEW();
+
  public:
   RawResourceClientStateChecker();
   ~RawResourceClientStateChecker();
diff --git a/third_party/blink/renderer/platform/loader/fetch/resource_fetcher_test.cc b/third_party/blink/renderer/platform/loader/fetch/resource_fetcher_test.cc
index cbfefe78..01b2ebc2 100644
--- a/third_party/blink/renderer/platform/loader/fetch/resource_fetcher_test.cc
+++ b/third_party/blink/renderer/platform/loader/fetch/resource_fetcher_test.cc
@@ -153,7 +153,7 @@
 
   KURL url("http://127.0.0.1:8000/foo.html");
   ResourceResponse response(url);
-  response.SetHTTPStatusCode(200);
+  response.SetHttpStatusCode(200);
   response.SetHTTPHeaderField(http_names::kCacheControl, "max-age=3600");
   RegisterMockedURLLoadWithCustomResponse(url, response);
 
@@ -182,7 +182,7 @@
       RawResource::CreateForTest(url, source_origin, ResourceType::kRaw);
   AddResourceToMemoryCache(resource);
   ResourceResponse response(url);
-  response.SetHTTPStatusCode(200);
+  response.SetHttpStatusCode(200);
   response.SetHTTPHeaderField(http_names::kCacheControl, "max-age=3600");
   resource->ResponseReceived(response);
   resource->FinishForTest();
@@ -213,7 +213,7 @@
   AddResourceToMemoryCache(resource);
 
   ResourceResponse response(url);
-  response.SetHTTPStatusCode(200);
+  response.SetHttpStatusCode(200);
   response.SetHTTPHeaderField(http_names::kCacheControl, "max-age=3600");
   response.SetHTTPHeaderField(http_names::kVary, "*");
   resource->ResponseReceived(response);
@@ -237,7 +237,7 @@
   info->AddFinalTransferSize(5);
   EXPECT_EQ(info->TransferSize(), 5);
   ResourceResponse redirect_response(KURL("https://example.com/original"));
-  redirect_response.SetHTTPStatusCode(200);
+  redirect_response.SetHttpStatusCode(200);
   redirect_response.SetEncodedDataLength(7);
   info->AddRedirect(redirect_response, KURL("https://example.com/redirect"));
   EXPECT_EQ(info->TransferSize(), 12);
@@ -255,7 +255,7 @@
   AddResourceToMemoryCache(resource);
 
   ResourceResponse response(url);
-  response.SetHTTPStatusCode(200);
+  response.SetHttpStatusCode(200);
   response.SetHTTPHeaderField(http_names::kCacheControl, "max-age=3600");
   response.SetHTTPHeaderField(http_names::kVary, "*");
   resource->ResponseReceived(response);
@@ -275,7 +275,7 @@
 
   KURL url("http://127.0.0.1:8000/foo.html");
   ResourceResponse response(url);
-  response.SetHTTPStatusCode(200);
+  response.SetHttpStatusCode(200);
   response.SetHTTPHeaderField(http_names::kCacheControl, "max-age=3600");
   response.SetHTTPHeaderField(http_names::kVary, "*");
   RegisterMockedURLLoadWithCustomResponse(url, response);
@@ -342,7 +342,7 @@
   KURL url("http://127.0.0.1:8000/foo.png");
 
   ResourceResponse response(url);
-  response.SetHTTPStatusCode(200);
+  response.SetHttpStatusCode(200);
   response.SetHTTPHeaderField(http_names::kCacheControl, "max-age=3600");
   response.SetHTTPHeaderField(http_names::kETag, "1234567890");
   RegisterMockedURLLoadWithCustomResponse(url, response);
@@ -456,7 +456,7 @@
     KURL redirect_url(from_url);
     WebURLResponse redirect_response;
     redirect_response.SetCurrentRequestUrl(redirect_url);
-    redirect_response.SetHTTPStatusCode(301);
+    redirect_response.SetHttpStatusCode(301);
     redirect_response.SetHTTPHeaderField(http_names::kLocation, to_url);
     redirect_response.SetEncodedDataLength(kRedirectResponseOverheadBytes);
     Platform::Current()->GetURLLoaderMockFactory()->RegisterURL(
@@ -779,7 +779,7 @@
   AddResourceToMemoryCache(resource);
 
   ResourceResponse response(url);
-  response.SetHTTPStatusCode(304);
+  response.SetHttpStatusCode(304);
   response.SetHTTPHeaderField("etag", "1234567890");
   resource->ResponseReceived(response);
   resource->FinishForTest();
@@ -846,7 +846,7 @@
 TEST_F(ResourceFetcherTest, ContentIdURL) {
   KURL url("cid:0123456789@example.com");
   ResourceResponse response(url);
-  response.SetHTTPStatusCode(200);
+  response.SetHttpStatusCode(200);
   RegisterMockedURLLoadWithCustomResponse(url, response);
 
   auto* fetcher = CreateFetcher();
@@ -873,7 +873,7 @@
   FetchParameters fetch_params{ResourceRequest(url)};
 
   ResourceResponse response(url);
-  response.SetHTTPStatusCode(200);
+  response.SetHttpStatusCode(200);
   response.SetHTTPHeaderField(http_names::kCacheControl,
                               "max-age=0, stale-while-revalidate=40");
 
@@ -898,7 +898,7 @@
   // memory cache after the revalidation completes.
   platform_->AdvanceClockSeconds(1);
   ResourceResponse revalidate_response(url);
-  revalidate_response.SetHTTPStatusCode(200);
+  revalidate_response.SetHttpStatusCode(200);
   platform_->GetURLLoaderMockFactory()->UnregisterURL(url);
   RegisterMockedURLLoadWithCustomResponse(url, revalidate_response);
   new_resource = MockResource::Fetch(fetch_params, fetcher, nullptr);
@@ -917,7 +917,7 @@
   // won't take a fast path.
   KURL url("http://127.0.0.1:8000/foo.html");
   ResourceResponse response(url);
-  response.SetHTTPStatusCode(200);
+  response.SetHttpStatusCode(200);
   RegisterMockedURLLoadWithCustomResponse(url, response);
   FetchParameters fetch_params{ResourceRequest(url)};
   MockResource::Fetch(fetch_params, fetcher, nullptr);
diff --git a/third_party/blink/renderer/platform/loader/fetch/resource_load_scheduler_test.cc b/third_party/blink/renderer/platform/loader/fetch/resource_load_scheduler_test.cc
index 10f02d6f..8db7407 100644
--- a/third_party/blink/renderer/platform/loader/fetch/resource_load_scheduler_test.cc
+++ b/third_party/blink/renderer/platform/loader/fetch/resource_load_scheduler_test.cc
@@ -12,6 +12,7 @@
 #include "third_party/blink/renderer/platform/runtime_enabled_features.h"
 #include "third_party/blink/renderer/platform/scheduler/test/fake_frame_scheduler.h"
 #include "third_party/blink/renderer/platform/testing/testing_platform_support.h"
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
 
 namespace blink {
 namespace {
@@ -23,6 +24,8 @@
  public:
   // A delegate that can be used to determine the order clients were run in.
   class MockClientDelegate {
+    DISALLOW_NEW();
+
    public:
     MockClientDelegate() = default;
     ~MockClientDelegate() = default;
diff --git a/third_party/blink/renderer/platform/loader/fetch/resource_loader.cc b/third_party/blink/renderer/platform/loader/fetch/resource_loader.cc
index 52b5ab3..d015a32 100644
--- a/third_party/blink/renderer/platform/loader/fetch/resource_loader.cc
+++ b/third_party/blink/renderer/platform/loader/fetch/resource_loader.cc
@@ -71,6 +71,7 @@
 #include "third_party/blink/renderer/platform/shared_buffer.h"
 #include "third_party/blink/renderer/platform/weborigin/scheme_registry.h"
 #include "third_party/blink/renderer/platform/weborigin/security_violation_reporting_policy.h"
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
 #include "third_party/blink/renderer/platform/wtf/assertions.h"
 #include "third_party/blink/renderer/platform/wtf/std_lib_extras.h"
 #include "third_party/blink/renderer/platform/wtf/text/string_builder.h"
@@ -167,6 +168,8 @@
 // if the response wasn't received.  One CodeCacheRequest handles only one
 // request. On a restart new CodeCacheRequest is created.
 class ResourceLoader::CodeCacheRequest {
+  USING_FAST_MALLOC(ResourceLoader::CodeCacheRequest);
+
  public:
   CodeCacheRequest(std::unique_ptr<CodeCacheLoader> code_cache_loader,
                    const KURL& url,
diff --git a/third_party/blink/renderer/platform/loader/fetch/resource_loader_test.cc b/third_party/blink/renderer/platform/loader/fetch/resource_loader_test.cc
index 2a00345..d8c05f1b 100644
--- a/third_party/blink/renderer/platform/loader/fetch/resource_loader_test.cc
+++ b/third_party/blink/renderer/platform/loader/fetch/resource_loader_test.cc
@@ -188,7 +188,7 @@
     ResourceLoader* loader = resource->Loader();
 
     ResourceResponse response(test.url);
-    response.SetHTTPStatusCode(200);
+    response.SetHttpStatusCode(200);
     response.SetType(test.original_response_type);
     response.SetWasFetchedViaServiceWorker(test.from == From::kServiceWorker);
     if (test.allowed_origin) {
@@ -218,7 +218,7 @@
   ResourceLoader* loader = resource->Loader();
 
   ResourceResponse response(url);
-  response.SetHTTPStatusCode(200);
+  response.SetHttpStatusCode(200);
 
   mojo::ScopedDataPipeProducerHandle producer;
   mojo::ScopedDataPipeConsumerHandle consumer;
@@ -569,14 +569,14 @@
 
 TEST_F(ResourceLoaderIsolatedCodeCacheTest, ResponseFromNetwork) {
   ResourceResponse response(foo_url_);
-  response.SetHTTPStatusCode(200);
+  response.SetHttpStatusCode(200);
   EXPECT_EQ(true, LoadAndCheckIsolatedCodeCache(response));
 }
 
 TEST_F(ResourceLoaderIsolatedCodeCacheTest,
        SyntheticResponseFromServiceWorker) {
   ResourceResponse response(foo_url_);
-  response.SetHTTPStatusCode(200);
+  response.SetHttpStatusCode(200);
   response.SetWasFetchedViaServiceWorker(true);
   EXPECT_EQ(false, LoadAndCheckIsolatedCodeCache(response));
 }
@@ -584,7 +584,7 @@
 TEST_F(ResourceLoaderIsolatedCodeCacheTest,
        PassThroughResponseFromServiceWorker) {
   ResourceResponse response(foo_url_);
-  response.SetHTTPStatusCode(200);
+  response.SetHttpStatusCode(200);
   response.SetWasFetchedViaServiceWorker(true);
   response.SetURLListViaServiceWorker(Vector<KURL>(1, foo_url_));
   EXPECT_EQ(true, LoadAndCheckIsolatedCodeCache(response));
@@ -593,7 +593,7 @@
 TEST_F(ResourceLoaderIsolatedCodeCacheTest,
        DifferentUrlResponseFromServiceWorker) {
   ResourceResponse response(foo_url_);
-  response.SetHTTPStatusCode(200);
+  response.SetHttpStatusCode(200);
   response.SetWasFetchedViaServiceWorker(true);
   response.SetURLListViaServiceWorker(Vector<KURL>(1, bar_url_));
   EXPECT_EQ(false, LoadAndCheckIsolatedCodeCache(response));
@@ -601,7 +601,7 @@
 
 TEST_F(ResourceLoaderIsolatedCodeCacheTest, CacheResponseFromServiceWorker) {
   ResourceResponse response(foo_url_);
-  response.SetHTTPStatusCode(200);
+  response.SetHttpStatusCode(200);
   response.SetWasFetchedViaServiceWorker(true);
   response.SetCacheStorageCacheName("dummy");
   // The browser does support code cache for cache_storage Responses, but they
diff --git a/third_party/blink/renderer/platform/loader/fetch/resource_response.cc b/third_party/blink/renderer/platform/loader/fetch/resource_response.cc
index 595737f..f41f3bf 100644
--- a/third_party/blink/renderer/platform/loader/fetch/resource_response.cc
+++ b/third_party/blink/renderer/platform/loader/fetch/resource_response.cc
@@ -174,7 +174,7 @@
   return http_status_code_;
 }
 
-void ResourceResponse::SetHTTPStatusCode(int status_code) {
+void ResourceResponse::SetHttpStatusCode(int status_code) {
   http_status_code_ = status_code;
 }
 
diff --git a/third_party/blink/renderer/platform/loader/fetch/resource_response.h b/third_party/blink/renderer/platform/loader/fetch/resource_response.h
index 882c3c6..988e3b9 100644
--- a/third_party/blink/renderer/platform/loader/fetch/resource_response.h
+++ b/third_party/blink/renderer/platform/loader/fetch/resource_response.h
@@ -42,6 +42,7 @@
 #include "third_party/blink/renderer/platform/network/http_parsers.h"
 #include "third_party/blink/renderer/platform/platform_export.h"
 #include "third_party/blink/renderer/platform/weborigin/kurl.h"
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
 #include "third_party/blink/renderer/platform/wtf/ref_counted.h"
 #include "third_party/blink/renderer/platform/wtf/text/cstring.h"
 #include "third_party/blink/renderer/platform/wtf/time.h"
@@ -56,6 +57,8 @@
 //
 // This class is thread-bound. Do not copy/pass an instance across threads.
 class PLATFORM_EXPORT ResourceResponse final {
+  USING_FAST_MALLOC(ResourceResponse);
+
  public:
   enum HTTPVersion : uint8_t {
     kHTTPVersionUnknown,
@@ -78,6 +81,8 @@
   };
 
   class PLATFORM_EXPORT SignedCertificateTimestamp final {
+    DISALLOW_NEW();
+
    public:
     SignedCertificateTimestamp(String status,
                                String origin,
@@ -194,7 +199,7 @@
   void SetTextEncodingName(const AtomicString&);
 
   int HttpStatusCode() const;
-  void SetHTTPStatusCode(int);
+  void SetHttpStatusCode(int);
 
   const AtomicString& HttpStatusText() const;
   void SetHTTPStatusText(const AtomicString&);
diff --git a/third_party/blink/renderer/platform/loader/fetch/resource_test.cc b/third_party/blink/renderer/platform/loader/fetch/resource_test.cc
index 1b108417..fbd1fd4e5 100644
--- a/third_party/blink/renderer/platform/loader/fetch/resource_test.cc
+++ b/third_party/blink/renderer/platform/loader/fetch/resource_test.cc
@@ -56,7 +56,7 @@
 
 ResourceResponse CreateTestResourceResponse() {
   ResourceResponse response(url_test_helpers::ToKURL("https://example.com/"));
-  response.SetHTTPStatusCode(200);
+  response.SetHttpStatusCode(200);
   return response;
 }
 
@@ -146,7 +146,7 @@
   ScopedTestingPlatformSupport<MockPlatform> mock;
   KURL url("http://127.0.0.1:8000/foo.html");
   ResourceResponse response(url);
-  response.SetHTTPStatusCode(200);
+  response.SetHttpStatusCode(200);
   MockResource* resource = MockResource::Create(url);
   resource->ResponseReceived(response);
   resource->FinishForTest();
@@ -156,7 +156,7 @@
   url.SetFragmentIdentifier("bar");
   resource->SetRevalidatingRequest(ResourceRequest(url));
   ResourceResponse revalidating_response(url);
-  revalidating_response.SetHTTPStatusCode(304);
+  revalidating_response.SetHttpStatusCode(304);
   resource->ResponseReceived(revalidating_response);
 }
 
@@ -164,7 +164,7 @@
   ScopedTestingPlatformSupport<MockPlatform> mock;
   const KURL url("http://127.0.0.1:8000/foo.html");
   ResourceResponse response(url);
-  response.SetHTTPStatusCode(200);
+  response.SetHttpStatusCode(200);
 
   MockResource* resource = MockResource::Create(url);
   resource->ResponseReceived(response);
@@ -222,7 +222,7 @@
   const KURL url("http://test.example.com/");
   MockResource* resource = MockResource::Create(ResourceRequest(url));
   ResourceResponse response(url);
-  response.SetHTTPStatusCode(200);
+  response.SetHttpStatusCode(200);
   resource->ResponseReceived(response);
   const char kData[5] = "abcd";
   resource->AppendData(kData, 4);
@@ -242,7 +242,7 @@
   resource->AddClient(client, nullptr);
 
   ResourceResponse revalidating_response(url);
-  revalidating_response.SetHTTPStatusCode(200);
+  revalidating_response.SetHttpStatusCode(200);
   resource->ResponseReceived(revalidating_response);
 
   EXPECT_FALSE(resource->IsCacheValidator());
@@ -270,7 +270,7 @@
   const KURL url("http://test.example.com/");
   MockResource* resource = MockResource::Create(ResourceRequest(url));
   ResourceResponse response(url);
-  response.SetHTTPStatusCode(200);
+  response.SetHttpStatusCode(200);
   resource->ResponseReceived(response);
   const char kData[5] = "abcd";
   resource->AppendData(kData, 4);
@@ -290,7 +290,7 @@
   resource->AddClient(client, nullptr);
 
   ResourceResponse revalidating_response(url);
-  revalidating_response.SetHTTPStatusCode(304);
+  revalidating_response.SetHttpStatusCode(304);
   resource->ResponseReceived(revalidating_response);
 
   EXPECT_FALSE(resource->IsCacheValidator());
@@ -312,7 +312,7 @@
   const KURL url("http://test.example.com/");
   Resource* resource = MockResource::Create(ResourceRequest(url));
   ResourceResponse response(url);
-  response.SetHTTPStatusCode(200);
+  response.SetHttpStatusCode(200);
   resource->ResponseReceived(response);
   resource->FinishForTest();
   GetMemoryCache()->Add(resource);
@@ -325,7 +325,7 @@
   resource->AddClient(client, nullptr);
 
   ResourceResponse revalidating_response(url);
-  revalidating_response.SetHTTPStatusCode(304);
+  revalidating_response.SetHttpStatusCode(304);
   resource->ResponseReceived(revalidating_response);
   EXPECT_FALSE(resource->IsCacheValidator());
   EXPECT_EQ(200, resource->GetResponse().HttpStatusCode());
@@ -344,7 +344,7 @@
   const KURL url("http://test.example.com/");
   Resource* resource = MockResource::Create(ResourceRequest(url));
   ResourceResponse response(url);
-  response.SetHTTPStatusCode(200);
+  response.SetHttpStatusCode(200);
   response.AddHTTPHeaderField("keep-alive", "keep-alive value");
   response.AddHTTPHeaderField("expires", "expires value");
   response.AddHTTPHeaderField("last-modified", "last-modified value");
@@ -380,7 +380,7 @@
 
   // Perform a revalidation step.
   ResourceResponse revalidating_response(url);
-  revalidating_response.SetHTTPStatusCode(304);
+  revalidating_response.SetHttpStatusCode(304);
   // Headers that aren't copied with an 304 code.
   revalidating_response.AddHTTPHeaderField("keep-alive", "garbage");
   revalidating_response.AddHTTPHeaderField("expires", "garbage");
@@ -422,7 +422,7 @@
 
   MockResource* resource = MockResource::Create(ResourceRequest(url));
   ResourceResponse response(url);
-  response.SetHTTPStatusCode(200);
+  response.SetHttpStatusCode(200);
   resource->ResponseReceived(response);
   const char kData[5] = "abcd";
   resource->AppendData(kData, 4);
@@ -451,7 +451,7 @@
   ResourceResponse redirect_response(url);
   redirect_response.SetHTTPHeaderField(
       "location", AtomicString(redirect_target_url.GetString()));
-  redirect_response.SetHTTPStatusCode(308);
+  redirect_response.SetHttpStatusCode(308);
   ResourceRequest redirected_revalidating_request(redirect_target_url);
   resource->WillFollowRedirect(redirected_revalidating_request,
                                redirect_response);
@@ -462,7 +462,7 @@
 
   // The final response is received.
   ResourceResponse revalidating_response(redirect_target_url);
-  revalidating_response.SetHTTPStatusCode(200);
+  revalidating_response.SetHttpStatusCode(200);
   resource->ResponseReceived(revalidating_response);
 
   EXPECT_TRUE(resource->CacheHandler());
@@ -504,7 +504,7 @@
   ScopedTestingPlatformSupport<MockPlatform> mock;
   const KURL url("http://127.0.0.1:8000/foo.html");
   ResourceResponse response(url);
-  response.SetHTTPStatusCode(200);
+  response.SetHttpStatusCode(200);
   response.SetHTTPHeaderField(http_names::kCacheControl,
                               "max-age=0, stale-while-revalidate=40");
 
@@ -533,13 +533,13 @@
   const KURL redirect_target_url("http://127.0.0.1:8000/food.html");
   ResourceResponse response(url);
   response.SetHTTPHeaderField(http_names::kCacheControl, "max-age=50");
-  response.SetHTTPStatusCode(200);
+  response.SetHttpStatusCode(200);
 
   // The revalidating request is redirected.
   ResourceResponse redirect_response(url);
   redirect_response.SetHTTPHeaderField(
       "location", AtomicString(redirect_target_url.GetString()));
-  redirect_response.SetHTTPStatusCode(302);
+  redirect_response.SetHttpStatusCode(302);
   redirect_response.SetHTTPHeaderField(http_names::kCacheControl,
                                        "max-age=0, stale-while-revalidate=40");
   redirect_response.SetAsyncRevalidationRequested(true);
diff --git a/third_party/blink/renderer/platform/loader/fetch/script_fetch_options.h b/third_party/blink/renderer/platform/loader/fetch/script_fetch_options.h
index d47fdb6..1a22837 100644
--- a/third_party/blink/renderer/platform/loader/fetch/script_fetch_options.h
+++ b/third_party/blink/renderer/platform/loader/fetch/script_fetch_options.h
@@ -12,6 +12,7 @@
 #include "third_party/blink/renderer/platform/loader/fetch/integrity_metadata.h"
 #include "third_party/blink/renderer/platform/loader/fetch/resource_loader_options.h"
 #include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
 #include "third_party/blink/renderer/platform/wtf/text/text_encoding.h"
 #include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
 
@@ -23,6 +24,8 @@
 // ScriptFetchOptions corresponds to the spec concept "script fetch options".
 // https://html.spec.whatwg.org/C/#script-fetch-options
 class PLATFORM_EXPORT ScriptFetchOptions final {
+  DISALLOW_NEW();
+
  public:
   // https://html.spec.whatwg.org/C/#default-classic-script-fetch-options
   // "The default classic script fetch options are a script fetch options whose
diff --git a/third_party/blink/renderer/platform/loader/fetch/source_keyed_cached_metadata_handler.cc b/third_party/blink/renderer/platform/loader/fetch/source_keyed_cached_metadata_handler.cc
index 48ba0ba..f54de8c 100644
--- a/third_party/blink/renderer/platform/loader/fetch/source_keyed_cached_metadata_handler.cc
+++ b/third_party/blink/renderer/platform/loader/fetch/source_keyed_cached_metadata_handler.cc
@@ -74,6 +74,8 @@
 };
 
 class SourceKeyedCachedMetadataHandler::KeyHash {
+  STATIC_ONLY(KeyHash);
+
  public:
   static unsigned GetHash(const Key& key) {
     return StringHasher::ComputeHash(key.data(),
diff --git a/third_party/blink/renderer/platform/loader/fetch/text_resource_decoder_options.h b/third_party/blink/renderer/platform/loader/fetch/text_resource_decoder_options.h
index 95bbab10..2316b95 100644
--- a/third_party/blink/renderer/platform/loader/fetch/text_resource_decoder_options.h
+++ b/third_party/blink/renderer/platform/loader/fetch/text_resource_decoder_options.h
@@ -7,11 +7,14 @@
 
 #include "third_party/blink/renderer/platform/platform_export.h"
 #include "third_party/blink/renderer/platform/weborigin/kurl.h"
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
 #include "third_party/blink/renderer/platform/wtf/text/text_encoding.h"
 
 namespace blink {
 
 class PLATFORM_EXPORT TextResourceDecoderOptions final {
+  DISALLOW_NEW();
+
  public:
   enum ContentType {
     kPlainTextContent,
diff --git a/third_party/blink/renderer/platform/loader/ftp_directory_listing_test.cc b/third_party/blink/renderer/platform/loader/ftp_directory_listing_test.cc
index c840479..86bda2e 100644
--- a/third_party/blink/renderer/platform/loader/ftp_directory_listing_test.cc
+++ b/third_party/blink/renderer/platform/loader/ftp_directory_listing_test.cc
@@ -12,12 +12,15 @@
 #include "third_party/blink/renderer/platform/loader/fetch/resource_error.h"
 #include "third_party/blink/renderer/platform/shared_buffer.h"
 #include "third_party/blink/renderer/platform/weborigin/kurl.h"
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
 #include "third_party/icu/source/i18n/unicode/timezone.h"
 
 namespace blink {
 namespace {
 
 class ScopedRestoreDefaultTimezone {
+  STACK_ALLOCATED();
+
  public:
   explicit ScopedRestoreDefaultTimezone(const char* zoneid) {
     original_zone_.reset(icu::TimeZone::createDefault());
diff --git a/third_party/blink/renderer/platform/loader/subresource_integrity.h b/third_party/blink/renderer/platform/loader/subresource_integrity.h
index a5fb3ca..fbe56ac 100644
--- a/third_party/blink/renderer/platform/loader/subresource_integrity.h
+++ b/third_party/blink/renderer/platform/loader/subresource_integrity.h
@@ -22,6 +22,8 @@
 
  public:
   class PLATFORM_EXPORT ReportInfo final {
+    DISALLOW_NEW();
+
    public:
     enum class UseCounterFeature {
       kSRIElementWithMatchingIntegrityAttribute,
diff --git a/third_party/blink/renderer/platform/loader/subresource_integrity_test.cc b/third_party/blink/renderer/platform/loader/subresource_integrity_test.cc
index f12067c..3f62128 100644
--- a/third_party/blink/renderer/platform/loader/subresource_integrity_test.cc
+++ b/third_party/blink/renderer/platform/loader/subresource_integrity_test.cc
@@ -246,7 +246,7 @@
     request.SetFetchRequestMode(request_mode);
 
     ResourceResponse response(url);
-    response.SetHTTPStatusCode(200);
+    response.SetHttpStatusCode(200);
     response.SetType(response_type);
 
     resource->SetResourceRequest(request);
diff --git a/third_party/blink/renderer/platform/network/encoded_form_data.h b/third_party/blink/renderer/platform/network/encoded_form_data.h
index c112cd5..4b0c6fc 100644
--- a/third_party/blink/renderer/platform/network/encoded_form_data.h
+++ b/third_party/blink/renderer/platform/network/encoded_form_data.h
@@ -117,6 +117,8 @@
 }
 
 class PLATFORM_EXPORT EncodedFormData : public RefCounted<EncodedFormData> {
+  USING_FAST_MALLOC(EncodedFormData);
+
  public:
   enum EncodingType {
     kFormURLEncoded,    // for application/x-www-form-urlencoded
diff --git a/third_party/blink/renderer/platform/network/network_state_notifier.h b/third_party/blink/renderer/platform/network/network_state_notifier.h
index 9df2bf9e..c8ec8d0df 100644
--- a/third_party/blink/renderer/platform/network/network_state_notifier.h
+++ b/third_party/blink/renderer/platform/network/network_state_notifier.h
@@ -313,6 +313,8 @@
   // thread.  Note that ScopedNotifier must be destroyed when not holding a lock
   // so that onLine notifications can be dispatched without a deadlock.
   class ScopedNotifier {
+    STACK_ALLOCATED();
+
    public:
     explicit ScopedNotifier(NetworkStateNotifier&);
     ~ScopedNotifier();
diff --git a/third_party/blink/renderer/platform/network/network_utils.cc b/third_party/blink/renderer/platform/network/network_utils.cc
index 3ecb398..8c69acef 100644
--- a/third_party/blink/renderer/platform/network/network_utils.cc
+++ b/third_party/blink/renderer/platform/network/network_utils.cc
@@ -87,7 +87,7 @@
 
   auto buffer = SharedBuffer::Create(data_string.data(), data_string.size());
   ResourceResponse response;
-  response.SetHTTPStatusCode(200);
+  response.SetHttpStatusCode(200);
   response.SetHTTPStatusText("OK");
   response.SetCurrentRequestUrl(url);
   response.SetMimeType(WebString::FromUTF8(utf8_mime_type));
diff --git a/third_party/blink/renderer/platform/network/server_timing_header.h b/third_party/blink/renderer/platform/network/server_timing_header.h
index c8dd457..f527395 100644
--- a/third_party/blink/renderer/platform/network/server_timing_header.h
+++ b/third_party/blink/renderer/platform/network/server_timing_header.h
@@ -12,8 +12,10 @@
 namespace blink {
 
 class ServerTimingHeader {
+  USING_FAST_MALLOC(ServerTimingHeader);
+
  public:
-  ServerTimingHeader(const String& name) : name_(name) {}
+  explicit ServerTimingHeader(const String& name) : name_(name) {}
 
   const String& Name() const { return name_; }
   const double& Duration() const { return duration_; }
diff --git a/third_party/blink/renderer/platform/runtime_enabled_features.json5 b/third_party/blink/renderer/platform/runtime_enabled_features.json5
index 7dd4e81..39b748f 100644
--- a/third_party/blink/renderer/platform/runtime_enabled_features.json5
+++ b/third_party/blink/renderer/platform/runtime_enabled_features.json5
@@ -182,6 +182,20 @@
       status: "stable"
     },
     {
+      name: "BuiltInModuleAll",
+      implied_by: ["ExperimentalProductivityFeatures"],
+    },
+    {
+      name: "BuiltInModuleInfra",
+      implied_by: ["ExperimentalProductivityFeatures",
+                   "BuiltInModuleAll",
+                   "BuiltInModuleKvStorage"],
+    },
+    {
+      name: "BuiltInModuleKvStorage",
+      implied_by: ["ExperimentalProductivityFeatures"],
+    },
+    {
       name: "CacheInlineScriptCode"
     },
     {
@@ -706,10 +720,6 @@
       status: "experimental",
     },
     {
-      name: "LayeredAPI",
-      implied_by: ["ExperimentalProductivityFeatures"],
-    },
-    {
       // Exposes layout jank fractions to Javascript. See explainer:
       // http://bit.ly/lsm-explainer.
       name: "LayoutJankAPI",
@@ -1055,6 +1065,13 @@
       status: "experimental",
     },
     {
+      // Flag enabling the performance observers buffered flag. The buffered
+      // flag indicates whether or not the buffered entries should be loaded
+      // into the observer's buffer upon registration. See crbug.com/725567.
+      name: "PerformanceObserverBufferedFlag",
+      status: "experimental",
+    },
+    {
       name: "PerMethodCanMakePaymentQuota",
       origin_trial_feature_name: "PerMethodCanMakePaymentQuota",
       status: "experimental",
@@ -1202,9 +1219,6 @@
     {
       name: "RTCUnifiedPlanByDefault",
     },
-    {
-      name: "ScheduledScriptStreaming",
-    },
     // WebSpeech API with both speech recognition and synthesis functionality
     // is not fully enabled on all platforms.
     {
diff --git a/third_party/blink/renderer/platform/scheduler/common/metrics_helper.cc b/third_party/blink/renderer/platform/scheduler/common/metrics_helper.cc
index 2d57044..1576919 100644
--- a/third_party/blink/renderer/platform/scheduler/common/metrics_helper.cc
+++ b/third_party/blink/renderer/platform/scheduler/common/metrics_helper.cc
@@ -35,7 +35,6 @@
     case WebThreadType::kHRTFDatabaseLoaderThread:
     case WebThreadType::kOfflineAudioRenderThread:
     case WebThreadType::kReverbConvolutionBackgroundThread:
-    case WebThreadType::kScriptStreamerThread:
     case WebThreadType::kSharedWorkerThread:
     case WebThreadType::kUnspecifiedWorkerThread:
     case WebThreadType::kWebAudioThread:
diff --git a/third_party/blink/renderer/platform/testing/url_test_helpers.cc b/third_party/blink/renderer/platform/testing/url_test_helpers.cc
index 1548d0f..d694735 100644
--- a/third_party/blink/renderer/platform/testing/url_test_helpers.cc
+++ b/third_party/blink/renderer/platform/testing/url_test_helpers.cc
@@ -71,7 +71,7 @@
   WebURLResponse response(full_url);
   response.SetMIMEType(mime_type);
   response.SetHTTPHeaderField(http_names::kContentType, mime_type);
-  response.SetHTTPStatusCode(200);
+  response.SetHttpStatusCode(200);
   response.SetLoadTiming(timing);
 
   RegisterMockedURLLoadWithCustomResponse(full_url, file_path, response);
@@ -84,7 +84,7 @@
   WebURLResponse response;
   response.SetMIMEType("image/png");
   response.SetHTTPHeaderField(http_names::kContentType, "image/png");
-  response.SetHTTPStatusCode(404);
+  response.SetHttpStatusCode(404);
   response.SetLoadTiming(timing);
 
   ResourceError error = ResourceError::Failure(full_url);
diff --git a/third_party/blink/renderer/platform/web_thread_type.cc b/third_party/blink/renderer/platform/web_thread_type.cc
index fd20b80..2c17a78 100644
--- a/third_party/blink/renderer/platform/web_thread_type.cc
+++ b/third_party/blink/renderer/platform/web_thread_type.cc
@@ -33,8 +33,6 @@
       return "Database thread";
     case WebThreadType::kWebAudioThread:
       return "WebAudio thread";
-    case WebThreadType::kScriptStreamerThread:
-      return "ScriptStreamer thread";
     case WebThreadType::kOfflineAudioRenderThread:
       return "OfflineAudioRender thread";
     case WebThreadType::kReverbConvolutionBackgroundThread:
diff --git a/third_party/blink/renderer/platform/wtf/dynamic_annotations.cc b/third_party/blink/renderer/platform/wtf/dynamic_annotations.cc
index aedfdf7..8c77981 100644
--- a/third_party/blink/renderer/platform/wtf/dynamic_annotations.cc
+++ b/third_party/blink/renderer/platform/wtf/dynamic_annotations.cc
@@ -24,6 +24,8 @@
  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
+#include <stdint.h>
+
 #include "third_party/blink/renderer/platform/wtf/dynamic_annotations.h"
 
 #if defined(WTF_USE_DYNAMIC_ANNOTATIONS) && \
@@ -33,12 +35,12 @@
 // This makes all Annotate* functions different, which prevents the linker from
 // folding them.
 #ifdef __COUNTER__
-#define DYNAMIC_ANNOTATIONS_IMPL                         \
-  volatile short lineno = (__LINE__ << 8) + __COUNTER__; \
+#define DYNAMIC_ANNOTATIONS_IMPL                            \
+  volatile uint16_t lineno = (__LINE__ << 8) + __COUNTER__; \
   (void)lineno;
 #else
-#define DYNAMIC_ANNOTATIONS_IMPL           \
-  volatile short lineno = (__LINE__ << 8); \
+#define DYNAMIC_ANNOTATIONS_IMPL              \
+  volatile uint16_t lineno = (__LINE__ << 8); \
   (void)lineno;
 #endif
 
diff --git a/third_party/blink/web_tests/FlagExpectations/enable-blink-features=LayoutNG b/third_party/blink/web_tests/FlagExpectations/enable-blink-features=LayoutNG
index ed1bdd0..15ef93a 100644
--- a/third_party/blink/web_tests/FlagExpectations/enable-blink-features=LayoutNG
+++ b/third_party/blink/web_tests/FlagExpectations/enable-blink-features=LayoutNG
@@ -149,6 +149,7 @@
 crbug.com/591099 external/wpt/css/css-masking/clip-path/clip-path-polygon-010.html [ Failure ]
 crbug.com/591099 external/wpt/css/css-multicol/multicol-span-all-005.html [ Failure ]
 crbug.com/591099 external/wpt/css/css-multicol/multicol-span-all-list-item-002.html [ Pass ]
+crbug.com/933054 external/wpt/css/css-position/position-absolute-container-dynamic-002.html [ Pass ]
 crbug.com/591099 external/wpt/css/css-position/position-relative-table-tbody-top-absolute-child.html [ Failure ]
 crbug.com/591099 external/wpt/css/css-position/position-relative-table-tr-left-absolute-child.html [ Failure ]
 crbug.com/591099 external/wpt/css/css-position/static-position/htb-ltr-rtl.tentative.html [ Pass ]
diff --git a/third_party/blink/web_tests/MSANExpectations b/third_party/blink/web_tests/MSANExpectations
index 4b5199a..30ea7c6 100644
--- a/third_party/blink/web_tests/MSANExpectations
+++ b/third_party/blink/web_tests/MSANExpectations
@@ -9,7 +9,7 @@
 
 # Deliberate infinite recursion. A JS exception is expected, but may crash with
 # a stack overflow due to bloated stack frames under MSan.
-crbug.com/420606 [ Linux ] fast/workers/worker-constructor.html [ Skip ]
+crbug.com/420606 [ Linux ] external/wpt/workers/constructors/Worker/Worker-constructor.html [ Skip ]
 
 # Flaky under MSan (hang forever).
 crbug.com/422982 [ Linux ] virtual/threaded [ Skip ]
diff --git a/third_party/blink/web_tests/SlowTests b/third_party/blink/web_tests/SlowTests
index 1457a756..069bb621 100644
--- a/third_party/blink/web_tests/SlowTests
+++ b/third_party/blink/web_tests/SlowTests
@@ -661,7 +661,6 @@
 crbug.com/874695 external/wpt/orientation-sensor/RelativeOrientationSensor.https.html [ Slow ]
 crbug.com/874695 external/wpt/payment-request/allowpaymentrequest/setting-allowpaymentrequest-timing.https.sub.html [ Slow ]
 crbug.com/874695 virtual/not-site-per-process/external/wpt/payment-request/allowpaymentrequest/setting-allowpaymentrequest-timing.https.sub.html [ Slow ]
-crbug.com/874695 external/wpt/performance-timeline/po-observe.html [ Slow ]
 crbug.com/874695 external/wpt/picture-in-picture/leave-picture-in-picture.html [ Slow ]
 crbug.com/874695 external/wpt/picture-in-picture/picture-in-picture-window.html [ Slow ]
 crbug.com/874695 external/wpt/picture-in-picture/request-picture-in-picture-twice.html [ Slow ]
diff --git a/third_party/blink/web_tests/SmokeTests b/third_party/blink/web_tests/SmokeTests
index 89432f8..0f38560 100644
--- a/third_party/blink/web_tests/SmokeTests
+++ b/third_party/blink/web_tests/SmokeTests
@@ -364,6 +364,7 @@
 external/wpt/workers/semantics/run-a-worker/001.html
 external/wpt/workers/semantics/xhr/002.html
 external/wpt/workers/Worker_dispatchEvent_ErrorEvent.htm
+external/wpt/workers/Worker-terminate-forever.html
 external/wpt/XMLHttpRequest/event-load.htm
 external/wpt/XMLHttpRequest/open-after-abort.htm
 fast/alignment/new-alignment-values.html
@@ -765,7 +766,6 @@
 fast/url/mailto.html
 fast/webgl/canvas-getContext-crash.html
 fast/workers/worker-sharedarraybuffer-transfer.html
-fast/workers/worker-terminate-forever.html
 fast/writing-mode/english-lr-text.html
 fast/writing-mode/flipped-blocks-outline-crash.html
 fast/writing-mode/percentage-padding.html
diff --git a/third_party/blink/web_tests/TestExpectations b/third_party/blink/web_tests/TestExpectations
index d8e3e09c..c6aee88 100644
--- a/third_party/blink/web_tests/TestExpectations
+++ b/third_party/blink/web_tests/TestExpectations
@@ -101,6 +101,7 @@
 
 # The following fail only on Android.
 crbug.com/891427 [ Android ] virtual/threaded/synthetic_gestures/smooth-scroll-tiny-delta.html [ Pass Failure Timeout Crash ]
+crbug.com/898987 [ Android ] synthetic_gestures/smooth-scroll-tiny-delta.html [ Failure Pass Timeout ]
 
 # These 6 are special. They are tied to both of these CLs:
 #   - https://chromium-review.googlesource.com/c/chromium/src/+/1213864
@@ -464,6 +465,7 @@
 crbug.com/711807 external/wpt/css/CSS2/normal-flow/replaced-intrinsic-002.xht [ Failure ]
 
 #### external/wpt/css/css-position
+crbug.com/933054 external/wpt/css/css-position/position-absolute-container-dynamic-002.html [ Failure ]
 crbug.com/752022 external/wpt/css/css-position/position-sticky-offset-overflow.html [ Failure ]
 crbug.com/702927 external/wpt/css/css-position/position-sticky-table-tr-top.html [ Failure ]
 crbug.com/702927 external/wpt/css/css-position/position-sticky-table-tr-bottom.html [ Failure ]
@@ -1618,6 +1620,7 @@
 crbug.com/591099 virtual/layout_ng_experimental/external/wpt/css/css-flexbox/css-flexbox-row-reverse-wrap-reverse.html [ Failure ]
 crbug.com/591099 virtual/layout_ng_experimental/external/wpt/css/css-flexbox/css-flexbox-row-wrap-reverse.html [ Failure ]
 crbug.com/591099 virtual/layout_ng_experimental/external/wpt/css/css-flexbox/flex-align-items-center.html [ Failure ]
+crbug.com/591099 virtual/layout_ng_experimental/external/wpt/css/css-flexbox/flex-basis-010.html [ Failure ]
 crbug.com/591099 virtual/layout_ng_experimental/external/wpt/css/css-flexbox/flex-direction-with-element-insert.html [ Failure ]
 crbug.com/591099 virtual/layout_ng_experimental/external/wpt/css/css-flexbox/flex-direction.html [ Failure ]
 crbug.com/591099 virtual/layout_ng_experimental/external/wpt/css/css-flexbox/flex-flow-003.html [ Failure ]
@@ -1639,6 +1642,7 @@
 crbug.com/249112 virtual/layout_ng_experimental/external/wpt/css/css-flexbox/flex-minimum-height-flex-items-007.xht [ Failure ]
 crbug.com/467127 virtual/layout_ng_experimental/external/wpt/css/css-flexbox/flex-minimum-height-flex-items-008.xht [ Failure ]
 crbug.com/591099 virtual/layout_ng_experimental/external/wpt/css/css-flexbox/flex-minimum-height-flex-items-011.xht [ Failure ]
+crbug.com/591099 virtual/layout_ng_experimental/external/wpt/css/css-flexbox/flex-minimum-height-flex-items-013.html [ Failure ]
 crbug.com/591099 virtual/layout_ng_experimental/external/wpt/css/css-flexbox/flex-minimum-width-flex-items-001.xht [ Failure ]
 crbug.com/591099 virtual/layout_ng_experimental/external/wpt/css/css-flexbox/flex-minimum-width-flex-items-002.xht [ Failure ]
 crbug.com/591099 virtual/layout_ng_experimental/external/wpt/css/css-flexbox/flex-minimum-width-flex-items-003.xht [ Failure ]
@@ -1788,8 +1792,8 @@
 crbug.com/894651 virtual/bidi-caret-affinity/editing/selection/modify_move/move_left_word_04_rtl_multi_line.html [ Failure Crash ]
 crbug.com/894651 virtual/bidi-caret-affinity/editing/selection/modify_move/move_left_word_05_ltr_multi_line.html [ Failure ]
 crbug.com/894651 virtual/bidi-caret-affinity/editing/selection/modify_move/move_left_word_05_rtl_multi_line.html [ Failure ]
-crbug.com/894651 virtual/bidi-caret-affinity/editing/selection/modify_move/move_left_word_06_ltr_multi_line.html [ Failure ]
-crbug.com/894651 virtual/bidi-caret-affinity/editing/selection/modify_move/move_left_word_06_rtl_multi_line.html [ Failure Crash ]
+crbug.com/894651 virtual/bidi-caret-affinity/editing/selection/modify_move/move_left_word_06_ltr_multi_line.html [ Failure Timeout Crash ]
+crbug.com/894651 virtual/bidi-caret-affinity/editing/selection/modify_move/move_left_word_06_rtl_multi_line.html [ Failure Timeout Crash ]
 crbug.com/894651 virtual/bidi-caret-affinity/editing/selection/modify_move/move_left_word_07_ltr_multi_line.html [ Failure ]
 crbug.com/894651 virtual/bidi-caret-affinity/editing/selection/modify_move/move_left_word_07_rtl_multi_line.html [ Failure ]
 crbug.com/894651 virtual/bidi-caret-affinity/editing/selection/modify_move/move_left_word_08_ltr_multi_line.html [ Failure ]
@@ -1810,8 +1814,8 @@
 crbug.com/894651 virtual/bidi-caret-affinity/editing/selection/modify_move/move_right_word_04_rtl_multi_line.html [ Failure ]
 crbug.com/894651 virtual/bidi-caret-affinity/editing/selection/modify_move/move_right_word_05_ltr_multi_line.html [ Failure ]
 crbug.com/894651 virtual/bidi-caret-affinity/editing/selection/modify_move/move_right_word_05_rtl_multi_line.html [ Failure ]
-crbug.com/894651 virtual/bidi-caret-affinity/editing/selection/modify_move/move_right_word_06_ltr_multi_line.html [ Failure Crash ]
-crbug.com/894651 virtual/bidi-caret-affinity/editing/selection/modify_move/move_right_word_06_rtl_multi_line.html [ Failure ]
+crbug.com/894651 virtual/bidi-caret-affinity/editing/selection/modify_move/move_right_word_06_ltr_multi_line.html [ Failure Crash Timeout ]
+crbug.com/894651 virtual/bidi-caret-affinity/editing/selection/modify_move/move_right_word_06_rtl_multi_line.html [ Failure Crash Timeout ]
 crbug.com/894651 virtual/bidi-caret-affinity/editing/selection/modify_move/move_right_word_07_ltr_multi_line.html [ Failure ]
 crbug.com/894651 virtual/bidi-caret-affinity/editing/selection/modify_move/move_right_word_07_rtl_multi_line.html [ Failure ]
 crbug.com/894651 virtual/bidi-caret-affinity/editing/selection/modify_move/move_right_word_08_ltr_multi_line.html [ Failure ]
@@ -3585,40 +3589,40 @@
 crbug.com/626703 external/wpt/css/vendor-imports/mozilla/mozilla-central-reftests/shapes1/shape-outside-margin-box-border-radius-004.html [ Failure ]
 crbug.com/626703 external/wpt/css/vendor-imports/mozilla/mozilla-central-reftests/shapes1/shape-outside-border-box-border-radius-005.html [ Failure ]
 crbug.com/626703 external/wpt/css/vendor-imports/mozilla/mozilla-central-reftests/shapes1/shape-outside-ellipse-049.html [ Failure ]
-crbug.com/626703 external/wpt/css/css-masking/clip-path/clip-path-polygon-007.html [ Failure ]
-crbug.com/626703 external/wpt/css/css-masking/clip-path/clip-path-circle-008.html [ Failure ]
-crbug.com/626703 external/wpt/css/css-masking/clip-path/clip-path-polygon-012.html [ Failure ]
-crbug.com/626703 external/wpt/css/css-masking/clip-path/clip-path-circle-003.html [ Failure ]
-crbug.com/626703 external/wpt/css/css-masking/clip-path-svg-content/mask-objectboundingbox-content-clip.svg [ Failure ]
-crbug.com/626703 external/wpt/css/css-masking/clip-path/clip-path-circle-001.html [ Failure ]
-crbug.com/626703 external/wpt/css/css-masking/clip-path/clip-path-ellipse-003.html [ Failure ]
-crbug.com/626703 external/wpt/css/css-masking/clip-path/clip-path-ellipse-001.html [ Failure ]
-crbug.com/626703 external/wpt/css/css-masking/clip-path/clip-path-circle-002.html [ Failure ]
-crbug.com/626703 external/wpt/css/css-masking/clip-path-svg-content/mask-userspaceonuse-content-clip.svg [ Failure ]
-crbug.com/626703 external/wpt/css/css-masking/clip-path/clip-path-ellipse-004.html [ Failure ]
-crbug.com/626703 external/wpt/css/css-masking/clip-path/clip-path-polygon-011.html [ Failure ]
-crbug.com/626703 external/wpt/css/css-masking/clip-path/clip-path-polygon-013.html [ Failure ]
-crbug.com/626703 external/wpt/css/css-masking/clip-path-svg-content/mask-userspaceonuse-content-clip-transform.svg [ Failure ]
-crbug.com/626703 external/wpt/css/css-masking/clip-path/clip-path-element-userSpaceOnUse-003.html [ Failure ]
-crbug.com/626703 external/wpt/css/css-masking/clip-path/clip-path-polygon-009.html [ Failure ]
-crbug.com/626703 external/wpt/css/css-masking/clip-path/clip-path-circle-005.html [ Failure ]
-crbug.com/626703 external/wpt/css/css-masking/clip-path/clip-path-ellipse-002.html [ Failure ]
-crbug.com/626703 external/wpt/css/css-masking/clip-path/clip-path-ellipse-005.html [ Failure ]
-crbug.com/626703 external/wpt/css/css-masking/clip-path/clip-path-ellipse-006.html [ Failure ]
-crbug.com/626703 external/wpt/css/css-masking/clip-path/clip-path-circle-004.html [ Failure ]
-crbug.com/626703 external/wpt/css/css-masking/clip-path/clip-path-polygon-010.html [ Failure ]
-crbug.com/626703 external/wpt/css/css-masking/clip-path/clip-path-circle-006.html [ Failure ]
-crbug.com/626703 external/wpt/css/css-masking/clip-path-svg-content/clip-path-shape-circle-003.svg [ Failure ]
+crbug.com/843084 external/wpt/css/css-masking/clip-path/clip-path-polygon-007.html [ Failure ]
+crbug.com/843084 external/wpt/css/css-masking/clip-path/clip-path-circle-008.html [ Failure ]
+crbug.com/843084 external/wpt/css/css-masking/clip-path/clip-path-polygon-012.html [ Failure ]
+crbug.com/843084 external/wpt/css/css-masking/clip-path/clip-path-circle-003.html [ Failure ]
+crbug.com/843084 external/wpt/css/css-masking/clip-path-svg-content/mask-objectboundingbox-content-clip.svg [ Failure ]
+crbug.com/843084 external/wpt/css/css-masking/clip-path/clip-path-circle-001.html [ Failure ]
+crbug.com/843084 external/wpt/css/css-masking/clip-path/clip-path-ellipse-003.html [ Failure ]
+crbug.com/843084 external/wpt/css/css-masking/clip-path/clip-path-ellipse-001.html [ Failure ]
+crbug.com/843084 external/wpt/css/css-masking/clip-path/clip-path-circle-002.html [ Failure ]
+crbug.com/843084 external/wpt/css/css-masking/clip-path-svg-content/mask-userspaceonuse-content-clip.svg [ Failure ]
+crbug.com/843084 external/wpt/css/css-masking/clip-path/clip-path-ellipse-004.html [ Failure ]
+crbug.com/843084 external/wpt/css/css-masking/clip-path/clip-path-polygon-011.html [ Failure ]
+crbug.com/843084 external/wpt/css/css-masking/clip-path/clip-path-polygon-013.html [ Failure ]
+crbug.com/843084 external/wpt/css/css-masking/clip-path-svg-content/mask-userspaceonuse-content-clip-transform.svg [ Failure ]
+crbug.com/843084 external/wpt/css/css-masking/clip-path/clip-path-element-userSpaceOnUse-003.html [ Failure ]
+crbug.com/843084 external/wpt/css/css-masking/clip-path/clip-path-polygon-009.html [ Failure ]
+crbug.com/843084 external/wpt/css/css-masking/clip-path/clip-path-circle-005.html [ Failure ]
+crbug.com/843084 external/wpt/css/css-masking/clip-path/clip-path-ellipse-002.html [ Failure ]
+crbug.com/843084 external/wpt/css/css-masking/clip-path/clip-path-ellipse-005.html [ Failure ]
+crbug.com/843084 external/wpt/css/css-masking/clip-path/clip-path-ellipse-006.html [ Failure ]
+crbug.com/843084 external/wpt/css/css-masking/clip-path/clip-path-circle-004.html [ Failure ]
+crbug.com/843084 external/wpt/css/css-masking/clip-path/clip-path-polygon-010.html [ Failure ]
+crbug.com/843084 external/wpt/css/css-masking/clip-path/clip-path-circle-006.html [ Failure ]
+crbug.com/843084 external/wpt/css/css-masking/clip-path-svg-content/clip-path-shape-circle-003.svg [ Failure ]
 crbug.com/432153 external/wpt/css/css-masking/test-mask.html [ Failure ]
-crbug.com/626703 external/wpt/css/css-masking/clip-path/clip-path-polygon-006.html [ Failure ]
-crbug.com/626703 external/wpt/css/css-masking/clip-path-svg-content/clip-path-shape-circle-004.svg [ Failure ]
-crbug.com/626703 external/wpt/css/css-masking/clip-path-svg-content/clip-path-recursion-002.svg [ Failure ]
-crbug.com/626703 external/wpt/css/css-masking/clip-path/clip-path-polygon-008.html [ Failure ]
-crbug.com/626703 external/wpt/css/css-masking/clip-path-svg-content/mask-objectboundingbox-content-clip-transform.svg [ Failure ]
-crbug.com/626703 external/wpt/css/css-masking/clip-path/clip-path-ellipse-008.html [ Failure ]
-crbug.com/626703 external/wpt/css/css-masking/clip-path/clip-path-ellipse-007.html [ Failure ]
-crbug.com/626703 external/wpt/css/css-masking/clip-path/clip-path-element-userSpaceOnUse-004.html [ Failure ]
-crbug.com/626703 external/wpt/css/css-masking/clip-path-svg-content/clip-path-shape-circle-005.svg [ Failure ]
+crbug.com/843084 external/wpt/css/css-masking/clip-path/clip-path-polygon-006.html [ Failure ]
+crbug.com/843084 external/wpt/css/css-masking/clip-path-svg-content/clip-path-shape-circle-004.svg [ Failure ]
+crbug.com/843084 external/wpt/css/css-masking/clip-path-svg-content/clip-path-recursion-002.svg [ Failure ]
+crbug.com/843084 external/wpt/css/css-masking/clip-path/clip-path-polygon-008.html [ Failure ]
+crbug.com/843084 external/wpt/css/css-masking/clip-path-svg-content/mask-objectboundingbox-content-clip-transform.svg [ Failure ]
+crbug.com/843084 external/wpt/css/css-masking/clip-path/clip-path-ellipse-008.html [ Failure ]
+crbug.com/843084 external/wpt/css/css-masking/clip-path/clip-path-ellipse-007.html [ Failure ]
+crbug.com/843084 external/wpt/css/css-masking/clip-path/clip-path-element-userSpaceOnUse-004.html [ Failure ]
+crbug.com/843084 external/wpt/css/css-masking/clip-path-svg-content/clip-path-shape-circle-005.svg [ Failure ]
 crbug.com/626703 external/wpt/css/css-shapes/shape-outside/shape-image/gradients/shape-outside-linear-gradient-016.html [ Failure ]
 crbug.com/626703 external/wpt/css/css-shapes/shape-outside/shape-image/gradients/shape-outside-linear-gradient-013.html [ Failure ]
 crbug.com/626703 external/wpt/css/css-shapes/shape-outside/shape-image/gradients/shape-outside-linear-gradient-011.html [ Failure ]
@@ -3889,7 +3893,6 @@
 crbug.com/626703 external/wpt/IndexedDB/request-abort-ordering.html [ Pass Failure ]
 crbug.com/626703 external/wpt/media-source/mediasource-avtracks.html [ Failure Crash ]
 crbug.com/626703 external/wpt/media-source/mediasource-getvideoplaybackquality.html [ Timeout Failure ]
-crbug.com/626703 external/wpt/performance-timeline/po-observe.html [ Timeout ]
 crbug.com/626703 external/wpt/pointerevents/pointerevent_disabled_form_control-manual.html [ Timeout Pass ]
 crbug.com/626703 external/wpt/presentation-api/controlling-ua/getAvailability.https.html [ Timeout ]
 crbug.com/626703 external/wpt/requestidlecallback/callback-timeout.html [ Timeout ]
@@ -4157,10 +4160,6 @@
 crbug.com/888470 virtual/omt-worker-fetch/external/wpt/referrer-policy/css-integration/child-css/internal-import-stylesheet.html [ Failure ]
 crbug.com/888470 virtual/omt-worker-fetch/external/wpt/referrer-policy/css-integration/child-css/processing-instruction.html [ Failure ]
 
-# Popups during page unloading is being put behind a policy; this test is
-# expected to fail. For now.
-crbug.com/936080 external/wpt/html/browsers/the-window-object/apis-for-creating-and-navigating-browsing-contexts-by-name/no_window_open_when_term_nesting_level_nonzero.window.html [ Failure ]
-
 # This test times out on debug builds, see https://crbug.com/755810
 crbug.com/626703 [ Debug ] external/wpt/html/semantics/tabular-data/processing-model-1/span-limits.html [ Skip ]
 
@@ -5581,7 +5580,6 @@
 crbug.com/899710 [ Win ] virtual/threaded/http/tests/devtools/tracing/timeline-paint/timeline-paint-with-layout-invalidations.js [ Failure Pass Timeout ]
 crbug.com/766357 [ Mac ] virtual/threaded/fast/scroll-behavior/wheel-and-touch-scroll-use-count.html [ Failure Pass ]
 crbug.com/766357 [ Win ] virtual/threaded/fast/scroll-behavior/wheel-and-touch-scroll-use-count.html [ Failure Pass ]
-crbug.com/898987 [ Android ] synthetic_gestures/smooth-scroll-tiny-delta.html [ Failure Pass ]
 
 # Sheriff 2018-10-30
 crbug.com/867668 [ Linux ] virtual/user-activation-v2/fast/events/middleClickAutoscroll-click-hyperlink.html [ Failure Pass ]
@@ -5659,7 +5657,6 @@
 crbug.com/912821 [ Mac ] virtual/threaded/http/tests/devtools/tracing/user-timing.js [ Pass Failure ]
 crbug.com/912821 [ Mac ] http/tests/devtools/tracing/user-timing.js [ Pass Failure ]
 
-crbug.com/913170 compositing/layer-creation/fixed-position-in-fixed-overflow.html [ Crash Pass ]
 crbug.com/913173 fast/backgrounds/background-svg-scaling-zoom.html [ Failure Pass ]
 
 # Sheriff 2018-12-13
@@ -5705,10 +5702,7 @@
 
 ### virtual/streams-native/http/tests/streams/transferable/
 crbug.com/902633 virtual/streams-native/http/tests/streams/transferable/writable-stream.html [ Timeout ]
-
-# Sheriff 2018-12-27
-crbug.com/917970 [ Mac10.13 Retina ] virtual/mouseevent_fractional/fast/events/popup-blocking-timers5.html [ Pass Failure ]
-crbug.com/917970 [ Mac10.13 Retina ] virtual/user-activation-v2/fast/events/popup-blocking-timers5.html [ Pass Failure ]
+crbug.com/902633 virtual/streams-native/http/tests/streams/transferable/window.html [ Timeout ]
 
 # Sheriff 2019-01-03
 crbug.com/918905 external/wpt/css/vendor-imports/mozilla/mozilla-central-reftests/sizing/block-size-with-min-or-max-content-table-1b.html [ Pass Failure ]
@@ -5963,6 +5957,11 @@
 # Wasm threads enabled by default
 crbug.com/754910 virtual/origin-trials-runtimeflags-disabled/http/tests/origin_trials/webexposed/wasm-threads-origin-trial-disabled.html [ Skip ]
 
+# These tests depend on targeting javascript: url navigations at the specified window.
+crbug.com/935064 external/wpt/html/browsers/browsing-the-web/navigating-across-documents/javascript-url-abort/javascript-url-abort-return-value-string.tentative.html [ Failure ]
+crbug.com/935064 external/wpt/content-security-policy/inheritance/iframe-all-local-schemes.sub.html [ Failure ]
+crbug.com/935064 external/wpt/content-security-policy/unsafe-hashes/javascript_src_allowed-href_blank.html [ Timeout ]
+
 # Sheriff 2019-02-28
 crbug.com/936827 external/wpt/fullscreen/api/element-request-fullscreen-and-remove-manual.html [ Failure Pass ]
 
@@ -5996,3 +5995,7 @@
 crbug.com/937858 [ Debug ] external/wpt/ambient-light/AmbientLightSensor.https.html [ Pass Failure ]
 crbug.com/937902 [ Linux Debug ] virtual/disable-blink-gen-property-trees/compositing/overflow/overflow-scroll-with-local-background.html [ Pass Failure ]
 crbug.com/937991 [ Win7 Release ] http/tests/devtools/cache-storage/cache-data.js [ Pass Timeout ]
+
+# Sheriff 2019-03-05
+crbug.com/938200 http/tests/devtools/network/network-blocked-reason.js [ Timeout Pass ] http/tests/devtools/network/network-blocked-reason.js
+crbug.com/938780 [ Mac ] external/wpt/animation-worklet/worklet-animation-with-scroll-timeline-root-scroller.https.html [ Failure ]
diff --git a/third_party/blink/web_tests/animations/animationworklet/scroll-timeline-overflow-hidden-expected.html b/third_party/blink/web_tests/animations/animationworklet/scroll-timeline-overflow-hidden-expected.html
deleted file mode 100644
index a1e4bce..0000000
--- a/third_party/blink/web_tests/animations/animationworklet/scroll-timeline-overflow-hidden-expected.html
+++ /dev/null
@@ -1,44 +0,0 @@
-<!DOCTYPE html>
-<style>
-#box {
-  width: 100px;
-  height: 100px;
-  background-color: #00ff00;
-  transform: translate(0, 100px);
-  opacity: 0.5;
-  will-change: transform; /* force compositing */
-}
-
-#covered {
-  width: 100px;
-  height: 100px;
-  background-color: #ff8080;
-}
-
-#scroller {
-  overflow: hidden;
-  height: 100px;
-  width: 100px;
-  will-change: transform; /* force compositing */
-}
-
-#contents {
-  height: 1000px;
-  width: 100%;
-}
-</style>
-
-<div id="box"></div>
-<div id="covered"></div>
-<div id="scroller">
-  <div id="contents"></div>
-</div>
-
-<script>
-window.addEventListener('load', function() {
-  // Move the scroller to halfway.
-  const scroller = document.getElementById("scroller");
-  const maxScroll = scroller.scrollHeight - scroller.clientHeight;
-  scroller.scrollTop = 0.5 * maxScroll;
-});
-</script>
diff --git a/third_party/blink/web_tests/animations/animationworklet/scroll-timeline-overflow-hidden.html b/third_party/blink/web_tests/animations/animationworklet/scroll-timeline-overflow-hidden.html
deleted file mode 100644
index 44cb935..0000000
--- a/third_party/blink/web_tests/animations/animationworklet/scroll-timeline-overflow-hidden.html
+++ /dev/null
@@ -1,75 +0,0 @@
-<!DOCTYPE html>
-<style>
-#box {
-  width: 100px;
-  height: 100px;
-  background-color: #00ff00;
-}
-
-#covered {
-  width: 100px;
-  height: 100px;
-  background-color: #ff8080;
-}
-
-#scroller {
-  overflow: hidden;
-  height: 100px;
-  width: 100px;
-}
-
-#contents {
-  height: 1000px;
-  width: 100%;
-}
-</style>
-
-<div id="box"></div>
-<div id="covered"></div>
-<div id="scroller">
-  <div id="contents"></div>
-</div>
-
-<script id="visual_update"  type="text/worklet">
-registerAnimator("test_animator", class {
-  animate(currentTime, effect) {
-    effect.localTime = currentTime;
-  }
-});
-</script>
-
-<script src="resources/animation-worklet-tests.js"></script>
-<script>
-if (window.testRunner) {
-  testRunner.waitUntilDone();
-}
-
-runInAnimationWorklet(
-  document.getElementById('visual_update').textContent
-).then(()=>{
-  const box = document.getElementById('box');
-  const effect = new KeyframeEffect(box,
-    [
-     { transform: 'translateY(0)', opacity: 1},
-     { transform: 'translateY(200px)', opacity: 0}
-    ], {
-      duration: 1000,
-    }
-  );
-
-  const scroller = document.getElementById('scroller');
-  const timeline = new ScrollTimeline({ scrollSource: scroller, timeRange: 1000, orientation: 'block' });
-  const animation = new WorkletAnimation('test_animator', [effect], timeline, {});
-  animation.play();
-
-  // Move the scroller to the halfway point.
-  const maxScroll = scroller.scrollHeight - scroller.clientHeight;
-  scroller.scrollTop = 0.5 * maxScroll;
-
-  if (window.testRunner) {
-    waitTwoAnimationFrames(_ => {
-      testRunner.notifyDone();
-    });
-  }
-});
-</script>
diff --git a/third_party/blink/web_tests/animations/animationworklet/scroll-timeline-root-scroller-expected.html b/third_party/blink/web_tests/animations/animationworklet/scroll-timeline-root-scroller-expected.html
deleted file mode 100644
index f8cfc2c..0000000
--- a/third_party/blink/web_tests/animations/animationworklet/scroll-timeline-root-scroller-expected.html
+++ /dev/null
@@ -1,36 +0,0 @@
-<!DOCTYPE html>
-<style>
-html {
-  min-height: 100%;
-  min-width: 100%;
-  padding-bottom: 100px;
-  padding-right: 100px;
-}
-
-#box {
-  width: 100px;
-  height: 100px;
-  background-color: #00ff00;
-  transform: translate(0, 100px);
-  opacity: 0.5;
-  will-change: transform; /* force compositing */
-}
-
-#covered {
-  width: 100px;
-  height: 100px;
-  background-color: #ff8080;
-}
-</style>
-
-<div id="box"></div>
-<div id="covered"></div>
-
-<script>
-window.addEventListener('load', function() {
-  // Move the scroller to halfway.
-  const scroller = document.scrollingElement;
-  const maxScroll = scroller.scrollHeight - scroller.clientHeight;
-  scroller.scrollTop = 0.5 * maxScroll;
-});
-</script>
diff --git a/third_party/blink/web_tests/animations/animationworklet/scroll-timeline-root-scroller.html b/third_party/blink/web_tests/animations/animationworklet/scroll-timeline-root-scroller.html
deleted file mode 100644
index 32fc6ccd..0000000
--- a/third_party/blink/web_tests/animations/animationworklet/scroll-timeline-root-scroller.html
+++ /dev/null
@@ -1,68 +0,0 @@
-<!DOCTYPE html>
-<style>
-html {
-  min-height: 100%;
-  min-width: 100%;
-  padding-bottom: 100px;
-  padding-right: 100px;
-}
-
-#box {
-  width: 100px;
-  height: 100px;
-  background-color: #00ff00;
-}
-
-#covered {
-  width: 100px;
-  height: 100px;
-  background-color: #ff8080;
-}
-</style>
-
-<div id="box"></div>
-<div id="covered"></div>
-
-<script id="visual_update"  type="text/worklet">
-registerAnimator("test_animator", class {
-  animate(currentTime, effect) {
-    effect.localTime = currentTime;
-  }
-});
-</script>
-
-<script src="resources/animation-worklet-tests.js"></script>
-<script>
-if (window.testRunner) {
-  testRunner.waitUntilDone();
-}
-
-runInAnimationWorklet(
-  document.getElementById('visual_update').textContent
-).then(()=>{
-  const box = document.getElementById('box');
-  const effect = new KeyframeEffect(box,
-    [
-     { transform: 'translateY(0)', opacity: 1},
-     { transform: 'translateY(200px)', opacity: 0}
-    ], {
-      duration: 1000,
-    }
-  );
-
-  const scroller = document.scrollingElement;
-  const timeline = new ScrollTimeline({ scrollSource: scroller, timeRange: 1000, orientation: 'block' });
-  const animation = new WorkletAnimation('test_animator', [effect], timeline, {});
-  animation.play();
-
-  // Move the scroller to the halfway point.
-  const maxScroll = scroller.scrollHeight - scroller.clientHeight;
-  scroller.scrollTop = 0.5 * maxScroll;
-
-  if (window.testRunner) {
-    waitTwoAnimationFrames(_ => {
-      testRunner.notifyDone();
-    });
-  }
-});
-</script>
diff --git a/third_party/blink/web_tests/external/wpt/animation-worklet/worklet-animation-with-scroll-timeline-and-overflow-hidden-ref.html b/third_party/blink/web_tests/external/wpt/animation-worklet/worklet-animation-with-scroll-timeline-and-overflow-hidden-ref.html
new file mode 100644
index 0000000..c6d7314e
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/animation-worklet/worklet-animation-with-scroll-timeline-and-overflow-hidden-ref.html
@@ -0,0 +1,45 @@
+<!DOCTYPE html>
+<title>Scroll timeline with WorkletAnimation using a scroller with overflow hidden</title>
+<style>
+  #box {
+    width: 100px;
+    height: 100px;
+    background-color: green;
+    transform: translate(0, 100px);
+    opacity: 0.5;
+    will-change: transform; /* force compositing */
+  }
+
+  #covered {
+    width: 100px;
+    height: 100px;
+    background-color: red;
+  }
+
+  #scroller {
+    overflow: hidden;
+    height: 100px;
+    width: 100px;
+    will-change: transform; /* force compositing */
+  }
+
+  #contents {
+    height: 1000px;
+    width: 100%;
+  }
+</style>
+
+<div id="box"></div>
+<div id="covered"></div>
+<div id="scroller">
+  <div id="contents"></div>
+</div>
+
+<script>
+  window.addEventListener('load', function() {
+    // Move the scroller to halfway.
+    const scroller = document.getElementById("scroller");
+    const maxScroll = scroller.scrollHeight - scroller.clientHeight;
+    scroller.scrollTop = 0.5 * maxScroll;
+  });
+</script>
\ No newline at end of file
diff --git a/third_party/blink/web_tests/external/wpt/animation-worklet/worklet-animation-with-scroll-timeline-and-overflow-hidden.https.html b/third_party/blink/web_tests/external/wpt/animation-worklet/worklet-animation-with-scroll-timeline-and-overflow-hidden.https.html
new file mode 100644
index 0000000..8c3ebb66
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/animation-worklet/worklet-animation-with-scroll-timeline-and-overflow-hidden.https.html
@@ -0,0 +1,68 @@
+<html class="reftest-wait">
+<title>Scroll timeline with WorkletAnimation using a scroller with overflow hidden</title>
+<link rel="help" href="https://drafts.css-houdini.org/css-animationworklet/">
+<meta name="assert" content="Worklet animation correctly updates values when using a overflow: hidden on the scroller being used as the source for the ScrollTimeline">
+<link rel="match" href="worklet-animation-with-scroll-timeline-and-overflow-hidden-ref.html">
+
+<script src="/web-animations/testcommon.js"></script>
+<script src="/common/reftest-wait.js"></script>
+<script src="common.js"></script>
+
+<style>
+  #box {
+    width: 100px;
+    height: 100px;
+    background-color: green;
+  }
+
+  #covered {
+    width: 100px;
+    height: 100px;
+    background-color: red;
+  }
+
+  #scroller {
+    overflow: hidden;
+    height: 100px;
+    width: 100px;
+  }
+
+  #contents {
+    height: 1000px;
+    width: 100%;
+  }
+</style>
+
+<div id="box"></div>
+<div id="covered"></div>
+<div id="scroller">
+  <div id="contents"></div>
+</div>
+
+<script>
+  registerPassthroughAnimator().then(_ => {
+    const box = document.getElementById('box');
+    const effect = new KeyframeEffect(box,
+      [
+        {transform: 'translateY(0)', opacity: 1},
+        {transform: 'translateY(200px)', opacity: 0}
+      ], {
+        duration: 1000,
+      }
+    );
+
+    const scroller = document.getElementById('scroller');
+    const timeline = new ScrollTimeline({ scrollSource: scroller, timeRange: 1000, orientation: 'block' });
+    const animation = new WorkletAnimation('passthrough', effect, timeline);
+    animation.play();
+
+    // Move the scroller to the halfway point.
+    const maxScroll = scroller.scrollHeight - scroller.clientHeight;
+    scroller.scrollTop = 0.5 * maxScroll;
+    waitForAnimationFrameWithCondition(_ => {
+      return getComputedStyle(box).transform != 'matrix(1, 0, 0, 1, 0, 0)';
+    }).then(_ => {
+      takeScreenshot();
+    });
+  });
+</script>
\ No newline at end of file
diff --git a/third_party/blink/web_tests/external/wpt/animation-worklet/worklet-animation-with-scroll-timeline-root-scroller-ref.html b/third_party/blink/web_tests/external/wpt/animation-worklet/worklet-animation-with-scroll-timeline-root-scroller-ref.html
new file mode 100644
index 0000000..5810e17
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/animation-worklet/worklet-animation-with-scroll-timeline-root-scroller-ref.html
@@ -0,0 +1,37 @@
+<!DOCTYPE html>
+<title>Reference for Scroll timeline with WorkletAnimation using the root scroller</title>
+<style>
+  html {
+    min-height: 100%;
+    min-width: 100%;
+    padding-bottom: 100px;
+    padding-right: 100px;
+  }
+
+  #box {
+    width: 100px;
+    height: 100px;
+    background-color: green;
+    transform: translate(0, 100px);
+    opacity: 0.5;
+    will-change: transform; /* force compositing */
+  }
+
+  #covered {
+    width: 100px;
+    height: 100px;
+    background-color: red;
+  }
+</style>
+
+<div id="box"></div>
+<div id="covered"></div>
+
+<script>
+  window.addEventListener('load', function() {
+    // Move the scroller to halfway.
+    const scroller = document.scrollingElement;
+    const maxScroll = scroller.scrollHeight - scroller.clientHeight;
+    scroller.scrollTop = 0.5 * maxScroll;
+  });
+</script>
diff --git a/third_party/blink/web_tests/external/wpt/animation-worklet/worklet-animation-with-scroll-timeline-root-scroller.https.html b/third_party/blink/web_tests/external/wpt/animation-worklet/worklet-animation-with-scroll-timeline-root-scroller.https.html
new file mode 100644
index 0000000..be577dcc
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/animation-worklet/worklet-animation-with-scroll-timeline-root-scroller.https.html
@@ -0,0 +1,62 @@
+<html class="reftest-wait">
+<title>Scroll timeline with WorkletAnimation using the root scroller</title>
+<link rel="help" href="https://drafts.css-houdini.org/css-animationworklet/">
+<meta name="assert" content="Worklet animation correctly updates values when using the root scroller as the source for the ScrollTimeline">
+<link rel="match" href="worklet-animation-with-scroll-timeline-root-scroller-ref.html">
+
+<script src="/web-animations/testcommon.js"></script>
+<script src="/common/reftest-wait.js"></script>
+<script src="common.js"></script>
+
+<style>
+  html {
+    min-height: 100%;
+    min-width: 100%;
+    padding-bottom: 100px;
+    padding-right: 100px;
+  }
+
+  #box {
+    width: 100px;
+    height: 100px;
+    background-color: green;
+  }
+
+  #covered {
+    width: 100px;
+    height: 100px;
+    background-color: red;
+  }
+</style>
+
+<div id="box"></div>
+<div id="covered"></div>
+
+<script>
+  registerPassthroughAnimator().then(()=>{
+    const box = document.getElementById('box');
+    const effect = new KeyframeEffect(box,
+      [
+        {transform: 'translateY(0)', opacity: 1},
+        {transform: 'translateY(200px)', opacity: 0}
+      ], {
+        duration: 1000,
+      }
+    );
+
+    const scroller = document.scrollingElement;
+    const timeline = new ScrollTimeline({ scrollSource: scroller, timeRange: 1000, orientation: 'block' });
+    const animation = new WorkletAnimation('passthrough', effect, timeline);
+    animation.play();
+
+    // Move the scroller to the halfway point.
+    const maxScroll = scroller.scrollHeight - scroller.clientHeight;
+    scroller.scrollTop = 0.5 * maxScroll;
+
+    waitForAnimationFrameWithCondition(_ => {
+      return getComputedStyle(box).transform != 'matrix(1, 0, 0, 1, 0, 0)';
+    }).then(_ => {
+      takeScreenshot();
+    });
+  });
+</script>
\ No newline at end of file
diff --git a/third_party/blink/web_tests/external/wpt/css/css-flexbox/flex-basis-010.html b/third_party/blink/web_tests/external/wpt/css/css-flexbox/flex-basis-010.html
new file mode 100644
index 0000000..79992974
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-flexbox/flex-basis-010.html
@@ -0,0 +1,31 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>CSS Flexbox Test: Indefinite % flex-basis should cause height to be ignored</title>
+<link rel="author" title="Google LLC" href="https://www.google.com">
+<link rel="help" href="http://www.w3.org/TR/css-flexbox-1/#flex-basis-property">
+<link rel="match" href="../reference/ref-filled-green-100px-square.xht">
+<style>
+  #container {
+    background-color: red;
+    display: flex;
+    width: 100px;
+    flex-direction: column;
+  }
+  #item {
+    flex: 0 0 0%;
+    height: 500px;
+    background-color: red;
+  }
+  #child {
+    height: 100px;
+    background-color: green;
+  }
+</style>
+<body>
+  <p>Test passes if there is a filled green square and <strong>no red</strong>.</p>
+  <div id="container">
+    <div id="item">
+      <div id="child"></div>
+    </div>
+  </div>
+</body>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-flexbox/flex-minimum-height-flex-items-013.html b/third_party/blink/web_tests/external/wpt/css/css-flexbox/flex-minimum-height-flex-items-013.html
new file mode 100644
index 0000000..9989b64
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-flexbox/flex-minimum-height-flex-items-013.html
@@ -0,0 +1,49 @@
+<!DOCTYPE html>
+<html>
+<title>CSS Flexbox: min-height: auto with nested flexboxes</title>
+<link rel="author" title="Google LLC" href="https://www.google.com/" />
+<link rel="help" href="https://drafts.csswg.org/css-flexbox/#min-size-auto" />
+<link rel="issue" href="https://bugs.chromium.org/p/chromium/issues/detail?id=933931" />
+<link rel="match" href="../reference/ref-filled-green-100px-square.xht" />
+
+<style>
+.outer {
+  display: flex;
+  flex-direction: column;
+  height: 20px;
+  width: 100px;
+  background: red;
+}
+
+.middle {
+  display: flex;
+  flex-direction: column;
+  background: green;
+}
+
+.inner {
+  display: flex;
+  flex-direction: column;
+}
+
+.tall {
+  width: 50px;
+  height: 100px;
+  background: green;
+}
+</style>
+<body>
+
+<p>Test passes if there is a filled green square and <strong>no red</strong>.</p>
+
+<div class="outer">
+  <div class="middle">
+    <div class="inner">
+      <div class="tall"></div>
+    </div>
+  </div>
+</div>
+
+
+</body>
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-overflow/webkit-line-clamp/webkit-line-clamp-with-line-height-ref.html b/third_party/blink/web_tests/external/wpt/css/css-overflow/webkit-line-clamp/webkit-line-clamp-with-line-height-ref.html
new file mode 100644
index 0000000..5c27ecce5
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-overflow/webkit-line-clamp/webkit-line-clamp-with-line-height-ref.html
@@ -0,0 +1,18 @@
+<!DOCTYPE html>
+<style>
+#test {
+  width: 100px;
+
+  text-overflow: ellipsis;
+  white-space: nowrap;
+  overflow: hidden;
+
+  border: solid thin grey;
+  font: 20px 'Ahem';
+  line-height: 40px;
+}
+</style>
+<p>This tests the -webkit-line-clamp property with line-height applied.</p>
+<div id="test">
+  XXXX XXX
+</div>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-overflow/webkit-line-clamp/webkit-line-clamp-with-line-height.tentative.html b/third_party/blink/web_tests/external/wpt/css/css-overflow/webkit-line-clamp/webkit-line-clamp-with-line-height.tentative.html
new file mode 100644
index 0000000..2cd7177e
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-overflow/webkit-line-clamp/webkit-line-clamp-with-line-height.tentative.html
@@ -0,0 +1,22 @@
+<!DOCTYPE html>
+<link rel="match" href="webkit-line-clamp-with-line-height-ref.html">
+<link rel="help" href="https://github.com/w3c/csswg-drafts/issues/2847#issuecomment-468084957">
+<meta name="assert" content="This test checks that -webkit-line-clamp calculates respects line-height when calculating its block-size.">
+<style>
+#test {
+  width: 100px;
+
+  display: -webkit-box;
+  -webkit-box-orient: vertical;
+  -webkit-line-clamp: 1;
+  overflow: hidden;
+
+  border: solid thin grey;
+  font: 20px 'Ahem';
+  line-height: 40px;
+}
+</style>
+<p>This tests the -webkit-line-clamp property with line-height applied.</p>
+<div id="test">
+  XXXX XXX
+</div>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-position/position-absolute-container-dynamic-002.html b/third_party/blink/web_tests/external/wpt/css/css-position/position-absolute-container-dynamic-002.html
new file mode 100644
index 0000000..91d86283
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-position/position-absolute-container-dynamic-002.html
@@ -0,0 +1,47 @@
+<!DOCTYPE html>
+<title>CSS Position Absolute: dynamic changes to containing block height</title>
+<link rel="author" href="mailto:atotic@google.com">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<link rel="help" href="https://bugs.chromium.org/p/chromium/issues/detail?id=933054">
+<meta name="assert" content="Chrome regression: abspos descendant responds to containing block size change through line items">
+<style>
+
+#container {
+  position: relative;
+}
+#intermediate {
+  overflow: hidden;
+  width:200px;
+  height:200px;
+  background:red;
+}
+#block {
+  height:200px;
+  background:green;
+}
+#target {
+  position: absolute;
+  width: 200px;
+  height: 100px;
+  background:green;
+}
+</style>
+<!-- Test for crbug.com/933054
+  Relayout optimizations cause OOF descendant not to be
+  repositioned
+-->
+<div id="container">
+  <div id="intermediate">
+    <div id="block"></div>
+    <div id="target"></div>
+  </div>
+</div>
+
+<script>
+document.body.offsetTop;
+test(() => {
+  document.getElementById("block").style.height = "100px";
+  assert_equals(document.querySelector("#target").offsetTop, 100);
+}, '#target static position responded to parent relayout');
+</script>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-position/position-absolute-crash-chrome-002.html b/third_party/blink/web_tests/external/wpt/css/css-position/position-absolute-crash-chrome-002.html
new file mode 100644
index 0000000..9a4ff0ae5
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-position/position-absolute-crash-chrome-002.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<title>CSS Position Absolute: Chrome chrash</title>
+<link rel="author" href="mailto:atotic@google.com">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<link rel="help" href="https://bugs.chromium.org/p/chromium/issues/detail?id=938224">
+<meta name="assert" content="absolute position in LI container does not crash">
+<style>
+  #container {
+    position: absolute;
+    top: 0px;
+    left: 0px;
+  }
+  #abs {
+    position: absolute;
+    top: 0px;
+    left: 0px;
+  }
+</style>
+<li id="container">
+  <ul>
+    <li>
+      <div id="abs">abs</div>
+    </li>
+  </ul>
+</li>
+<script>
+test(() => {
+}, 'test passes if it does not crash');
+</script>
diff --git a/third_party/blink/web_tests/external/wpt/performance-timeline/po-observe.html b/third_party/blink/web_tests/external/wpt/performance-timeline/po-observe.html
index d65c8a4..1132f12 100644
--- a/third_party/blink/web_tests/external/wpt/performance-timeline/po-observe.html
+++ b/third_party/blink/web_tests/external/wpt/performance-timeline/po-observe.html
@@ -53,7 +53,8 @@
         buffered: false
       });
 
-      // this PerformanceObserver should be notified about the previously buffered entries
+      // this PerformanceObserver should be notified about the previously
+      // buffered mark entry only
       const bufferedEntries = [];
       new PerformanceObserver(function (entryList, observer) {
         entryList.getEntries().forEach(function(entry) {
@@ -62,15 +63,17 @@
           }
 
           bufferedEntries.push(entry);
-          if (bufferedEntries.length === entryTypes.length) {
+          if (bufferedEntries.length === 1) {
             observer.disconnect();
             po_nop.disconnect();
-            checkEntries(bufferedEntries, observedEntries);
+            for (i = 0; i < bufferedEntries.length; i++) {
+              assert_equals(bufferedEntries[i].entryType, "mark")
+            }
             t.done();
           }
         });
       }).observe({
-        entryTypes,
+        type: "mark",
         buffered: true
       });
     }
diff --git a/third_party/blink/web_tests/external/wpt/signed-exchange/resources/generate-test-sxgs.sh b/third_party/blink/web_tests/external/wpt/signed-exchange/resources/generate-test-sxgs.sh
index cc2b6dd5..60746ff3 100755
--- a/third_party/blink/web_tests/external/wpt/signed-exchange/resources/generate-test-sxgs.sh
+++ b/third_party/blink/web_tests/external/wpt/signed-exchange/resources/generate-test-sxgs.sh
@@ -355,8 +355,6 @@
   -miRecordSize 100 \
   -ignoreErrors true
 
-
-
 # Signed Exchange with payload integrity error.
 echo 'garbage' | cat sxg/sxg-location.sxg - >sxg/sxg-merkle-integrity-error.sxg
 
@@ -441,4 +439,19 @@
   -o sxg/sxg-variants-mismatch.sxg \
   -miRecordSize 100
 
+# A valid Signed Exchange that reports navigation timing.
+gen-signedexchange \
+  -version $sxg_version \
+  -uri $inner_url_origin/signed-exchange/resources/inner-url.html \
+  -status 200 \
+  -content sxg-navigation-timing.html \
+  -certificate $certfile \
+  -certUrl $cert_url_origin/signed-exchange/resources/$certfile.cbor \
+  -validityUrl $inner_url_origin/signed-exchange/resources/resource.validity.msg \
+  -privateKey $keyfile \
+  -date 2018-04-01T00:00:00Z \
+  -expire 168h \
+  -o sxg/sxg-navigation-timing.sxg \
+  -miRecordSize 100
+
 rm -fr $tmpdir
diff --git a/third_party/blink/web_tests/external/wpt/signed-exchange/resources/sxg-navigation-timing.html b/third_party/blink/web_tests/external/wpt/signed-exchange/resources/sxg-navigation-timing.html
new file mode 100644
index 0000000..ddbe350
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/signed-exchange/resources/sxg-navigation-timing.html
@@ -0,0 +1,10 @@
+<!DOCTYPE html>
+<title>Navigation timing of SignedHTTPExchange</title>
+<script>
+window.addEventListener('message', (event) => {
+  event.data.port.postMessage({
+      location: document.location.href,
+      timing: JSON.stringify(performance.getEntriesByType('navigation')[0]),
+      is_fallback: false});
+}, false);
+</script>
diff --git a/third_party/blink/web_tests/external/wpt/signed-exchange/resources/sxg/sxg-navigation-timing.sxg b/third_party/blink/web_tests/external/wpt/signed-exchange/resources/sxg/sxg-navigation-timing.sxg
new file mode 100644
index 0000000..56a90d5
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/signed-exchange/resources/sxg/sxg-navigation-timing.sxg
Binary files differ
diff --git a/third_party/blink/web_tests/external/wpt/signed-exchange/sxg-navigation-timing.tentative.html b/third_party/blink/web_tests/external/wpt/signed-exchange/sxg-navigation-timing.tentative.html
new file mode 100644
index 0000000..b3f08824
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/signed-exchange/sxg-navigation-timing.tentative.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<title>Navigation timing of SignedHTTPExchange</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/common/get-host-info.sub.js"></script>
+<script src="./resources/sxg-util.js"></script>
+<body>
+<script>
+promise_test(async (t) => {
+  const sxgUrl = get_host_info().HTTPS_ORIGIN + '/signed-exchange/resources/sxg/sxg-navigation-timing.sxg';
+  const message = await openSXGInIframeAndWaitForMessage(t, sxgUrl);
+  assert_false(message.is_fallback);
+
+  let timing = JSON.parse(message.timing);
+  let originalContent = await fetch('resources/sxg-navigation-timing.html').then(resp => resp.arrayBuffer());
+
+  assert_equals(timing.decodedBodySize, originalContent.byteLength);
+  assert_equals(timing.encodedBodySize, computeMiceLength(originalContent.byteLength, 100));
+  // TODO(https://crbug.com/928589): Test other fields too.
+}, 'Navigation timing of SignedHTTPExchange');
+
+// Returns content length after MI encode.
+function computeMiceLength(len, recordSize) {
+  const recordSizeLen = 8;
+  const sha256DigestLength = 32;
+  return recordSizeLen + len + Math.floor(len / recordSize) * sha256DigestLength;
+}
+
+</script>
+</body>
diff --git a/third_party/blink/web_tests/external/wpt/trusted-types/TrustedTypePolicyFactory-isXXX.tentative.html b/third_party/blink/web_tests/external/wpt/trusted-types/TrustedTypePolicyFactory-isXXX.tentative.html
index fada29a..854f69ed 100644
--- a/third_party/blink/web_tests/external/wpt/trusted-types/TrustedTypePolicyFactory-isXXX.tentative.html
+++ b/third_party/blink/web_tests/external/wpt/trusted-types/TrustedTypePolicyFactory-isXXX.tentative.html
@@ -82,6 +82,30 @@
     assert_false(TrustedTypes.isURL(url3));
   }, 'TrustedTypePolicyFactory.isURL requires the object to be created via policy.');
 
+  // Test non-object parameters.
+  test(t => {
+    assert_false(TrustedTypes.isHTML(null));
+    assert_false(TrustedTypes.isHTML(123));
+    assert_false(TrustedTypes.isHTML(0.5));
+    assert_false(TrustedTypes.isHTML('test'));
+    assert_false(TrustedTypes.isHTML({}));
+    assert_false(TrustedTypes.isScript(null));
+    assert_false(TrustedTypes.isScript(123));
+    assert_false(TrustedTypes.isScript(0.5));
+    assert_false(TrustedTypes.isScript('test'));
+    assert_false(TrustedTypes.isScript({}));
+    assert_false(TrustedTypes.isURL(null));
+    assert_false(TrustedTypes.isURL(123));
+    assert_false(TrustedTypes.isURL(0.5));
+    assert_false(TrustedTypes.isURL('test'));
+    assert_false(TrustedTypes.isURL({}));
+    assert_false(TrustedTypes.isScriptURL(null));
+    assert_false(TrustedTypes.isScriptURL(123));
+    assert_false(TrustedTypes.isScriptURL(0.5));
+    assert_false(TrustedTypes.isScriptURL('test'));
+    assert_false(TrustedTypes.isScriptURL({}));
+  }, 'TrustedTypePolicyFactory.isXXX should accept anything without throwing.');
+
   // Redefinition tests, assign to property.
   // (Assignments will through in the polyfill (because the objects are frozen)
   //  but will be silently dropped in the native implementation (because that's
diff --git a/third_party/blink/web_tests/external/wpt/uievents/click/click_event_target-manual.html b/third_party/blink/web_tests/external/wpt/uievents/click/click_event_target.html
similarity index 71%
rename from third_party/blink/web_tests/external/wpt/uievents/click/click_event_target-manual.html
rename to third_party/blink/web_tests/external/wpt/uievents/click/click_event_target.html
index 1e567026..ffe5dba 100644
--- a/third_party/blink/web_tests/external/wpt/uievents/click/click_event_target-manual.html
+++ b/third_party/blink/web_tests/external/wpt/uievents/click/click_event_target.html
@@ -5,6 +5,9 @@
     <title>Click targets the nearest common ancestor</title>
     <script src="/resources/testharness.js"></script>
     <script src="/resources/testharnessreport.js"></script>
+    <script src="/resources/testdriver.js"></script>
+    <script src="/resources/testdriver-actions.js"></script>
+    <script src="/resources/testdriver-vendor.js"></script>
     <style>
     div {
       padding: 10px;
@@ -70,6 +73,25 @@
       });
       test_click_target.done();
     });
+
+    // Inject mouse events.
+    var actions = new test_driver.Actions();
+    actions.pointerMove(0, 0, {origin: document.getElementById('red_div')})
+           .pointerDown()
+           .pointerMove(0, 0, {origin: document.getElementById('blue_div')})
+           .pointerUp()
+           .pointerMove(0, 0, {origin: document.getElementById('button1')})
+           .pointerDown()
+           .pointerMove(0, 0, {origin: document.getElementById('button2')})
+           .pointerUp()
+           .pointerMove(0, 0, {origin: document.getElementById('link1')})
+           .pointerDown()
+           .pointerMove(0, 0, {origin: document.getElementById('link2')})
+           .pointerUp()
+           .pointerMove(0, 0, {origin: document.getElementById('done')})
+           .pointerDown()
+           .pointerUp()
+           .send();
     </script>
   </body>
 </html>
diff --git a/third_party/blink/web_tests/fast/workers/worker-messageport.html b/third_party/blink/web_tests/external/wpt/workers/Worker-messageport.html
similarity index 83%
rename from third_party/blink/web_tests/fast/workers/worker-messageport.html
rename to third_party/blink/web_tests/external/wpt/workers/Worker-messageport.html
index 1c59195e..f7734b2 100644
--- a/third_party/blink/web_tests/fast/workers/worker-messageport.html
+++ b/third_party/blink/web_tests/external/wpt/workers/Worker-messageport.html
@@ -1,10 +1,10 @@
 <!DOCTYPE html>
 <title>Test that pages and workers can send MessagePorts to one another.</title>
-<script src="../../resources/testharness.js"></script>
-<script src="../../resources/testharnessreport.js"></script>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
 <script>
 async_test(function(t) {
-  var worker = new Worker("resources/worker-messageport.js");
+  var worker = new Worker("support/Worker-messageport.js");
 
   // Send messages with and without ports to the worker to make sure it gets them.
   worker.postMessage("noport");
@@ -14,7 +14,7 @@
 }, 'Test sending messages to workers with no port.');
 
 async_test(function(t) {
-  var worker = new Worker("resources/worker-messageport.js");
+  var worker = new Worker("support/Worker-messageport.js");
   var channel = new MessageChannel();
 
   worker.postMessage("port", [channel.port1]);
@@ -33,7 +33,7 @@
 }, 'Test sending message to a worker on a port.');
 
 async_test(function(t) {
-  var worker = new Worker("resources/worker-messageport.js");
+  var worker = new Worker("support/Worker-messageport.js");
   var channel = new MessageChannel();
 
   worker.onmessage = t.step_func(evt => {
@@ -50,7 +50,7 @@
 }, 'Test getting messages from a worker on a port.');
 
 async_test(function(t) {
-  var worker = new Worker("resources/worker-messageport.js");
+  var worker = new Worker("support/Worker-messageport.js");
   var channel = new MessageChannel();
   worker.onmessage = t.step_func(evt => { gotSpam(channel.port1); });
   worker.postMessage("spam", [channel.port2]);
diff --git a/third_party/blink/web_tests/fast/workers/worker-multi-port.html b/third_party/blink/web_tests/external/wpt/workers/Worker-multi-port.html
similarity index 80%
rename from third_party/blink/web_tests/fast/workers/worker-multi-port.html
rename to third_party/blink/web_tests/external/wpt/workers/Worker-multi-port.html
index 1a721f2..9a89583a 100644
--- a/third_party/blink/web_tests/fast/workers/worker-multi-port.html
+++ b/third_party/blink/web_tests/external/wpt/workers/Worker-multi-port.html
@@ -1,10 +1,10 @@
 <!DOCTYPE html>
 <title>Test sending multiple ports through Worker.postMessage.</title>
-<script src="../../resources/testharness.js"></script>
-<script src="../../resources/testharnessreport.js"></script>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
 <script>
 async_test(function(t) {
-  var worker = new Worker("resources/worker-thread-multi-port.js");
+  var worker = new Worker("support/Worker-thread-multi-port.js");
   worker.onmessage = t.step_func_done(function(evt) {
     assert_true(evt.data.startsWith('PASS'));
   });
@@ -12,7 +12,7 @@
 }, 'Test postMessage with no port.');
 
 async_test(function(t) {
-  var worker = new Worker("resources/worker-thread-multi-port.js");
+  var worker = new Worker("support/Worker-thread-multi-port.js");
   worker.onmessage = t.step_func_done(function(evt) {
     assert_true(evt.data.startsWith('PASS'));
   });
@@ -20,7 +20,7 @@
 }, 'Test postMessage with no arguments.');
 
 async_test(function(t) {
-  var worker = new Worker("resources/worker-thread-multi-port.js");
+  var worker = new Worker("support/Worker-thread-multi-port.js");
   worker.onmessage = t.step_func_done(function(evt) {
     assert_true(evt.data.startsWith('PASS'));
   });
@@ -28,7 +28,7 @@
 }, 'Test postMessage with no ports and empty array.');
 
 async_test(function(t) {
-  var worker = new Worker("resources/worker-thread-multi-port.js");
+  var worker = new Worker("support/Worker-thread-multi-port.js");
   var channel = new MessageChannel();
   worker.onmessage = t.step_func_done(function(evt) {
     assert_true(evt.data.startsWith('PASS'));
@@ -37,14 +37,14 @@
 }, 'Test postMessage with two ports.');
 
 test(() => {
-  var worker = new Worker("resources/worker-thread-multi-port.js");
+  var worker = new Worker("support/Worker-thread-multi-port.js");
   assert_throws(new TypeError(),
                 function() { worker.postMessage(); },
                 'Empty postMessage should throw exception.');
 }, 'Test empty postMessage throws exception.');
 
 test(() => {
-  var worker = new Worker("resources/worker-thread-multi-port.js");
+  var worker = new Worker("support/Worker-thread-multi-port.js");
   var channel = new MessageChannel();
   assert_throws(new TypeError(),
                 function() { worker.postMessage("null port",
@@ -54,7 +54,7 @@
 }, 'Test postMessage with null ports throws exception.');
 
 test(() => {
-  var worker = new Worker("resources/worker-thread-multi-port.js")
+  var worker = new Worker("support/Worker-thread-multi-port.js")
   var channel = new MessageChannel();
   assert_throws(new TypeError(),
                 function() { worker.postMessage("notAPort",
@@ -64,14 +64,14 @@
 }, 'Test postMessage with incorrect ports throws exception');
 
 test(() => {
-  var worker = new Worker("resources/worker-thread-multi-port.js");
+  var worker = new Worker("support/Worker-thread-multi-port.js");
   assert_throws(new TypeError(),
                 function() { worker.postMessage("notASequence", [{length: 3}]) },
                 'postMessage without sequence should throw exception.');
 }, 'Test postMessage without sequence throws exception');
 
 async_test(function(t) {
-  var worker = new Worker("resources/worker-thread-multi-port.js");
+  var worker = new Worker("support/Worker-thread-multi-port.js");
   var channel = new MessageChannel();
   assert_throws(new TypeError(),
                 function() { worker.postMessage("notAPort",
diff --git a/third_party/blink/web_tests/fast/workers/worker-nested-importScripts-error.html b/third_party/blink/web_tests/external/wpt/workers/Worker-nested-importScripts-error.html
similarity index 71%
rename from third_party/blink/web_tests/fast/workers/worker-nested-importScripts-error.html
rename to third_party/blink/web_tests/external/wpt/workers/Worker-nested-importScripts-error.html
index 61cdf4c7..8863b752 100644
--- a/third_party/blink/web_tests/fast/workers/worker-nested-importScripts-error.html
+++ b/third_party/blink/web_tests/external/wpt/workers/Worker-nested-importScripts-error.html
@@ -1,13 +1,13 @@
 <!DOCTYPE html>
 <title>This tests that errors from nested importScripts have the expected provenance.</title>
-<script src="../../resources/testharness.js"></script>
-<script src="../../resources/testharnessreport.js"></script>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
 <script>
 promise_test(t => {
   let worker;
 
   return new Promise((resolve) => {
-    worker = new Worker("resources/importScripts-1.js");
+    worker = new Worker("support/importScripts-1.js");
     worker.onerror = resolve;
   }).then(e => {
     assert_equals(e.type, "error");
diff --git a/third_party/blink/web_tests/fast/workers/worker-structure-message.html b/third_party/blink/web_tests/external/wpt/workers/Worker-structure-message.html
similarity index 80%
rename from third_party/blink/web_tests/fast/workers/worker-structure-message.html
rename to third_party/blink/web_tests/external/wpt/workers/Worker-structure-message.html
index 200f863..9eb12dc 100644
--- a/third_party/blink/web_tests/fast/workers/worker-structure-message.html
+++ b/third_party/blink/web_tests/external/wpt/workers/Worker-structure-message.html
@@ -1,13 +1,13 @@
 <!DOCTYPE html>
 <title>Test that pages and workers can send Structure Message to one another.</title>
-<script src="../../resources/testharness.js"></script>
-<script src="../../resources/testharnessreport.js"></script>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
 <script>
 promise_test(t => {
   let worker;
 
   return new Promise(resolve => {
-    worker = new Worker("resources/worker-structure-message.js");
+    worker = new Worker("support/Worker-structure-message.js");
     worker.onmessage = resolve;
     worker.postMessage({
       operation: 'find-edges',
diff --git a/third_party/blink/web_tests/external/wpt/workers/Worker-terminate-forever.html b/third_party/blink/web_tests/external/wpt/workers/Worker-terminate-forever.html
new file mode 100644
index 0000000..3528bb62
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/workers/Worker-terminate-forever.html
@@ -0,0 +1,11 @@
+<!DOCTYPE html>
+<title>Test Worker.terminate() for a worker that tries to run forever.</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+test((t) => {
+  var worker = new Worker('support/Worker-run-forever.js');
+  worker.terminate();
+  t.step_timeout(function() { t.done(); }, 500);
+}, 'Tests terminating a worker that is trying to run forever.');
+</script>
diff --git a/third_party/blink/web_tests/fast/workers/worker-timeout-cancel-order.html b/third_party/blink/web_tests/external/wpt/workers/Worker-timeout-cancel-order.html
similarity index 66%
rename from third_party/blink/web_tests/fast/workers/worker-timeout-cancel-order.html
rename to third_party/blink/web_tests/external/wpt/workers/Worker-timeout-cancel-order.html
index f15d51c9..78d930d 100644
--- a/third_party/blink/web_tests/fast/workers/worker-timeout-cancel-order.html
+++ b/third_party/blink/web_tests/external/wpt/workers/Worker-timeout-cancel-order.html
@@ -1,13 +1,13 @@
 <!DOCTYPE html>
 <title>Test setTimeOut,cancelTimeout in Web Workers.</title>
-<script src="../../resources/testharness.js"></script>
-<script src="../../resources/testharnessreport.js"></script>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
 <script>
 promise_test(t => {
   let worker
 
   return new Promise(resolve => {
-    worker = new Worker('resources/worker-timeout-cancel-order.js');
+    worker = new Worker('support/Worker-timeout-cancel-order.js');
     worker.postMessage('start');
     worker.onmessage = resolve;
   }).then(evt => {
diff --git a/third_party/blink/web_tests/fast/workers/worker-timeout-decreasing-order.html b/third_party/blink/web_tests/external/wpt/workers/Worker-timeout-decreasing-order.html
similarity index 75%
rename from third_party/blink/web_tests/fast/workers/worker-timeout-decreasing-order.html
rename to third_party/blink/web_tests/external/wpt/workers/Worker-timeout-decreasing-order.html
index da4056c..f459461e 100644
--- a/third_party/blink/web_tests/fast/workers/worker-timeout-decreasing-order.html
+++ b/third_party/blink/web_tests/external/wpt/workers/Worker-timeout-decreasing-order.html
@@ -1,13 +1,13 @@
 <!DOCTYPE html>
 <title>Test setTimeOut,fired in decreasing order in Web Workers.</title>
-<script src="../../resources/testharness.js"></script>
-<script src="../../resources/testharnessreport.js"></script>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
 <script>
 promise_test (t => {
   let worker;
-    
+
   return new Promise(resolve => {
-    worker = new Worker('resources/worker-timeout-decreasing-order.js');
+    worker = new Worker('support/Worker-timeout-decreasing-order.js');
     worker.postMessage('start');
     worker.onmessage = resolve;
   }).then(evt => {
diff --git a/third_party/blink/web_tests/fast/workers/worker-timeout-increasing-order.html b/third_party/blink/web_tests/external/wpt/workers/Worker-timeout-increasing-order.html
similarity index 75%
rename from third_party/blink/web_tests/fast/workers/worker-timeout-increasing-order.html
rename to third_party/blink/web_tests/external/wpt/workers/Worker-timeout-increasing-order.html
index 3e44116..34b02946 100644
--- a/third_party/blink/web_tests/fast/workers/worker-timeout-increasing-order.html
+++ b/third_party/blink/web_tests/external/wpt/workers/Worker-timeout-increasing-order.html
@@ -1,13 +1,13 @@
 <!DOCTYPE html>
 <title>Test setTimeOut,fired in increasing order in Web Workers.</title>
-<script src="../../resources/testharness.js"></script>
-<script src="../../resources/testharnessreport.js"></script>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
 <script>
 promise_test (t => {
   let worker;
 
   return new Promise(resolve => {
-    worker = new Worker('resources/worker-timeout-increasing-order.js');
+    worker = new Worker('support/Worker-timeout-increasing-order.js');
     worker.postMessage('start');
     worker.onmessage = resolve;
   }).then(evt => {
diff --git a/third_party/blink/web_tests/fast/workers/worker-close.html b/third_party/blink/web_tests/external/wpt/workers/WorkerGlobalScope-close.html
similarity index 81%
rename from third_party/blink/web_tests/fast/workers/worker-close.html
rename to third_party/blink/web_tests/external/wpt/workers/WorkerGlobalScope-close.html
index f54af13..a193a34 100644
--- a/third_party/blink/web_tests/fast/workers/worker-close.html
+++ b/third_party/blink/web_tests/external/wpt/workers/WorkerGlobalScope-close.html
@@ -1,12 +1,12 @@
 <!DOCTYPE html>
 <title>Test WorkerGlobalScope.close functionality.</title>
-<script src="../../resources/testharness.js"></script>
-<script src="../../resources/testharnessreport.js"></script>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
 <script>
 setup({ allow_uncaught_exception: true });
 
 async_test(function(t) {
-  var worker = new Worker('resources/worker-close.js');
+  var worker = new Worker('support/WorkerGlobalScope-close.js');
   worker.postMessage("typeofClose");
     worker.onmessage = t.step_func_done(function(evt) {
     assert_equals(evt.data, "typeof close: function");
@@ -14,7 +14,7 @@
 }, 'Test type of close function.');
 
 async_test(function(t) {
-  var worker = new Worker('resources/worker-close.js');  
+  var worker = new Worker('support/WorkerGlobalScope-close.js');
   worker.postMessage("ping");
     worker.onmessage = t.step_func(function(evt) {
     assert_equals(evt.data, "pong");
@@ -32,7 +32,7 @@
 }, 'Test sending a message after closing.');
 
 async_test(function(t) {
-  var worker = new Worker('resources/worker-close.js');
+  var worker = new Worker('support/WorkerGlobalScope-close.js');
   worker.postMessage("closeWithError");
   worker.onerror = function(event) {
     t.done()
@@ -40,7 +40,7 @@
 }, 'Test errors are delivered after close.');
 
 async_test(function(t) {
-  var worker = new Worker('resources/worker-close.js');
+  var worker = new Worker('support/WorkerGlobalScope-close.js');
   worker.postMessage("closeWithPendingEvents");
   worker.onmessage = t.step_func(function(evt) {
     assert_unreached("Pending events should not fire: " + evt.data);
@@ -52,7 +52,7 @@
 }, 'Test workers do not deliver pending events.');
 
 async_test(function(t) {
-  var worker = new Worker('resources/worker-close.js');
+  var worker = new Worker('support/WorkerGlobalScope-close.js');
   worker.postMessage("close");
   worker.onmessage = function(evt) {
     assert_equals(evt.data, "Should be delivered");
diff --git a/third_party/blink/web_tests/external/wpt/workers/WorkerGlobalScope_close.htm b/third_party/blink/web_tests/external/wpt/workers/WorkerGlobalScope_close.htm
deleted file mode 100644
index 2ad5205..0000000
--- a/third_party/blink/web_tests/external/wpt/workers/WorkerGlobalScope_close.htm
+++ /dev/null
@@ -1,17 +0,0 @@
-<!DOCTYPE html>
-<title> WorkerGlobalScope close(): clear events queue </title>
-<script src="/resources/testharness.js"></script>
-<script src="/resources/testharnessreport.js"></script>
-<div id=log></div>
-<script>
-async_test(function(t) {
-  var worker = new Worker('./support/WorkerClose.js');
-  worker.onmessage = t.step_func(function(e) {
-    assert_equals(e.data, "ping");
-    worker.onmessage = t.unreached_func("Unexpected message event");
-    worker.postMessage("pong");
-    setTimeout(t.step_func_done(), 100);
-  });
-  worker.postMessage("ping");
-});
-</script>
diff --git a/third_party/blink/web_tests/fast/workers/worker-constructor.html b/third_party/blink/web_tests/external/wpt/workers/constructors/Worker/Worker-constructor.html
similarity index 90%
rename from third_party/blink/web_tests/fast/workers/worker-constructor.html
rename to third_party/blink/web_tests/external/wpt/workers/constructors/Worker/Worker-constructor.html
index e6af447..e073df7 100644
--- a/third_party/blink/web_tests/fast/workers/worker-constructor.html
+++ b/third_party/blink/web_tests/external/wpt/workers/constructors/Worker/Worker-constructor.html
@@ -1,7 +1,7 @@
 <!DOCTYPE html>
 <title>Test Worker constructor functionality.</title>
-<script src="../../resources/testharness.js"></script>
-<script src="../../resources/testharnessreport.js"></script>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
 <script>
 
 test(() => {
@@ -51,7 +51,7 @@
 }, 'Test not existent script URL.');
 
 test(() => {
-  var worker = new Worker('resources/worker-common.js');
+  var worker = new Worker('../../support/Worker-common.js');
   assert_true('postMessage' in worker,
               'worker.postMessage did not exist.');
   assert_true('addEventListener' in worker,
diff --git a/third_party/blink/web_tests/external/wpt/workers/constructors/Worker/no-arguments-ctor.html b/third_party/blink/web_tests/external/wpt/workers/constructors/Worker/no-arguments-ctor.html
deleted file mode 100644
index 770c7cc..0000000
--- a/third_party/blink/web_tests/external/wpt/workers/constructors/Worker/no-arguments-ctor.html
+++ /dev/null
@@ -1,13 +0,0 @@
-<!doctype html>
-<meta charset=utf-8>
-<title>without arguments</title>
-<link rel=help href="http://dev.w3.org/2006/webapi/WebIDL/#es-interface-call">
-<link rel=help href="http://www.whatwg.org/html/#dedicated-workers-and-the-worker-interface">
-<script src="/resources/testharness.js"></script>
-<script src="/resources/testharnessreport.js"></script>
-<div id="log"></div>
-<script>
-test(function() {
-  assert_throws(new TypeError(), function() { new Worker(); });
-});
-</script>
diff --git a/third_party/blink/web_tests/external/wpt/workers/constructors/Worker/resolve-empty-string.html b/third_party/blink/web_tests/external/wpt/workers/constructors/Worker/resolve-empty-string.html
deleted file mode 100644
index 0426223..0000000
--- a/third_party/blink/web_tests/external/wpt/workers/constructors/Worker/resolve-empty-string.html
+++ /dev/null
@@ -1,22 +0,0 @@
-<!--
-postMessage('ok');
-/*
--->
-<!doctype html>
-<meta charset=utf-8>
-<title>resolve the empty string</title>
-<script src="/resources/testharness.js"></script>
-<script src="/resources/testharnessreport.js"></script>
-<div id="log"></div>
-<script>
-var t = async_test();
-t.step(function() {
-  var worker = new Worker('');
-  worker.addEventListener('message', t.step_func_done(function(e) {
-    assert_equals(e.data, 'ok');
-  }), false);
-});
-</script>
-<!--
-*/
-//-->
diff --git a/third_party/blink/web_tests/external/wpt/workers/constructors/Worker/unresolvable-url-expected.txt b/third_party/blink/web_tests/external/wpt/workers/constructors/Worker/unresolvable-url-expected.txt
deleted file mode 100644
index f6e2da6..0000000
--- a/third_party/blink/web_tests/external/wpt/workers/constructors/Worker/unresolvable-url-expected.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-This is a testharness.js-based test.
-FAIL unresolvable url assert_throws: function "function() { new Worker('http://invalid url/'); }" threw object "SecurityError: Failed to construct 'Worker': Script at 'http://invalid%20url/' cannot be accessed from origin 'http://web-platform.test:8001'." that is not a DOMException SyntaxError: property "code" is equal to 18, expected 12
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/external/wpt/workers/constructors/Worker/unresolvable-url.html b/third_party/blink/web_tests/external/wpt/workers/constructors/Worker/unresolvable-url.html
deleted file mode 100644
index 8c04b00..0000000
--- a/third_party/blink/web_tests/external/wpt/workers/constructors/Worker/unresolvable-url.html
+++ /dev/null
@@ -1,12 +0,0 @@
-<!doctype html>
-<meta charset=utf-8>
-<title>unresolvable url</title>
-<link rel=help href="http://www.whatwg.org/html/#dom-worker">
-<script src="/resources/testharness.js"></script>
-<script src="/resources/testharnessreport.js"></script>
-<div id="log"></div>
-<script>
-test(function() {
-  assert_throws("SyntaxError", function() { new Worker('http://invalid url/'); });
-});
-</script>
diff --git a/third_party/blink/web_tests/external/wpt/workers/support/Worker-messageport.js b/third_party/blink/web_tests/external/wpt/workers/support/Worker-messageport.js
new file mode 100644
index 0000000..1e01b0a
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/workers/support/Worker-messageport.js
@@ -0,0 +1,40 @@
+onmessage = function(evt) {
+    if (evt.data == "port") {
+        if (evt.ports) {
+            postMessage("PASS: Received message port");
+            evt.ports[0].onmessage = pingBack;
+            evt.ports[0].start();
+        } else {
+            postMessage("FAIL: Did not receive expected MessagePort");
+        }
+    } else if (evt.data == "noport") {
+        if (!evt.ports || evt.ports.length) {
+            postMessage("FAIL: Received message port or null ports array");
+        } else {
+            postMessage("PASS: evt.ports = [] as expected");
+        }
+    } else if (evt.data == "spam") {
+        for (var i = 0 ; i < 1000 ; i++) {
+            evt.ports[0].postMessage(i);
+        }
+        postMessage("spamDone");
+    } else if (evt.data == "getport") {
+        var channel = new MessageChannel();
+        postMessage("port", [channel.port1]);
+        channel.port2.onmessage = pingBack;
+        channel.port2.start();
+    } else {
+        postMessage("Unknown message:" + evt.data);
+    }
+
+}
+
+function pingBack(evt) {
+    // Make sure we got the expected data and send a return message over
+    // the port.
+    if (evt.data == "ping") {
+        evt.target.postMessage("pong");
+    } else {
+        postMessage("FAIL: unknown message: " + evt.data);
+    }
+}
diff --git a/third_party/blink/web_tests/fast/workers/resources/worker-run-forever.js b/third_party/blink/web_tests/external/wpt/workers/support/Worker-run-forever.js
similarity index 100%
rename from third_party/blink/web_tests/fast/workers/resources/worker-run-forever.js
rename to third_party/blink/web_tests/external/wpt/workers/support/Worker-run-forever.js
diff --git a/third_party/blink/web_tests/fast/workers/resources/worker-structure-message.js b/third_party/blink/web_tests/external/wpt/workers/support/Worker-structure-message.js
similarity index 100%
rename from third_party/blink/web_tests/fast/workers/resources/worker-structure-message.js
rename to third_party/blink/web_tests/external/wpt/workers/support/Worker-structure-message.js
diff --git a/third_party/blink/web_tests/fast/workers/resources/worker-thread-multi-port.js b/third_party/blink/web_tests/external/wpt/workers/support/Worker-thread-multi-port.js
similarity index 100%
rename from third_party/blink/web_tests/fast/workers/resources/worker-thread-multi-port.js
rename to third_party/blink/web_tests/external/wpt/workers/support/Worker-thread-multi-port.js
diff --git a/third_party/blink/web_tests/fast/workers/resources/worker-timeout-cancel-order.js b/third_party/blink/web_tests/external/wpt/workers/support/Worker-timeout-cancel-order.js
similarity index 100%
rename from third_party/blink/web_tests/fast/workers/resources/worker-timeout-cancel-order.js
rename to third_party/blink/web_tests/external/wpt/workers/support/Worker-timeout-cancel-order.js
diff --git a/third_party/blink/web_tests/fast/workers/resources/worker-timeout-decreasing-order.js b/third_party/blink/web_tests/external/wpt/workers/support/Worker-timeout-decreasing-order.js
similarity index 100%
rename from third_party/blink/web_tests/fast/workers/resources/worker-timeout-decreasing-order.js
rename to third_party/blink/web_tests/external/wpt/workers/support/Worker-timeout-decreasing-order.js
diff --git a/third_party/blink/web_tests/fast/workers/resources/worker-timeout-increasing-order.js b/third_party/blink/web_tests/external/wpt/workers/support/Worker-timeout-increasing-order.js
similarity index 100%
rename from third_party/blink/web_tests/fast/workers/resources/worker-timeout-increasing-order.js
rename to third_party/blink/web_tests/external/wpt/workers/support/Worker-timeout-increasing-order.js
diff --git a/third_party/blink/web_tests/external/wpt/workers/support/WorkerGlobalScope-close.js b/third_party/blink/web_tests/external/wpt/workers/support/WorkerGlobalScope-close.js
new file mode 100644
index 0000000..e2b1c0a
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/workers/support/WorkerGlobalScope-close.js
@@ -0,0 +1,43 @@
+// Check to see if the worker handles pending events. Messages after close() will not be sent to the parent page, so we use exceptions instead to report failures after close().
+onmessage = function(evt)
+{
+    if (evt.data == "closeWithPendingEvents") {
+        // Set a timer to generate an event - minimum timeout is 1ms.
+        setTimeout(function() {
+                postMessage("pending event processed");
+                throw "should not be executed";
+            }, 1);
+        var start = new Date().getTime();
+        // Loop for 10 ms so the timer is ready to fire
+        while (new Date().getTime() - start < 100)
+            ;
+        // Now close - timer should not fire
+        close();
+    } else if (evt.data == "typeofClose") {
+        postMessage("typeof close: " + (typeof close));
+    } else if (evt.data == "close") {
+        close();
+        postMessage("Should be delivered");
+    } else if (evt.data == "ping") {
+        postMessage("pong");
+    } else if (evt.data == "throw") {
+        throw "should never be executed";
+    } else if (evt.data == "closeWithError") {
+        close();
+        nonExistentFunction();  // Undefined function - throws exception
+    } else if (evt.data == "close_post_loop") {
+        close();
+        postMessage("closed");
+        while(true) {} // Should loop forever.
+    } else if (evt.data == "take_port") {
+        messagePort = evt.ports[0];
+        messagePort.onmessage = function(event) {
+            close();
+            postMessage("echo_" + event.data);
+        }
+    } else if (evt.data == "start_port") {
+        messagePort.start();
+    } else {
+        postMessage("FAIL: Unknown message type: " + evt.data);
+    }
+}
diff --git a/third_party/blink/web_tests/fast/workers/resources/importScripts-1.js b/third_party/blink/web_tests/external/wpt/workers/support/importScripts-1.js
similarity index 100%
rename from third_party/blink/web_tests/fast/workers/resources/importScripts-1.js
rename to third_party/blink/web_tests/external/wpt/workers/support/importScripts-1.js
diff --git a/third_party/blink/web_tests/fast/workers/resources/importScripts-2.js b/third_party/blink/web_tests/external/wpt/workers/support/importScripts-2.js
similarity index 100%
rename from third_party/blink/web_tests/fast/workers/resources/importScripts-2.js
rename to third_party/blink/web_tests/external/wpt/workers/support/importScripts-2.js
diff --git a/third_party/blink/web_tests/fast/workers/resources/importScripts-3.js b/third_party/blink/web_tests/external/wpt/workers/support/importScripts-3.js
similarity index 100%
rename from third_party/blink/web_tests/fast/workers/resources/importScripts-3.js
rename to third_party/blink/web_tests/external/wpt/workers/support/importScripts-3.js
diff --git a/third_party/blink/web_tests/fast/workers/resources/invalidScript.js b/third_party/blink/web_tests/external/wpt/workers/support/invalidScript.js
similarity index 100%
rename from third_party/blink/web_tests/fast/workers/resources/invalidScript.js
rename to third_party/blink/web_tests/external/wpt/workers/support/invalidScript.js
diff --git a/third_party/blink/web_tests/external/wpt_automation/uievents/click/click_event_target-manual-automation.js b/third_party/blink/web_tests/external/wpt_automation/uievents/click/click_event_target-manual-automation.js
deleted file mode 100644
index 65bf360..0000000
--- a/third_party/blink/web_tests/external/wpt_automation/uievents/click/click_event_target-manual-automation.js
+++ /dev/null
@@ -1,11 +0,0 @@
-importAutomationScript('/pointerevents/pointerevent_common_input.js');
-
-function inject_input() {
-  return mouseDragInTargets(['#red_div', '#blue_div']).then(function() {
-    return mouseDragInTargets(['#button1', '#button2']);
-  }).then(function() {
-    return mouseDragInTargets(['#link1', '#link2']);
-  }).then(function() {
-    return mouseClickInTarget('#done');
-  });
-}
diff --git a/third_party/blink/web_tests/fast/events/resources/test.m4a b/third_party/blink/web_tests/fast/events/resources/test.m4a
new file mode 100644
index 0000000..4c03ac5
--- /dev/null
+++ b/third_party/blink/web_tests/fast/events/resources/test.m4a
Binary files differ
diff --git a/third_party/blink/web_tests/fast/mediastream/MediaStreamTrack-getSettings.html b/third_party/blink/web_tests/fast/mediastream/MediaStreamTrack-getSettings.html
index e223c52..da405fe 100644
--- a/third_party/blink/web_tests/fast/mediastream/MediaStreamTrack-getSettings.html
+++ b/third_party/blink/web_tests/fast/mediastream/MediaStreamTrack-getSettings.html
@@ -34,7 +34,7 @@
                 'Noise suppression missing: ' + JSON.stringify(settings));
     assert_true('echoCancellationType' in settings,
                 'Echo cancellation type missing: ' + JSON.stringify(settings));
-    assert_in_array(settings.echoCancellationType, [ "aec3", "system" ],
+    assert_in_array(settings.echoCancellationType, [ "browser", "system" ],
                 'Echo cancellation type invalid: ' + JSON.stringify(settings));
   });
 }, 'An audio track returns the expected variables');
diff --git a/third_party/blink/web_tests/fast/workers/worker-terminate-forever.html b/third_party/blink/web_tests/fast/workers/worker-terminate-forever.html
deleted file mode 100644
index cd6db3e..0000000
--- a/third_party/blink/web_tests/fast/workers/worker-terminate-forever.html
+++ /dev/null
@@ -1,11 +0,0 @@
-<!DOCTYPE html>
-<title>Test Worker.terminate() for a worker that tries to run forever.</title>
-<script src="../../resources/testharness.js"></script>
-<script src="../../resources/testharnessreport.js"></script>
-<script>
-test((t) => {
-  var worker = new Worker('resources/worker-run-forever.js');
-  worker.terminate();
-  t.step_timeout(function() { t.done(); }, 500);
-}, 'Tests terminating a worker that is trying to run forever.');
-</script>
diff --git a/third_party/blink/web_tests/http/tests/devtools/console/argument-hints-expected.txt b/third_party/blink/web_tests/http/tests/devtools/console/argument-hints-expected.txt
index b2c90ca..bbb29de7 100644
--- a/third_party/blink/web_tests/http/tests/devtools/console/argument-hints-expected.txt
+++ b/third_party/blink/web_tests/http/tests/devtools/console/argument-hints-expected.txt
@@ -92,7 +92,6 @@
 URL.createObjectURL(
 blob
 source
-stream
 
 (() => window)().URL["revokeObjectURL"](
 url
@@ -103,4 +102,13 @@
 some gibberish $@#)(*^@#
 null
 
+Date.parse(
+s
+
+JSON.parse(
+text,?reviver
+
+CSSNumericValue.parse(
+cssText
+
 
diff --git a/third_party/blink/web_tests/http/tests/devtools/console/argument-hints.js b/third_party/blink/web_tests/http/tests/devtools/console/argument-hints.js
index 6244dbf..742a862 100644
--- a/third_party/blink/web_tests/http/tests/devtools/console/argument-hints.js
+++ b/third_party/blink/web_tests/http/tests/devtools/console/argument-hints.js
@@ -82,6 +82,9 @@
   await testHints('(() => window)().URL["revokeObjectURL"](');
   await testHints('var notInAfunction');
   await testHints('some gibberish $@#)(*^@#');
+  await testHints('Date.parse(');
+  await testHints('JSON.parse(');
+  await testHints('CSSNumericValue.parse(');
   TestRunner.completeTest();
 
   /**
diff --git a/third_party/blink/web_tests/http/tests/devtools/tracing/timeline-js/timeline-js-line-level-profile-no-url-end-to-end-expected.txt b/third_party/blink/web_tests/http/tests/devtools/tracing/timeline-js/timeline-js-line-level-profile-no-url-end-to-end-expected.txt
new file mode 100644
index 0000000..f4dc996
--- /dev/null
+++ b/third_party/blink/web_tests/http/tests/devtools/tracing/timeline-js/timeline-js-line-level-profile-no-url-end-to-end-expected.txt
@@ -0,0 +1,4 @@
+Tests that a line-level CPU profile is collected and shown in the text editor.
+
+Decoration found: true
+
diff --git a/third_party/blink/web_tests/http/tests/devtools/tracing/timeline-js/timeline-js-line-level-profile-no-url-end-to-end.js b/third_party/blink/web_tests/http/tests/devtools/tracing/timeline-js/timeline-js-line-level-profile-no-url-end-to-end.js
new file mode 100644
index 0000000..cc1fb9f
--- /dev/null
+++ b/third_party/blink/web_tests/http/tests/devtools/tracing/timeline-js/timeline-js-line-level-profile-no-url-end-to-end.js
@@ -0,0 +1,49 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+(async function() {
+  TestRunner.addResult(`Tests that a line-level CPU profile is collected and shown in the text editor.\n`);
+  await TestRunner.loadModule('console_test_runner');
+  await TestRunner.loadModule('sources_test_runner');
+  await TestRunner.loadModule('performance_test_runner');
+  await TestRunner.showPanel('timeline');
+  await TestRunner.showPanel('sources');
+
+  await TestRunner.evaluateInPageAnonymously(`
+      function performActions() {
+        console.trace('Message to capture the scriptId');
+        const endTime = Date.now() + 100;
+        let s = 0;
+        while (Date.now() < endTime) s += Math.cos(s);
+        return s;
+      }`);
+
+  let scriptId;
+  ConsoleTestRunner.addConsoleSniffer(m => {
+    if (m.messageText === 'Message to capture the scriptId')
+      scriptId = m.stackTrace.callFrames[0].scriptId;
+  }, true);
+
+  let hasLineLevelInfo;
+  do {
+    await PerformanceTestRunner.evaluateWithTimeline('performActions()');
+    const events = PerformanceTestRunner.timelineModel().inspectedTargetEvents();
+    hasLineLevelInfo = events.some(e => e.name === 'ProfileChunk' && e.args.data.lines);
+  } while (!hasLineLevelInfo);
+
+  TestRunner.addSniffer(SourceFrame.SourcesTextEditor.prototype, 'setGutterDecoration', decorationAdded, true);
+
+  const debuggerModel = SDK.targetManager.mainTarget().model(SDK.DebuggerModel);
+  const rawLocation = debuggerModel.createRawLocationByScriptId(scriptId, 0, 0);
+  const uiLocation = Bindings.debuggerWorkspaceBinding.rawLocationToUILocation(rawLocation);
+  await SourcesTestRunner.showUISourceCodePromise(uiLocation.uiSourceCode);
+
+  function decorationAdded(line, type, element) {
+    if (type !== 'CodeMirror-gutter-performance' || line !== 5)
+      return;
+    const value = parseFloat(element.textContent);
+    TestRunner.addResult(`Decoration found: ${isFinite(value)}`);
+    TestRunner.completeTest();
+  }
+})();
diff --git a/third_party/blink/web_tests/http/tests/inspector-protocol/resources/inspector-protocol-test.js b/third_party/blink/web_tests/http/tests/inspector-protocol/resources/inspector-protocol-test.js
index c6b30d7..c5a52c6 100644
--- a/third_party/blink/web_tests/http/tests/inspector-protocol/resources/inspector-protocol-test.js
+++ b/third_party/blink/web_tests/http/tests/inspector-protocol/resources/inspector-protocol-test.js
@@ -13,6 +13,10 @@
     this._browserSession = new TestRunner.Session(this, '');
   }
 
+  static get stabilizeNames() {
+    return ['id', 'nodeId', 'objectId', 'scriptId', 'timestamp', 'backendNodeId', 'parentId', 'frameId', 'loaderId', 'baseURL', 'documentURL', 'styleSheetId', 'executionContextId', 'targetId', 'browserContextId', 'sessionId', 'ownerNode'];
+  }
+
   startDumpingProtocolMessages() {
     this._dumpInspectorProtocolMessages = true;
   };
@@ -27,7 +31,7 @@
     this._log.call(null, item);
   }
 
-  _logObject(object, title, stabilizeNames = ['id', 'nodeId', 'objectId', 'scriptId', 'timestamp', 'backendNodeId', 'parentId', 'frameId', 'loaderId', 'baseURL', 'documentURL', 'styleSheetId', 'executionContextId', 'targetId', 'browserContextId', 'sessionId']) {
+  _logObject(object, title, stabilizeNames = TestRunner.stabilizeNames) {
     var lines = [];
 
     function dumpValue(value, prefix, prefixWithName) {
diff --git a/third_party/blink/web_tests/images/cross-fade-overflow-position-expected.png b/third_party/blink/web_tests/images/cross-fade-overflow-position-expected.png
index a64ffc9a..1ba6f8c 100644
--- a/third_party/blink/web_tests/images/cross-fade-overflow-position-expected.png
+++ b/third_party/blink/web_tests/images/cross-fade-overflow-position-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/images/drag-svg-image-expected.png b/third_party/blink/web_tests/images/drag-svg-image-expected.png
index acb2e07..9aa8bb4 100644
--- a/third_party/blink/web_tests/images/drag-svg-image-expected.png
+++ b/third_party/blink/web_tests/images/drag-svg-image-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/inspector-protocol/css/css-get-styles-for-node-expected.txt b/third_party/blink/web_tests/inspector-protocol/css/css-get-styles-for-node-expected.txt
new file mode 100644
index 0000000..f3a93c93
--- /dev/null
+++ b/third_party/blink/web_tests/inspector-protocol/css/css-get-styles-for-node-expected.txt
@@ -0,0 +1,43 @@
+The test verifies functionality of protocol method CSS.getMatchedStylesForNode and CSS.getInlineStylesForNode.
+{
+    error : {
+        code : -32000
+        message : Node is not an Element
+    }
+    id : <number>
+    sessionId : <string>
+}
+{
+    error : {
+        code : -32000
+        message : Node is not an Element
+    }
+    id : <number>
+    sessionId : <string>
+}
+Dumping inline style: 
+{
+    padding-top: 55px;
+    margin-top: 33px !important;
+}
+Dumping matched rules: 
+*#inspected* {    regular
+    margin-left: 10px !important;
+}
+*#inspected* {    regular
+    padding: 10px 20px 30px 40px;
+    padding-top: 50px;
+    padding-right: 20px;
+    padding-bottom: 30px;
+    padding-left: 40px;
+}
+@media (min-width: 1px)
+    *#inspected* {    regular
+        padding-left: 5px;
+        margin-left: 20px;
+        padding-left: 10px;
+        margin-top: 15px !important;
+    }
+Dumping inherited rules: 
+    padding-top: 20px;
+
diff --git a/third_party/blink/web_tests/inspector-protocol/css/css-get-styles-for-node.js b/third_party/blink/web_tests/inspector-protocol/css/css-get-styles-for-node.js
new file mode 100644
index 0000000..5d186ed
--- /dev/null
+++ b/third_party/blink/web_tests/inspector-protocol/css/css-get-styles-for-node.js
@@ -0,0 +1,28 @@
+(async function(testRunner) {
+  var {page, session, dp} = await testRunner.startHTML(`
+      <link rel='stylesheet' href='${testRunner.url('resources/set-active-property-value.css')}'/>
+      <div id='parent-div' style='padding-top: 20px;'>
+          <div id='inspected' style='padding-top: 55px; margin-top: 33px !important;'></div>
+      </div>`,
+      'The test verifies functionality of protocol method CSS.getMatchedStylesForNode and CSS.getInlineStylesForNode.');
+
+  await dp.DOM.enable();
+  await dp.CSS.enable();
+
+  const CSSHelper = await testRunner.loadScript('../resources/css-helper.js');
+  const cssHelper = new CSSHelper(testRunner, dp);
+
+  const documentNodeId = await cssHelper.requestDocumentNodeId();
+  await cssHelper.requestNodeId(documentNodeId, '#inspected');
+
+  // Test on non Element node
+  let result = await dp.CSS.getInlineStylesForNode({'nodeId': documentNodeId});
+  testRunner.log(result);
+  result = await dp.CSS.getMatchedStylesForNode({'nodeId': documentNodeId});
+  testRunner.log(result);
+
+  // Test on Element node
+  await cssHelper.loadAndDumpInlineAndMatchingRules(documentNodeId, '#inspected');
+  testRunner.completeTest();
+});
+
diff --git a/third_party/blink/web_tests/inspector-protocol/css/css-style-sheet-events-expected.txt b/third_party/blink/web_tests/inspector-protocol/css/css-style-sheet-events-expected.txt
new file mode 100644
index 0000000..6991d2c0
--- /dev/null
+++ b/third_party/blink/web_tests/inspector-protocol/css/css-style-sheet-events-expected.txt
@@ -0,0 +1,35 @@
+The test verifies events fire for adding, changing, and removing CSSStyleSheets.
+{
+    method : CSS.styleSheetAdded
+    params : {
+        header : {
+            disabled : false
+            frameId : <string>
+            isInline : false
+            length : <number>
+            origin : regular
+            ownerNode : <number>
+            sourceURL : 
+            startColumn : 0
+            startLine : 0
+            styleSheetId : <string>
+            title : 
+        }
+    }
+    sessionId : <string>
+}
+{
+    method : CSS.styleSheetChanged
+    params : {
+        styleSheetId : <string>
+    }
+    sessionId : <string>
+}
+{
+    method : CSS.styleSheetRemoved
+    params : {
+        styleSheetId : <string>
+    }
+    sessionId : <string>
+}
+
diff --git a/third_party/blink/web_tests/inspector-protocol/css/css-style-sheet-events.js b/third_party/blink/web_tests/inspector-protocol/css/css-style-sheet-events.js
new file mode 100644
index 0000000..a2730e53a
--- /dev/null
+++ b/third_party/blink/web_tests/inspector-protocol/css/css-style-sheet-events.js
@@ -0,0 +1,40 @@
+(async function(testRunner) {
+  var {page, session, dp} = await testRunner.startHTML(`
+      <style>
+      #test {
+          box-sizing: border-box;
+      }
+      </style>
+      <article id='test'></article>`,
+      'The test verifies events fire for adding, changing, and removing CSSStyleSheets.');
+
+  const CSSHelper = await testRunner.loadScript('../resources/css-helper.js');
+  const cssHelper = new CSSHelper(testRunner, dp);
+  await cssHelper.requestDocumentNodeId();
+
+  // Add Event
+  dp.CSS.enable();
+  const addEvent = await dp.CSS.onceStyleSheetAdded();
+  testRunner.log(addEvent, '', [ ...TestRunner.stabilizeNames, 'length' ]);
+  const styleSheetId = addEvent.params.header.styleSheetId;
+
+  // Change Event
+  async function addRuleAndUndo(options) {
+    options.styleSheetId = styleSheetId;
+    await dp.CSS.addRule(options);
+    await dp.DOM.undo();
+  }
+  addRuleAndUndo({
+    location: { startLine: 0, startColumn: 0, endLine: 0, endColumn: 0 },
+    ruleText: `#test { content: 'EDITED'; }`,
+  });
+  const changedEvent = await dp.CSS.onceStyleSheetChanged();
+  testRunner.log(changedEvent);
+
+  // Remove event
+  await dp.Page.navigate({url: 'about:blank'});
+  const removeEvent = await dp.CSS.onceStyleSheetRemoved();
+  testRunner.log(removeEvent);
+  testRunner.completeTest();
+});
+
diff --git a/third_party/blink/web_tests/inspector-protocol/resources/css-helper.js b/third_party/blink/web_tests/inspector-protocol/resources/css-helper.js
index 2401619d..b7831bd 100644
--- a/third_party/blink/web_tests/inspector-protocol/resources/css-helper.js
+++ b/third_party/blink/web_tests/inspector-protocol/resources/css-helper.js
@@ -112,7 +112,7 @@
     if (!omitLog)
       this._testRunner.log('Dumping inherited rules: ');
     for (var inheritedEntry of result.inherited) {
-      this.dumpStyle(inheritedEntry.inlineStyle);
+      this.dumpStyle(inheritedEntry.inlineStyle, /*indent=*/0);
       dumpRuleMatches.call(this, inheritedEntry.matchedCSSRules);
     }
 
diff --git a/third_party/blink/web_tests/media/sources-fallback-codecs.html b/third_party/blink/web_tests/media/sources-fallback-codecs.html
index 6ffa741..6df41aea 100644
--- a/third_party/blink/web_tests/media/sources-fallback-codecs.html
+++ b/third_party/blink/web_tests/media/sources-fallback-codecs.html
@@ -26,7 +26,9 @@
     { element: "audio", typeArray: ["audio/wav", "audio/ogg"], srcArray: ["content/test.wav", "content/test.oga"], description: "Test audio with ['audio/wav', 'audio/ogg']" },
     { element: "audio", typeArray: ["audio/ogg", "audio/wav"], srcArray: ["content/test.oga", "content/test.wav"], description: "Test audio with ['audio/ogg', 'audio/wav']" },
     { element: "video", srcArray: [mp4DataSrc, ogvDataSrc], description: "Test video with ['data:video/mp4;base64', 'data:video/ogg;base64']" },
-    { element: "video", srcArray: [ogvDataSrc, mp4DataSrc], description: "Test video with ['data:video/ogg;base64', 'data:video/mp4;base64']" }
+    { element: "video", srcArray: [ogvDataSrc, mp4DataSrc], description: "Test video with ['data:video/ogg;base64', 'data:video/mp4;base64']" },
+    { element: "audio", typeArray: ["audio/mp4", "audio/ogg"], srcArray: ["content/test.m4a", "content/test.oga"], description: "Test audio with ['audio/mp4', \"audio/ogg\"]" },
+    { element: "audio", typeArray: ["audio/ogg", "audio/mp4"], srcArray: ["content/test.oga", "content/test.m4a"], description: "Test audio with ['audio/ogg', \"audio/mp4\"]" },
 ];
 
 testInfo.forEach(function(test) {
diff --git a/third_party/blink/web_tests/platform/linux/fast/overflow/line-clamp-expected.png b/third_party/blink/web_tests/platform/linux/fast/overflow/line-clamp-expected.png
index 202a59b2..e524d9a 100644
--- a/third_party/blink/web_tests/platform/linux/fast/overflow/line-clamp-expected.png
+++ b/third_party/blink/web_tests/platform/linux/fast/overflow/line-clamp-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/paint/invalidation/text-line-clamp-truncation-expected.txt b/third_party/blink/web_tests/platform/linux/paint/invalidation/text-line-clamp-truncation-expected.txt
index 8130226e..b1e6d643 100644
--- a/third_party/blink/web_tests/platform/linux/paint/invalidation/text-line-clamp-truncation-expected.txt
+++ b/third_party/blink/web_tests/platform/linux/paint/invalidation/text-line-clamp-truncation-expected.txt
@@ -19,7 +19,7 @@
       "paintInvalidations": [
         {
           "object": "EllipsisBox",
-          "rect": [8, 8, 285, 21],
+          "rect": [8, 8, 285, 24],
           "reason": "disappeared"
         },
         {
@@ -44,12 +44,12 @@
         },
         {
           "object": "LayoutDeprecatedFlexibleBox DIV id='container'",
-          "rect": [8, 29, 284, 24],
+          "rect": [8, 32, 284, 21],
           "reason": "incremental"
         },
         {
           "object": "LayoutDeprecatedFlexibleBox DIV id='container'",
-          "rect": [292, 10, 1, 19],
+          "rect": [292, 10, 1, 22],
           "reason": "incremental"
         }
       ]
diff --git a/third_party/blink/web_tests/platform/mac-mac10.10/fast/text/orientation-sideways-expected.png b/third_party/blink/web_tests/platform/mac-mac10.10/fast/text/orientation-sideways-expected.png
index 6b9949d..48e7515e 100644
--- a/third_party/blink/web_tests/platform/mac-mac10.10/fast/text/orientation-sideways-expected.png
+++ b/third_party/blink/web_tests/platform/mac-mac10.10/fast/text/orientation-sideways-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.10/fast/text/selection/emphasis-expected.png b/third_party/blink/web_tests/platform/mac-mac10.10/fast/text/selection/emphasis-expected.png
index ccc4922..7712571 100644
--- a/third_party/blink/web_tests/platform/mac-mac10.10/fast/text/selection/emphasis-expected.png
+++ b/third_party/blink/web_tests/platform/mac-mac10.10/fast/text/selection/emphasis-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.10/fast/text/stroking-decorations-expected.png b/third_party/blink/web_tests/platform/mac-mac10.10/fast/text/stroking-decorations-expected.png
index 9dd6f796..f599cf9d 100644
--- a/third_party/blink/web_tests/platform/mac-mac10.10/fast/text/stroking-decorations-expected.png
+++ b/third_party/blink/web_tests/platform/mac-mac10.10/fast/text/stroking-decorations-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.10/fast/text/stroking-expected.png b/third_party/blink/web_tests/platform/mac-mac10.10/fast/text/stroking-expected.png
index f17c948..d7b9015 100644
--- a/third_party/blink/web_tests/platform/mac-mac10.10/fast/text/stroking-expected.png
+++ b/third_party/blink/web_tests/platform/mac-mac10.10/fast/text/stroking-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.10/fast/text/whitespace/002-expected.png b/third_party/blink/web_tests/platform/mac-mac10.10/fast/text/whitespace/002-expected.png
index c1a8b5cd..5e76a67 100644
--- a/third_party/blink/web_tests/platform/mac-mac10.10/fast/text/whitespace/002-expected.png
+++ b/third_party/blink/web_tests/platform/mac-mac10.10/fast/text/whitespace/002-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.10/fast/text/whitespace/003-expected.png b/third_party/blink/web_tests/platform/mac-mac10.10/fast/text/whitespace/003-expected.png
index c1a8b5cd..5e76a67 100644
--- a/third_party/blink/web_tests/platform/mac-mac10.10/fast/text/whitespace/003-expected.png
+++ b/third_party/blink/web_tests/platform/mac-mac10.10/fast/text/whitespace/003-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.10/fast/text/whitespace/004-expected.png b/third_party/blink/web_tests/platform/mac-mac10.10/fast/text/whitespace/004-expected.png
index 92f40d0e..59747e8 100644
--- a/third_party/blink/web_tests/platform/mac-mac10.10/fast/text/whitespace/004-expected.png
+++ b/third_party/blink/web_tests/platform/mac-mac10.10/fast/text/whitespace/004-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.10/fast/text/whitespace/005-expected.png b/third_party/blink/web_tests/platform/mac-mac10.10/fast/text/whitespace/005-expected.png
index 03c28b8..94776f2 100644
--- a/third_party/blink/web_tests/platform/mac-mac10.10/fast/text/whitespace/005-expected.png
+++ b/third_party/blink/web_tests/platform/mac-mac10.10/fast/text/whitespace/005-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.10/fast/text/whitespace/007-expected.png b/third_party/blink/web_tests/platform/mac-mac10.10/fast/text/whitespace/007-expected.png
index caf3e79..46baecb 100644
--- a/third_party/blink/web_tests/platform/mac-mac10.10/fast/text/whitespace/007-expected.png
+++ b/third_party/blink/web_tests/platform/mac-mac10.10/fast/text/whitespace/007-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.10/fast/text/whitespace/008-expected.png b/third_party/blink/web_tests/platform/mac-mac10.10/fast/text/whitespace/008-expected.png
index 4f12fe8..045a541f 100644
--- a/third_party/blink/web_tests/platform/mac-mac10.10/fast/text/whitespace/008-expected.png
+++ b/third_party/blink/web_tests/platform/mac-mac10.10/fast/text/whitespace/008-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.10/fast/text/whitespace/009-expected.png b/third_party/blink/web_tests/platform/mac-mac10.10/fast/text/whitespace/009-expected.png
index be08174..1b2d226 100644
--- a/third_party/blink/web_tests/platform/mac-mac10.10/fast/text/whitespace/009-expected.png
+++ b/third_party/blink/web_tests/platform/mac-mac10.10/fast/text/whitespace/009-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.10/fast/text/whitespace/010-expected.png b/third_party/blink/web_tests/platform/mac-mac10.10/fast/text/whitespace/010-expected.png
index 92f40d0e..59747e8 100644
--- a/third_party/blink/web_tests/platform/mac-mac10.10/fast/text/whitespace/010-expected.png
+++ b/third_party/blink/web_tests/platform/mac-mac10.10/fast/text/whitespace/010-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.10/fast/text/whitespace/011-expected.png b/third_party/blink/web_tests/platform/mac-mac10.10/fast/text/whitespace/011-expected.png
index 03c28b8..94776f2 100644
--- a/third_party/blink/web_tests/platform/mac-mac10.10/fast/text/whitespace/011-expected.png
+++ b/third_party/blink/web_tests/platform/mac-mac10.10/fast/text/whitespace/011-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.10/fast/text/whitespace/012-expected.png b/third_party/blink/web_tests/platform/mac-mac10.10/fast/text/whitespace/012-expected.png
index 38eda2f..25a28df7 100644
--- a/third_party/blink/web_tests/platform/mac-mac10.10/fast/text/whitespace/012-expected.png
+++ b/third_party/blink/web_tests/platform/mac-mac10.10/fast/text/whitespace/012-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.10/fast/text/whitespace/015-expected.png b/third_party/blink/web_tests/platform/mac-mac10.10/fast/text/whitespace/015-expected.png
index 92f40d0e..59747e8 100644
--- a/third_party/blink/web_tests/platform/mac-mac10.10/fast/text/whitespace/015-expected.png
+++ b/third_party/blink/web_tests/platform/mac-mac10.10/fast/text/whitespace/015-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.10/fast/text/whitespace/016-expected.png b/third_party/blink/web_tests/platform/mac-mac10.10/fast/text/whitespace/016-expected.png
index 03c28b8..94776f2 100644
--- a/third_party/blink/web_tests/platform/mac-mac10.10/fast/text/whitespace/016-expected.png
+++ b/third_party/blink/web_tests/platform/mac-mac10.10/fast/text/whitespace/016-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.10/fast/text/whitespace/018-expected.png b/third_party/blink/web_tests/platform/mac-mac10.10/fast/text/whitespace/018-expected.png
index b05ffab..94572ad 100644
--- a/third_party/blink/web_tests/platform/mac-mac10.10/fast/text/whitespace/018-expected.png
+++ b/third_party/blink/web_tests/platform/mac-mac10.10/fast/text/whitespace/018-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.10/fast/text/whitespace/020-expected.png b/third_party/blink/web_tests/platform/mac-mac10.10/fast/text/whitespace/020-expected.png
index c5b771a..7a681430 100644
--- a/third_party/blink/web_tests/platform/mac-mac10.10/fast/text/whitespace/020-expected.png
+++ b/third_party/blink/web_tests/platform/mac-mac10.10/fast/text/whitespace/020-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.12/fast/text/selection/emphasis-expected.png b/third_party/blink/web_tests/platform/mac-mac10.12/fast/text/selection/emphasis-expected.png
index 4a1d24d..bb49b17 100644
--- a/third_party/blink/web_tests/platform/mac-mac10.12/fast/text/selection/emphasis-expected.png
+++ b/third_party/blink/web_tests/platform/mac-mac10.12/fast/text/selection/emphasis-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.12/fast/text/stroking-decorations-expected.png b/third_party/blink/web_tests/platform/mac-mac10.12/fast/text/stroking-decorations-expected.png
index 8c318c2..a3d68983 100644
--- a/third_party/blink/web_tests/platform/mac-mac10.12/fast/text/stroking-decorations-expected.png
+++ b/third_party/blink/web_tests/platform/mac-mac10.12/fast/text/stroking-decorations-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.12/fast/text/stroking-expected.png b/third_party/blink/web_tests/platform/mac-mac10.12/fast/text/stroking-expected.png
index 32426ae..5b2e3e7 100644
--- a/third_party/blink/web_tests/platform/mac-mac10.12/fast/text/stroking-expected.png
+++ b/third_party/blink/web_tests/platform/mac-mac10.12/fast/text/stroking-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.12/fast/text/whitespace/002-expected.png b/third_party/blink/web_tests/platform/mac-mac10.12/fast/text/whitespace/002-expected.png
index 8c71802..47f337a2 100644
--- a/third_party/blink/web_tests/platform/mac-mac10.12/fast/text/whitespace/002-expected.png
+++ b/third_party/blink/web_tests/platform/mac-mac10.12/fast/text/whitespace/002-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.12/fast/text/whitespace/003-expected.png b/third_party/blink/web_tests/platform/mac-mac10.12/fast/text/whitespace/003-expected.png
index 8c71802..47f337a2 100644
--- a/third_party/blink/web_tests/platform/mac-mac10.12/fast/text/whitespace/003-expected.png
+++ b/third_party/blink/web_tests/platform/mac-mac10.12/fast/text/whitespace/003-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.12/fast/text/whitespace/004-expected.png b/third_party/blink/web_tests/platform/mac-mac10.12/fast/text/whitespace/004-expected.png
index d609949..101dc38 100644
--- a/third_party/blink/web_tests/platform/mac-mac10.12/fast/text/whitespace/004-expected.png
+++ b/third_party/blink/web_tests/platform/mac-mac10.12/fast/text/whitespace/004-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.12/fast/text/whitespace/005-expected.png b/third_party/blink/web_tests/platform/mac-mac10.12/fast/text/whitespace/005-expected.png
index f190461..9261e79 100644
--- a/third_party/blink/web_tests/platform/mac-mac10.12/fast/text/whitespace/005-expected.png
+++ b/third_party/blink/web_tests/platform/mac-mac10.12/fast/text/whitespace/005-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.12/fast/text/whitespace/007-expected.png b/third_party/blink/web_tests/platform/mac-mac10.12/fast/text/whitespace/007-expected.png
index 836c6dd..a0f9cc2 100644
--- a/third_party/blink/web_tests/platform/mac-mac10.12/fast/text/whitespace/007-expected.png
+++ b/third_party/blink/web_tests/platform/mac-mac10.12/fast/text/whitespace/007-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.12/fast/text/whitespace/008-expected.png b/third_party/blink/web_tests/platform/mac-mac10.12/fast/text/whitespace/008-expected.png
index faad0bc4..29603de 100644
--- a/third_party/blink/web_tests/platform/mac-mac10.12/fast/text/whitespace/008-expected.png
+++ b/third_party/blink/web_tests/platform/mac-mac10.12/fast/text/whitespace/008-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.12/fast/text/whitespace/009-expected.png b/third_party/blink/web_tests/platform/mac-mac10.12/fast/text/whitespace/009-expected.png
index 1516900f..da75077 100644
--- a/third_party/blink/web_tests/platform/mac-mac10.12/fast/text/whitespace/009-expected.png
+++ b/third_party/blink/web_tests/platform/mac-mac10.12/fast/text/whitespace/009-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.12/fast/text/whitespace/010-expected.png b/third_party/blink/web_tests/platform/mac-mac10.12/fast/text/whitespace/010-expected.png
index d609949..101dc38 100644
--- a/third_party/blink/web_tests/platform/mac-mac10.12/fast/text/whitespace/010-expected.png
+++ b/third_party/blink/web_tests/platform/mac-mac10.12/fast/text/whitespace/010-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.12/fast/text/whitespace/011-expected.png b/third_party/blink/web_tests/platform/mac-mac10.12/fast/text/whitespace/011-expected.png
index f190461..9261e79 100644
--- a/third_party/blink/web_tests/platform/mac-mac10.12/fast/text/whitespace/011-expected.png
+++ b/third_party/blink/web_tests/platform/mac-mac10.12/fast/text/whitespace/011-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.12/fast/text/whitespace/012-expected.png b/third_party/blink/web_tests/platform/mac-mac10.12/fast/text/whitespace/012-expected.png
index 09a79e1..06007855 100644
--- a/third_party/blink/web_tests/platform/mac-mac10.12/fast/text/whitespace/012-expected.png
+++ b/third_party/blink/web_tests/platform/mac-mac10.12/fast/text/whitespace/012-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.12/fast/text/whitespace/015-expected.png b/third_party/blink/web_tests/platform/mac-mac10.12/fast/text/whitespace/015-expected.png
index d609949..101dc38 100644
--- a/third_party/blink/web_tests/platform/mac-mac10.12/fast/text/whitespace/015-expected.png
+++ b/third_party/blink/web_tests/platform/mac-mac10.12/fast/text/whitespace/015-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.12/fast/text/whitespace/016-expected.png b/third_party/blink/web_tests/platform/mac-mac10.12/fast/text/whitespace/016-expected.png
index f190461..9261e79 100644
--- a/third_party/blink/web_tests/platform/mac-mac10.12/fast/text/whitespace/016-expected.png
+++ b/third_party/blink/web_tests/platform/mac-mac10.12/fast/text/whitespace/016-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.12/fast/text/whitespace/018-expected.png b/third_party/blink/web_tests/platform/mac-mac10.12/fast/text/whitespace/018-expected.png
index 5ab58bd..3a745f9 100644
--- a/third_party/blink/web_tests/platform/mac-mac10.12/fast/text/whitespace/018-expected.png
+++ b/third_party/blink/web_tests/platform/mac-mac10.12/fast/text/whitespace/018-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.12/fast/text/whitespace/020-expected.png b/third_party/blink/web_tests/platform/mac-mac10.12/fast/text/whitespace/020-expected.png
index ad781b3..aed525f 100644
--- a/third_party/blink/web_tests/platform/mac-mac10.12/fast/text/whitespace/020-expected.png
+++ b/third_party/blink/web_tests/platform/mac-mac10.12/fast/text/whitespace/020-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-retina/fast/text/selection/emphasis-expected.png b/third_party/blink/web_tests/platform/mac-retina/fast/text/selection/emphasis-expected.png
deleted file mode 100644
index 6bcc50c..0000000
--- a/third_party/blink/web_tests/platform/mac-retina/fast/text/selection/emphasis-expected.png
+++ /dev/null
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/fast/text/apply-start-width-after-skipped-text-expected.png b/third_party/blink/web_tests/platform/mac/fast/text/apply-start-width-after-skipped-text-expected.png
index f13913a..90f8bbd 100644
--- a/third_party/blink/web_tests/platform/mac/fast/text/apply-start-width-after-skipped-text-expected.png
+++ b/third_party/blink/web_tests/platform/mac/fast/text/apply-start-width-after-skipped-text-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/fast/text/orientation-sideways-expected.png b/third_party/blink/web_tests/platform/mac/fast/text/orientation-sideways-expected.png
index db38145b..1a6ee78 100644
--- a/third_party/blink/web_tests/platform/mac/fast/text/orientation-sideways-expected.png
+++ b/third_party/blink/web_tests/platform/mac/fast/text/orientation-sideways-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/fast/text/selection/emphasis-expected.png b/third_party/blink/web_tests/platform/mac/fast/text/selection/emphasis-expected.png
index 6bcc50c..7223cd0 100644
--- a/third_party/blink/web_tests/platform/mac/fast/text/selection/emphasis-expected.png
+++ b/third_party/blink/web_tests/platform/mac/fast/text/selection/emphasis-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/fast/text/soft-hyphen-3-expected.png b/third_party/blink/web_tests/platform/mac/fast/text/soft-hyphen-3-expected.png
index ce475ed..f6e8fa91 100644
--- a/third_party/blink/web_tests/platform/mac/fast/text/soft-hyphen-3-expected.png
+++ b/third_party/blink/web_tests/platform/mac/fast/text/soft-hyphen-3-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/fast/text/stroking-decorations-expected.png b/third_party/blink/web_tests/platform/mac/fast/text/stroking-decorations-expected.png
index c0c5047..7473bf0 100644
--- a/third_party/blink/web_tests/platform/mac/fast/text/stroking-decorations-expected.png
+++ b/third_party/blink/web_tests/platform/mac/fast/text/stroking-decorations-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/fast/text/stroking-expected.png b/third_party/blink/web_tests/platform/mac/fast/text/stroking-expected.png
index c7fdf8d..10e5c3ea 100644
--- a/third_party/blink/web_tests/platform/mac/fast/text/stroking-expected.png
+++ b/third_party/blink/web_tests/platform/mac/fast/text/stroking-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/fast/text/whitespace/002-expected.png b/third_party/blink/web_tests/platform/mac/fast/text/whitespace/002-expected.png
index 7d68c5c..bdcc203d 100644
--- a/third_party/blink/web_tests/platform/mac/fast/text/whitespace/002-expected.png
+++ b/third_party/blink/web_tests/platform/mac/fast/text/whitespace/002-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/fast/text/whitespace/003-expected.png b/third_party/blink/web_tests/platform/mac/fast/text/whitespace/003-expected.png
index 7d68c5c..bdcc203d 100644
--- a/third_party/blink/web_tests/platform/mac/fast/text/whitespace/003-expected.png
+++ b/third_party/blink/web_tests/platform/mac/fast/text/whitespace/003-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/fast/text/whitespace/004-expected.png b/third_party/blink/web_tests/platform/mac/fast/text/whitespace/004-expected.png
index c7830a9b..69efcef 100644
--- a/third_party/blink/web_tests/platform/mac/fast/text/whitespace/004-expected.png
+++ b/third_party/blink/web_tests/platform/mac/fast/text/whitespace/004-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/fast/text/whitespace/005-expected.png b/third_party/blink/web_tests/platform/mac/fast/text/whitespace/005-expected.png
index 8c0c8bd..b33e113 100644
--- a/third_party/blink/web_tests/platform/mac/fast/text/whitespace/005-expected.png
+++ b/third_party/blink/web_tests/platform/mac/fast/text/whitespace/005-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/fast/text/whitespace/007-expected.png b/third_party/blink/web_tests/platform/mac/fast/text/whitespace/007-expected.png
index c8ff6a0..10d24ca 100644
--- a/third_party/blink/web_tests/platform/mac/fast/text/whitespace/007-expected.png
+++ b/third_party/blink/web_tests/platform/mac/fast/text/whitespace/007-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/fast/text/whitespace/008-expected.png b/third_party/blink/web_tests/platform/mac/fast/text/whitespace/008-expected.png
index d811fae..699c036 100644
--- a/third_party/blink/web_tests/platform/mac/fast/text/whitespace/008-expected.png
+++ b/third_party/blink/web_tests/platform/mac/fast/text/whitespace/008-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/fast/text/whitespace/009-expected.png b/third_party/blink/web_tests/platform/mac/fast/text/whitespace/009-expected.png
index 475e398..c01bf061 100644
--- a/third_party/blink/web_tests/platform/mac/fast/text/whitespace/009-expected.png
+++ b/third_party/blink/web_tests/platform/mac/fast/text/whitespace/009-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/fast/text/whitespace/010-expected.png b/third_party/blink/web_tests/platform/mac/fast/text/whitespace/010-expected.png
index c7830a9b..69efcef 100644
--- a/third_party/blink/web_tests/platform/mac/fast/text/whitespace/010-expected.png
+++ b/third_party/blink/web_tests/platform/mac/fast/text/whitespace/010-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/fast/text/whitespace/011-expected.png b/third_party/blink/web_tests/platform/mac/fast/text/whitespace/011-expected.png
index 8c0c8bd..b33e113 100644
--- a/third_party/blink/web_tests/platform/mac/fast/text/whitespace/011-expected.png
+++ b/third_party/blink/web_tests/platform/mac/fast/text/whitespace/011-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/fast/text/whitespace/012-expected.png b/third_party/blink/web_tests/platform/mac/fast/text/whitespace/012-expected.png
index c55097c..fd5087e 100644
--- a/third_party/blink/web_tests/platform/mac/fast/text/whitespace/012-expected.png
+++ b/third_party/blink/web_tests/platform/mac/fast/text/whitespace/012-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/fast/text/whitespace/013-expected.png b/third_party/blink/web_tests/platform/mac/fast/text/whitespace/013-expected.png
index 2e67ea3..f7ce758 100644
--- a/third_party/blink/web_tests/platform/mac/fast/text/whitespace/013-expected.png
+++ b/third_party/blink/web_tests/platform/mac/fast/text/whitespace/013-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/fast/text/whitespace/014-expected.png b/third_party/blink/web_tests/platform/mac/fast/text/whitespace/014-expected.png
index 5b9f2e86..700962a 100644
--- a/third_party/blink/web_tests/platform/mac/fast/text/whitespace/014-expected.png
+++ b/third_party/blink/web_tests/platform/mac/fast/text/whitespace/014-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/fast/text/whitespace/015-expected.png b/third_party/blink/web_tests/platform/mac/fast/text/whitespace/015-expected.png
index c7830a9b..69efcef 100644
--- a/third_party/blink/web_tests/platform/mac/fast/text/whitespace/015-expected.png
+++ b/third_party/blink/web_tests/platform/mac/fast/text/whitespace/015-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/fast/text/whitespace/016-expected.png b/third_party/blink/web_tests/platform/mac/fast/text/whitespace/016-expected.png
index 8c0c8bd..b33e113 100644
--- a/third_party/blink/web_tests/platform/mac/fast/text/whitespace/016-expected.png
+++ b/third_party/blink/web_tests/platform/mac/fast/text/whitespace/016-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/fast/text/whitespace/017-expected.png b/third_party/blink/web_tests/platform/mac/fast/text/whitespace/017-expected.png
index 9aa4a34..9e3fd62 100644
--- a/third_party/blink/web_tests/platform/mac/fast/text/whitespace/017-expected.png
+++ b/third_party/blink/web_tests/platform/mac/fast/text/whitespace/017-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/fast/text/whitespace/018-expected.png b/third_party/blink/web_tests/platform/mac/fast/text/whitespace/018-expected.png
index 418cd2a..1e3b67142 100644
--- a/third_party/blink/web_tests/platform/mac/fast/text/whitespace/018-expected.png
+++ b/third_party/blink/web_tests/platform/mac/fast/text/whitespace/018-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/fast/text/whitespace/020-expected.png b/third_party/blink/web_tests/platform/mac/fast/text/whitespace/020-expected.png
index e78a5623..db31d2f7 100644
--- a/third_party/blink/web_tests/platform/mac/fast/text/whitespace/020-expected.png
+++ b/third_party/blink/web_tests/platform/mac/fast/text/whitespace/020-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/paint/invalidation/text-line-clamp-truncation-expected.txt b/third_party/blink/web_tests/platform/mac/paint/invalidation/text-line-clamp-truncation-expected.txt
index bf6c51a..5c9c337 100644
--- a/third_party/blink/web_tests/platform/mac/paint/invalidation/text-line-clamp-truncation-expected.txt
+++ b/third_party/blink/web_tests/platform/mac/paint/invalidation/text-line-clamp-truncation-expected.txt
@@ -33,14 +33,14 @@
           "reason": "disappeared"
         },
         {
-          "object": "LayoutDeprecatedFlexibleBox DIV id='container'",
-          "rect": [8, 29, 285, 24],
-          "reason": "incremental"
+          "object": "EllipsisBox",
+          "rect": [8, 8, 285, 24],
+          "reason": "disappeared"
         },
         {
-          "object": "EllipsisBox",
-          "rect": [8, 8, 285, 21],
-          "reason": "disappeared"
+          "object": "LayoutDeprecatedFlexibleBox DIV id='container'",
+          "rect": [8, 32, 285, 21],
+          "reason": "incremental"
         },
         {
           "object": "LayoutDeprecatedFlexibleBox DIV id='container'",
diff --git a/third_party/blink/web_tests/platform/win/fast/overflow/line-clamp-expected.png b/third_party/blink/web_tests/platform/win/fast/overflow/line-clamp-expected.png
index 61919889..de78765 100644
--- a/third_party/blink/web_tests/platform/win/fast/overflow/line-clamp-expected.png
+++ b/third_party/blink/web_tests/platform/win/fast/overflow/line-clamp-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/paint/invalidation/text-line-clamp-truncation-expected.txt b/third_party/blink/web_tests/platform/win/paint/invalidation/text-line-clamp-truncation-expected.txt
index 5864bfa..f0b6ffc 100644
--- a/third_party/blink/web_tests/platform/win/paint/invalidation/text-line-clamp-truncation-expected.txt
+++ b/third_party/blink/web_tests/platform/win/paint/invalidation/text-line-clamp-truncation-expected.txt
@@ -19,7 +19,7 @@
       "paintInvalidations": [
         {
           "object": "LayoutDeprecatedFlexibleBox DIV id='container' class='folded'",
-          "rect": [8, 8, 285, 21],
+          "rect": [8, 8, 285, 24],
           "reason": "chunk disappeared"
         },
         {
diff --git a/third_party/blink/web_tests/virtual/exotic-color-space/images/cross-fade-overflow-position-expected.png b/third_party/blink/web_tests/virtual/exotic-color-space/images/cross-fade-overflow-position-expected.png
index 56e2aeeb..0e6cc0a 100644
--- a/third_party/blink/web_tests/virtual/exotic-color-space/images/cross-fade-overflow-position-expected.png
+++ b/third_party/blink/web_tests/virtual/exotic-color-space/images/cross-fade-overflow-position-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/virtual/streams-native/http/tests/streams/transferable/readable-stream-expected.txt b/third_party/blink/web_tests/virtual/streams-native/http/tests/streams/transferable/readable-stream-expected.txt
new file mode 100644
index 0000000..dd671bf
--- /dev/null
+++ b/third_party/blink/web_tests/virtual/streams-native/http/tests/streams/transferable/readable-stream-expected.txt
@@ -0,0 +1,19 @@
+This is a testharness.js-based test.
+FAIL sending one chunk through a transferred stream should work promise_test: Unhandled rejection with value: object "Error: what is this thing: "null"?"
+FAIL sending ten chunks through a transferred stream should work promise_test: Unhandled rejection with value: object "Error: what is this thing: "null"?"
+FAIL sending ten chunks one at a time should work promise_test: Unhandled rejection with value: object "Error: what is this thing: "null"?"
+FAIL sending ten chunks on demand should work promise_test: Unhandled rejection with value: object "Error: what is this thing: "null"?"
+PASS transferring a stream should relieve backpressure
+FAIL transferring a stream should add one chunk to the queue size promise_test: Unhandled rejection with value: object "Error: what is this thing: "null"?"
+FAIL the extra queue from transferring is counted in chunks promise_test: Unhandled rejection with value: object "Error: what is this thing: "null"?"
+FAIL cancel should be propagated to the original promise_test: Unhandled rejection with value: object "Error: what is this thing: "null"?"
+FAIL cancel should abort a pending read() promise_test: Unhandled rejection with value: object "Error: what is this thing: "null"?"
+FAIL stream cancel should not wait for underlying source cancel promise_test: Unhandled rejection with value: object "Error: what is this thing: "null"?"
+FAIL serialization should not happen until the value is read promise_test: Unhandled rejection with value: object "Error: what is this thing: "null"?"
+FAIL transferring a non-serializable chunk should error both sides promise_test: Unhandled rejection with value: object "Error: what is this thing: "null"?"
+FAIL errors should be passed through promise_test: Unhandled rejection with value: object "Error: what is this thing: "null"?"
+FAIL race between cancel() and error() should leave sides in different states promise_test: Unhandled rejection with value: object "Error: what is this thing: "null"?"
+FAIL race between cancel() and close() should be benign promise_test: Unhandled rejection with value: object "Error: what is this thing: "null"?"
+FAIL race between cancel() and enqueue() should be benign promise_test: Unhandled rejection with value: object "Error: what is this thing: "null"?"
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/virtual/streams-native/http/tests/streams/transferable/reason-expected.txt b/third_party/blink/web_tests/virtual/streams-native/http/tests/streams/transferable/reason-expected.txt
new file mode 100644
index 0000000..e381006
--- /dev/null
+++ b/third_party/blink/web_tests/virtual/streams-native/http/tests/streams/transferable/reason-expected.txt
@@ -0,0 +1,25 @@
+This is a testharness.js-based test.
+FAIL reason with a simple value of 'hi' should be preserved promise_test: Unhandled rejection with value: object "Error: what is this thing: "null"?"
+FAIL reason with a simple value of '	\r
+' should be preserved promise_test: Unhandled rejection with value: object "Error: what is this thing: "null"?"
+FAIL reason with a simple value of '7' should be preserved promise_test: Unhandled rejection with value: object "Error: what is this thing: "null"?"
+FAIL reason with a simple value of '3' should be preserved promise_test: Unhandled rejection with value: object "Error: what is this thing: "null"?"
+FAIL reason with a simple value of 'undefined' should be preserved promise_test: Unhandled rejection with value: object "Error: what is this thing: "null"?"
+FAIL reason with a simple value of 'null' should be preserved promise_test: Unhandled rejection with value: object "Error: what is this thing: "null"?"
+FAIL reason with a simple value of 'true' should be preserved promise_test: Unhandled rejection with value: object "Error: what is this thing: "null"?"
+FAIL reason with a simple value of 'false' should be preserved promise_test: Unhandled rejection with value: object "Error: what is this thing: "null"?"
+FAIL reason with a type of 'symbol' should be squished to undefined promise_test: Unhandled rejection with value: object "Error: what is this thing: "null"?"
+FAIL reason with a type of 'function' should be squished to undefined promise_test: Unhandled rejection with value: object "Error: what is this thing: "null"?"
+FAIL number with a value of 'NaN' should be squished to null promise_test: Unhandled rejection with value: object "Error: what is this thing: "null"?"
+FAIL number with a value of 'Infinity' should be squished to null promise_test: Unhandled rejection with value: object "Error: what is this thing: "null"?"
+FAIL objects that can be completely expressed in JSON should be preserved promise_test: Unhandled rejection with value: object "Error: what is this thing: "null"?"
+FAIL objects that cannot be expressed in JSON should result in a TypeError promise_test: Unhandled rejection with value: object "Error: what is this thing: "null"?"
+FAIL the type and message of a TypeError should be preserved promise_test: Unhandled rejection with value: object "Error: what is this thing: "null"?"
+FAIL other attributes of a TypeError should not be preserved promise_test: Unhandled rejection with value: object "Error: what is this thing: "null"?"
+FAIL a TypeError message should not be preserved if it is not a string promise_test: Unhandled rejection with value: object "Error: what is this thing: "null"?"
+FAIL a TypeError message should not be preserved if it is a getter promise_test: Unhandled rejection with value: object "Error: what is this thing: "null"?"
+FAIL a TypeError message should not be preserved if it is inherited promise_test: Unhandled rejection with value: object "Error: what is this thing: "null"?"
+FAIL DOMException errors should be preserved promise_test: Unhandled rejection with value: object "Error: what is this thing: "null"?"
+FAIL RangeErrors should not be preserved promise_test: Unhandled rejection with value: object "Error: what is this thing: "null"?"
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/virtual/streams-native/http/tests/streams/transferable/service-worker-expected.txt b/third_party/blink/web_tests/virtual/streams-native/http/tests/streams/transferable/service-worker-expected.txt
new file mode 100644
index 0000000..68a7e42c
--- /dev/null
+++ b/third_party/blink/web_tests/virtual/streams-native/http/tests/streams/transferable/service-worker-expected.txt
@@ -0,0 +1,6 @@
+This is a testharness.js-based test.
+PASS service-worker
+FAIL serviceWorker.controller.postMessage should be able to transfer a ReadableStream promise_test: Unhandled rejection with value: "BAD: TypeError: Cannot read property 'constructor' of null"
+FAIL postMessage in a service worker should be able to transfer ReadableStream promise_test: Unhandled rejection with value: object "TypeError: Cannot read property 'constructor' of null"
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/virtual/streams-native/http/tests/streams/transferable/shared-worker-expected.txt b/third_party/blink/web_tests/virtual/streams-native/http/tests/streams/transferable/shared-worker-expected.txt
new file mode 100644
index 0000000..20648c3
--- /dev/null
+++ b/third_party/blink/web_tests/virtual/streams-native/http/tests/streams/transferable/shared-worker-expected.txt
@@ -0,0 +1,5 @@
+This is a testharness.js-based test.
+FAIL worker.postMessage should be able to transfer a ReadableStream promise_test: Unhandled rejection with value: "BAD: TypeError: Cannot read property 'constructor' of null"
+FAIL postMessage in a worker should be able to transfer a ReadableStream promise_test: Unhandled rejection with value: object "TypeError: Cannot read property 'constructor' of null"
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/virtual/streams-native/http/tests/streams/transferable/transform-stream-expected.txt b/third_party/blink/web_tests/virtual/streams-native/http/tests/streams/transferable/transform-stream-expected.txt
index 4e10590..d3bc518 100644
--- a/third_party/blink/web_tests/virtual/streams-native/http/tests/streams/transferable/transform-stream-expected.txt
+++ b/third_party/blink/web_tests/virtual/streams-native/http/tests/streams/transferable/transform-stream-expected.txt
@@ -1,8 +1,8 @@
 This is a testharness.js-based test.
-PASS window.postMessage should be able to transfer a TransformStream
+FAIL window.postMessage should be able to transfer a TransformStream Cannot read property 'constructor' of null
 PASS a TransformStream with a locked writable should not be transferable
 PASS a TransformStream with a locked readable should not be transferable
 PASS a TransformStream with both sides locked should not be transferable
-FAIL piping through transferred transforms should work Failed to execute 'pipeThrough' on 'ReadableStream': pipeThrough disabled because StreamsNative feature is enabled
+FAIL piping through transferred transforms should work Cannot read property 'source' of null
 Harness: the test ran to completion.
 
diff --git a/third_party/blink/web_tests/virtual/streams-native/http/tests/streams/transferable/worker-expected.txt b/third_party/blink/web_tests/virtual/streams-native/http/tests/streams/transferable/worker-expected.txt
new file mode 100644
index 0000000..2aecae0
--- /dev/null
+++ b/third_party/blink/web_tests/virtual/streams-native/http/tests/streams/transferable/worker-expected.txt
@@ -0,0 +1,8 @@
+CONSOLE ERROR: line 2: Uncaught TypeError: Failed to execute 'postMessage' on 'DedicatedWorkerGlobalScope': Value at index 0 is an untransferable 'null' value.
+CONSOLE ERROR: line 2: Uncaught TypeError: Failed to execute 'postMessage' on 'DedicatedWorkerGlobalScope': Value at index 0 is an untransferable 'null' value.
+This is a testharness.js-based test.
+FAIL worker.postMessage should be able to transfer a ReadableStream promise_test: Unhandled rejection with value: "BAD: TypeError: Cannot read property 'constructor' of null"
+FAIL postMessage in a worker should be able to transfer a ReadableStream promise_test: Unhandled rejection with value: object "TypeError: Cannot read property 'constructor' of null"
+FAIL terminating a worker should not error the stream promise_test: Unhandled rejection with value: "error in worker"
+Harness: the test ran to completion.
+
diff --git a/third_party/closure_compiler/externs/bluetooth_private.js b/third_party/closure_compiler/externs/bluetooth_private.js
index cfcfbbc..164060c2 100644
--- a/third_party/closure_compiler/externs/bluetooth_private.js
+++ b/third_party/closure_compiler/externs/bluetooth_private.js
@@ -9,7 +9,7 @@
 // Please run the closure compiler before committing changes.
 // See https://chromium.googlesource.com/chromium/src/+/master/docs/closure_compilation.md
 
-// IMPORTANT:
+// IMPORTANT NOTE: Work-around for crbug.com/543822
 // s/chrome.bluetoothPrivate.bluetooth.Device/chrome.bluetooth.Device/
 
 /** @fileoverview Externs generated from namespace: bluetoothPrivate */
diff --git a/third_party/closure_compiler/externs/bookmark_manager_private.js b/third_party/closure_compiler/externs/bookmark_manager_private.js
index dfabdaf..cd0a6da 100644
--- a/third_party/closure_compiler/externs/bookmark_manager_private.js
+++ b/third_party/closure_compiler/externs/bookmark_manager_private.js
@@ -1,121 +1,127 @@
-// Copyright 2014 The Chromium Authors. All rights reserved.
+// Copyright 2019 The Chromium Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+// This file was generated by:
+//   tools/json_schema_compiler/compiler.py.
+// NOTE: The format of types has changed. 'FooType' is now
+//   'chrome.bookmarkManagerPrivate.FooType'.
+// Please run the closure compiler before committing changes.
+// See https://chromium.googlesource.com/chromium/src/+/master/docs/closure_compilation.md
+
+// IMPORTANT NOTE: Work-around for crbug.com/543822
+// s/chrome.bookmarkManagerPrivate.bookmarks.BookmarkTreeNode/BookmarkTreeNode/
+// s/chrome.bookmarkManagerPrivate.bookmarks.CreateDetails/chrome.bookmarks.CreateDetails/
+
 /** @fileoverview Externs generated from namespace: bookmarkManagerPrivate */
 
 /**
- * @typedef {{
- *   id: (string|undefined),
- *   parentId: (string|undefined),
- *   title: string,
- *   url: (string|undefined),
- *   children: Array
- * }}
- */
-var BookmarkNodeDataElement;
-
-/**
- * Information about the drag and drop data for use with drag and drop events.
- * @typedef {{
- *   sameProfile: boolean,
- *   elements: Array
- * }}
- */
-var BookmarkNodeData;
-
-/**
- * Collection of meta info fields.
- * @typedef {Object}
- */
-var MetaInfoFields;
-
-/**
  * @const
  */
 chrome.bookmarkManagerPrivate = {};
 
 /**
- * Copies the given bookmarks into the clipboard
- * @param {Array} idList An array of string-valued ids
- * @param {Function=} callback
+ * @typedef {{
+ *   id: (string|undefined),
+ *   parentId: (string|undefined),
+ *   title: string,
+ *   url: (string|undefined),
+ *   children: !Array<!chrome.bookmarkManagerPrivate.BookmarkNodeDataElement>
+ * }}
+ */
+chrome.bookmarkManagerPrivate.BookmarkNodeDataElement;
+
+/**
+ * Information about the drag and drop data for use with drag and drop events.
+ * @typedef {{
+ *   sameProfile: boolean,
+ *   elements: !Array<!chrome.bookmarkManagerPrivate.BookmarkNodeDataElement>
+ * }}
+ */
+chrome.bookmarkManagerPrivate.BookmarkNodeData;
+
+/**
+ * Collection of meta info fields.
+ * @typedef {Object}
+ */
+chrome.bookmarkManagerPrivate.MetaInfoFields;
+
+/**
+ * Copies the given bookmarks into the clipboard.
+ * @param {!Array<string>} idList An array of string-valued ids
+ * @param {function():void=} callback
  */
 chrome.bookmarkManagerPrivate.copy = function(idList, callback) {};
 
 /**
- * Cuts the given bookmarks into the clipboard
- * @param {Array} idList An array of string-valued ids
- * @param {Function=} callback
+ * Cuts the given bookmarks into the clipboard.
+ * @param {!Array<string>} idList An array of string-valued ids
+ * @param {function():void=} callback
  */
 chrome.bookmarkManagerPrivate.cut = function(idList, callback) {};
 
 /**
  * Pastes bookmarks from the clipboard into the parent folder after the last
- * selected node
+ * selected node.
  * @param {string} parentId
- * @param {Array=} selectedIdList An array of string-valued ids for selected
- * bookmarks
- * @param {Function=} callback
+ * @param {!Array<string>=} selectedIdList An array of string-valued ids for
+ *     selected bookmarks.
+ * @param {function():void=} callback
  */
 chrome.bookmarkManagerPrivate.paste = function(parentId, selectedIdList, callback) {};
 
 /**
- * Whether there are any bookmarks that can be pasted
- * @param {string} parentId The ID of the folder to paste into
- * @param {Function} callback
+ * Whether there are any bookmarks that can be pasted.
+ * @param {string} parentId The ID of the folder to paste into.
+ * @param {function(boolean):void} callback
  */
 chrome.bookmarkManagerPrivate.canPaste = function(parentId, callback) {};
 
 /**
- * Sorts the children of a given folder
- * @param {string} parentId The ID of the folder to sort the children of
+ * Sorts the children of a given folder.
+ * @param {string} parentId The ID of the folder to sort the children of.
  */
 chrome.bookmarkManagerPrivate.sortChildren = function(parentId) {};
 
 /**
- * Gets the i18n strings for the bookmark manager
- * @param {Function} callback
- */
-chrome.bookmarkManagerPrivate.getStrings = function(callback) {};
-
-/**
- * Begins dragging a set of bookmarks
- * @param {Array} idList An array of string-valued ids
+ * Begins dragging a set of bookmarks.
+ * @param {!Array<string>} idList An array of string-valued ids.
  * @param {number} dragNodeIndex The index of the dragged node in |idList|
- * @param {boolean} isFromTouch True if the drag was initiated from touch
+ * @param {boolean} isFromTouch True if the drag was initiated from touch.
  */
 chrome.bookmarkManagerPrivate.startDrag = function(idList, dragNodeIndex, isFromTouch) {};
 
 /**
- * Performs the drop action of the drag and drop session
- * @param {string} parentId The ID of the folder that the drop was made
+ * Performs the drop action of the drag and drop session.
+ * @param {string} parentId The ID of the folder that the drop was made.
  * @param {number=} index The index of the position to drop at. If left out the
- * dropped items will be placed at the end of the existing children
- * @param {Function=} callback
+ *     dropped items will be placed at the end of the existing children.
+ * @param {function():void=} callback
  */
 chrome.bookmarkManagerPrivate.drop = function(parentId, index, callback) {};
 
 /**
- * Retrieves a bookmark hierarchy from the given node.  If the node id is
- * empty, it is the full tree.  If foldersOnly is true, it will only return
- * folders, not actual bookmarks.
+ * Retrieves a bookmark hierarchy from the given node.  If the node id is empty,
+ * it is the full tree.  If foldersOnly is true, it will only return folders,
+ * not actual bookmarks.
  * @param {string} id ID of the root of the tree to pull.  If empty, the entire
- * tree will be returned.
+ *     tree will be returned.
  * @param {boolean} foldersOnly Pass true to only return folders.
- * @param {Function} callback
+ * @param {function(!Array<!BookmarkTreeNode>):void}
+ *     callback
  */
 chrome.bookmarkManagerPrivate.getSubtree = function(id, foldersOnly, callback) {};
 
 /**
- * Whether bookmarks can be modified
- * @param {Function} callback
+ * Whether bookmarks can be modified.
+ * @param {function(boolean):void} callback
  */
 chrome.bookmarkManagerPrivate.canEdit = function(callback) {};
 
 /**
  * Recursively removes list of bookmarks nodes.
- * @param {Array} idList An array of string-valued ids
- * @param {Function=} callback
+ * @param {!Array<string>} idList An array of string-valued ids.
+ * @param {function():void=} callback
  */
 chrome.bookmarkManagerPrivate.removeTrees = function(idList, callback) {};
 
@@ -126,70 +132,89 @@
 /**
  * Mimics the functionality of bookmarks.create, but will additionally set the
  * given meta info fields.
- * @param {chrome.bookmarks.CreateDetails} bookmark
- * @param {MetaInfoFields} metaInfo
- * @param {Function=} callback
+ * @param {!chrome.bookmarks.CreateDetails} bookmark
+ * @param {!chrome.bookmarkManagerPrivate.MetaInfoFields} metaInfo
+ * @param {function(!BookmarkTreeNode):void=}
+ *     callback
  */
 chrome.bookmarkManagerPrivate.createWithMetaInfo = function(bookmark, metaInfo, callback) {};
 
 /**
- * Gets meta info from a bookmark node
+ * Gets meta info from a bookmark node.
  * @param {string=} id The id of the bookmark to retrieve meta info from. If
- * omitted meta info for all nodes is returned.
+ *     omitted meta info for all nodes is returned.
  * @param {string=} key The key for the meta info to retrieve. If omitted, all
- * fields are returned
- * @param {Function=} callback
+ *     fields are returned.
+ * @param {function(((string|Object)|undefined)):void=} callback
  */
 chrome.bookmarkManagerPrivate.getMetaInfo = function(id, key, callback) {};
 
 /**
- * Sets a meta info value for a bookmark node
- * @param {string} id The id of the bookmark node to set the meta info on
- * @param {string} key The key of the meta info to set
- * @param {string} value The meta info to set
- * @param {Function=} callback
+ * Sets a meta info value for a bookmark node.
+ * @param {string} id The id of the bookmark node to set the meta info on.
+ * @param {string} key The key of the meta info to set.
+ * @param {string} value The meta info to set.
+ * @param {function():void=} callback
  */
 chrome.bookmarkManagerPrivate.setMetaInfo = function(id, key, value, callback) {};
 
 /**
  * Updates a set of meta info values for a bookmark node.
  * @param {string} id The id of the bookmark node to update the meta info of.
- * @param {MetaInfoFields} metaInfoChanges A set of meta info key/value pairs
- * to update.
- * @param {Function=} callback
+ * @param {!chrome.bookmarkManagerPrivate.MetaInfoFields} metaInfoChanges A set
+ *     of meta info key/value pairs to update.
+ * @param {function():void=} callback
  */
 chrome.bookmarkManagerPrivate.updateMetaInfo = function(id, metaInfoChanges, callback) {};
 
 /**
- * Performs an undo of the last change to the bookmark model
+ * Performs an undo of the last change to the bookmark model.
  */
 chrome.bookmarkManagerPrivate.undo = function() {};
 
 /**
- * Performs a redo of last undone change to the bookmark model
+ * Performs a redo of last undone change to the bookmark model.
  */
 chrome.bookmarkManagerPrivate.redo = function() {};
 
 /**
- * Gets the information for the undo if available
- * @param {Function} callback
+ * Gets the information for the undo if available.
+ * @param {function({
+ *   enabled: boolean,
+ *   label: string
+ * }):void} callback
  */
 chrome.bookmarkManagerPrivate.getUndoInfo = function(callback) {};
 
 /**
- * Gets the information for the redo if available
- * @param {Function} callback
+ * Gets the information for the redo if available.
+ * @param {function({
+ *   enabled: boolean,
+ *   label: string
+ * }):void} callback
  */
 chrome.bookmarkManagerPrivate.getRedoInfo = function(callback) {};
 
-/** @type {!ChromeEvent} */
+/**
+ * Fired when dragging bookmarks over the document.
+ * @type {!ChromeEvent}
+ */
 chrome.bookmarkManagerPrivate.onDragEnter;
 
-/** @type {!ChromeEvent} */
+/**
+ * Fired when the drag and drop leaves the document.
+ * @type {!ChromeEvent}
+ */
 chrome.bookmarkManagerPrivate.onDragLeave;
 
-/** @type {!ChromeEvent} */
+/**
+ * Fired when the user drops bookmarks on the document.
+ * @type {!ChromeEvent}
+ */
 chrome.bookmarkManagerPrivate.onDrop;
 
-/** @type {!ChromeEvent} */
+/**
+ * Fired when the meta info of a node changes.
+ * @type {!ChromeEvent}
+ */
 chrome.bookmarkManagerPrivate.onMetaInfoChanged;
diff --git a/third_party/dav1d/BUILD.gn b/third_party/dav1d/BUILD.gn
index 6275d19..56d709e 100644
--- a/third_party/dav1d/BUILD.gn
+++ b/third_party/dav1d/BUILD.gn
@@ -143,8 +143,6 @@
   ]
 
   sources = entry_point_sources
-
-  # TODO(dalecurtis): Fix arm compiler options...
   cflags = dav1d_copts
   if (is_win) {
     sources += [ "libdav1d/src/win32/thread.c" ]
@@ -166,12 +164,11 @@
   if (current_cpu == "x86" || current_cpu == "x64") {
     sources += x86_template_sources
   } else if (current_cpu == "arm") {
-    sources += arm_template_sources + arm32_asm_sources
+    sources += arm_template_sources
   } else if (current_cpu == "arm64") {
-    sources += arm_template_sources + arm64_asm_sources
+    sources += arm_template_sources
   }
 
-  # TODO(dalecurtis): Fix arm compiler options...
   cflags = dav1d_copts
   cflags += [ "-DBITDEPTH=8" ]
 }
@@ -187,12 +184,11 @@
   if (current_cpu == "x86" || current_cpu == "x64") {
     sources += x86_template_sources
   } else if (current_cpu == "arm") {
-    sources += arm_template_sources + arm32_asm_sources
+    sources += arm_template_sources
   } else if (current_cpu == "arm64") {
-    sources += arm_template_sources + arm64_asm_sources
+    sources += arm_template_sources
   }
 
-  # TODO(dalecurtis): Fix arm compiler options...
   cflags = dav1d_copts
   cflags += [ "-DBITDEPTH=16" ]
 }
@@ -216,13 +212,20 @@
       "libdav1d/src/arm/cpu.c",
       "libdav1d/src/arm/cpu.h",
     ]
+
+    # These are not template based so should only be built once.
+    if (current_cpu == "arm") {
+      sources += arm32_asm_sources
+    } else if (current_cpu == "arm64") {
+      sources += arm64_asm_sources
+    }
+
     configs -= [ "//build/config/compiler:chromium_code" ]
     configs += [
       "//build/config/compiler:no_chromium_code",
       ":dav1d_config",
     ]
 
-    # TODO(dalecurtis): Fix arm compiler options...
     cflags = dav1d_copts
   }
 }
diff --git a/third_party/dav1d/config/linux/arm/config.h b/third_party/dav1d/config/linux/arm/config.h
new file mode 100644
index 0000000..8a92b40
--- /dev/null
+++ b/third_party/dav1d/config/linux/arm/config.h
@@ -0,0 +1,32 @@
+/*
+ * Autogenerated by the Meson build system.
+ * Do not edit, your changes will be lost.
+ */
+
+#pragma once
+
+#define ARCH_AARCH64 0
+
+#define ARCH_ARM 1
+
+#define ARCH_X86 0
+
+#define ARCH_X86_32 0
+
+#define ARCH_X86_64 0
+
+#define CONFIG_16BPC 1
+
+#define CONFIG_8BPC 1
+
+// #define CONFIG_LOG 1 -- Logging is controlled by Chromium
+
+#define HAVE_ASM 1
+
+#define HAVE_AS_FUNC 0
+
+#define HAVE_GETAUXVAL 1
+
+#define HAVE_POSIX_MEMALIGN 1
+
+#define HAVE_UNISTD_H 1
diff --git a/third_party/dav1d/config/linux/arm64/config.h b/third_party/dav1d/config/linux/arm64/config.h
new file mode 100644
index 0000000..b27e1428
--- /dev/null
+++ b/third_party/dav1d/config/linux/arm64/config.h
@@ -0,0 +1,32 @@
+/*
+ * Autogenerated by the Meson build system.
+ * Do not edit, your changes will be lost.
+ */
+
+#pragma once
+
+#define ARCH_AARCH64 1
+
+#define ARCH_ARM 0
+
+#define ARCH_X86 0
+
+#define ARCH_X86_32 0
+
+#define ARCH_X86_64 0
+
+#define CONFIG_16BPC 1
+
+#define CONFIG_8BPC 1
+
+// #define CONFIG_LOG 1 -- Logging is controlled by Chromium
+
+#define HAVE_ASM 1
+
+#define HAVE_AS_FUNC 0
+
+#define HAVE_GETAUXVAL 1
+
+#define HAVE_POSIX_MEMALIGN 1
+
+#define HAVE_UNISTD_H 1
diff --git a/third_party/dav1d/config/linux/x64/config.asm b/third_party/dav1d/config/linux/x64/config.asm
index 3a4b4626..a28aa40 100644
--- a/third_party/dav1d/config/linux/x64/config.asm
+++ b/third_party/dav1d/config/linux/x64/config.asm
@@ -6,4 +6,3 @@
 %define ARCH_X86_64 1
 
 ; %define STACK_ALIGNMENT 32 -- Stack alignment is controlled by Chromium
-
diff --git a/third_party/dav1d/config/linux/x86/config.asm b/third_party/dav1d/config/linux/x86/config.asm
index ca6ab318..9efb555 100644
--- a/third_party/dav1d/config/linux/x86/config.asm
+++ b/third_party/dav1d/config/linux/x86/config.asm
@@ -8,4 +8,3 @@
 %define PIC 1
 
 ; %define STACK_ALIGNMENT 16 -- Stack alignment is controlled by Chromium
-
diff --git a/third_party/dav1d/config/win/x64/config.asm b/third_party/dav1d/config/win/x64/config.asm
index 87d6b09..6abf1a6 100644
--- a/third_party/dav1d/config/win/x64/config.asm
+++ b/third_party/dav1d/config/win/x64/config.asm
@@ -6,4 +6,3 @@
 %define ARCH_X86_64 1
 
 ; %define STACK_ALIGNMENT 16 -- Stack alignment is controlled by Chromium
-
diff --git a/third_party/dav1d/config/win/x86/config.asm b/third_party/dav1d/config/win/x86/config.asm
index 340609a..9c9ba8e 100644
--- a/third_party/dav1d/config/win/x86/config.asm
+++ b/third_party/dav1d/config/win/x86/config.asm
@@ -10,4 +10,3 @@
 %define PREFIX 1
 
 ; %define STACK_ALIGNMENT 4 -- Stack alignment is controlled by Chromium
-
diff --git a/third_party/dav1d/crossfiles/arm.crossfile b/third_party/dav1d/crossfiles/arm.crossfile
new file mode 100644
index 0000000..9bd967c
--- /dev/null
+++ b/third_party/dav1d/crossfiles/arm.crossfile
@@ -0,0 +1,15 @@
+[binaries]
+c = 'clang'
+ar = 'llvm-ar'
+
+# Copied from //third_party/ffmpeg's build_ffmpeg.py script. Does not use all
+# the cflags listed there since dav1d doesn't currently need such specificity.
+[properties]
+c_args = ['-march=armv7-a', '--target=arm-linux-gnueabihf']
+c_link_args = ['--target=arm-linux-gnueabihf']
+
+[host_machine]
+system = 'linux'
+cpu_family = 'arm'
+cpu = 'armv7-a'
+endian = 'little'
diff --git a/third_party/dav1d/crossfiles/arm64.crossfile b/third_party/dav1d/crossfiles/arm64.crossfile
new file mode 100644
index 0000000..7fd8fd3
--- /dev/null
+++ b/third_party/dav1d/crossfiles/arm64.crossfile
@@ -0,0 +1,15 @@
+[binaries]
+c = 'clang'
+ar = 'llvm-ar'
+
+# Copied from //third_party/ffmpeg's build_ffmpeg.py script. Does not use all
+# the cflags listed there since dav1d doesn't currently need such specificity.
+[properties]
+c_args = ['-march=armv8-a', '--target=aarch64-linux-gnu']
+c_link_args = ['--target=aarch64-linux-gnu']
+
+[host_machine]
+system = 'linux'
+cpu_family = 'aarch64'
+cpu = 'armv8-a'
+endian = 'little'
diff --git a/third_party/dav1d/generate_configs.py b/third_party/dav1d/generate_configs.py
index 514be0109..140e2c79 100755
--- a/third_party/dav1d/generate_configs.py
+++ b/third_party/dav1d/generate_configs.py
@@ -41,7 +41,9 @@
   with open(path, 'w') as f:
     for search, replace in search_replace:
       contents = re.sub(search, replace, contents)
-    f.write(contents)
+
+    # Cleanup trailing newlines.
+    f.write(contents.strip() + '\n')
 
 
 def SetupWindowsCrossCompileToolchain(target_arch):
@@ -141,9 +143,14 @@
   linux_env['CC'] = 'clang'
 
   GenerateConfig('config/linux/x64', linux_env)
+  GenerateConfig('config/linux-noasm/x64', linux_env, ['-Dbuild_asm=false'])
+
   GenerateConfig('config/linux/x86', linux_env,
                  ['--cross-file', '../crossfiles/linux32.crossfile'])
-  GenerateConfig('config/linux-noasm/x64', linux_env, ['-Dbuild_asm=false'])
+  GenerateConfig('config/linux/arm', linux_env,
+                 ['--cross-file', '../crossfiles/arm.crossfile'])
+  GenerateConfig('config/linux/arm64', linux_env,
+                 ['--cross-file', '../crossfiles/arm64.crossfile'])
 
   win_x86_env = SetupWindowsCrossCompileToolchain('x86')
   GenerateConfig(
diff --git a/third_party/freetype/README.chromium b/third_party/freetype/README.chromium
index eb10801..318973e6 100644
--- a/third_party/freetype/README.chromium
+++ b/third_party/freetype/README.chromium
@@ -1,7 +1,7 @@
 Name: FreeType
 URL: http://www.freetype.org/
-Version: VER-2-9-1-333
-Revision: a6feefdfefd6eb8e62728f40638241bb1d8c993e
+Version: VER-2-9-1-349
+Revision: 6d65c60fca0ebce88e2bcfeac92a7a791e03bf42
 License: Custom license "inspired by the BSD, Artistic, and IJG (Independent
          JPEG Group) licenses"
 License File: src/docs/FTL.TXT
diff --git a/third_party/freetype/include/freetype-custom-config/ftconfig.h b/third_party/freetype/include/freetype-custom-config/ftconfig.h
index b3337fa..6a2eed1 100644
--- a/third_party/freetype/include/freetype-custom-config/ftconfig.h
+++ b/third_party/freetype/include/freetype-custom-config/ftconfig.h
@@ -4,7 +4,7 @@
  *
  *   ANSI-specific configuration file (specification only).
  *
- * Copyright 1996-2019 by
+ * Copyright (C) 1996-2019 by
  * David Turner, Robert Wilhelm, and Werner Lemberg.
  *
  * This file is part of the FreeType project, and may only be used,
diff --git a/third_party/freetype/include/freetype-custom-config/ftoption.h b/third_party/freetype/include/freetype-custom-config/ftoption.h
index e7bc45a9..523ee05 100644
--- a/third_party/freetype/include/freetype-custom-config/ftoption.h
+++ b/third_party/freetype/include/freetype-custom-config/ftoption.h
@@ -4,7 +4,7 @@
  *
  *   User-selectable configuration macros (specification only).
  *
- * Copyright 1996-2019 by
+ * Copyright (C) 1996-2019 by
  * David Turner, Robert Wilhelm, and Werner Lemberg.
  *
  * This file is part of the FreeType project, and may only be used,
@@ -301,13 +301,13 @@
    *   By default, FreeType 2 is compiled with the 'psnames' module.  This
    *   module is in charge of converting a glyph name string into a Unicode
    *   value, or return a Macintosh standard glyph name for the use with the
-   *   TrueType `post` table.
+   *   TrueType 'post' table.
    *
    *   Undefine this macro if you do not want 'psnames' compiled in your
    *   build of FreeType.  This has the following effects:
    *
    *   - The TrueType driver will provide its own set of glyph names, if you
-   *     build it to support postscript names in the TrueType `post` table,
+   *     build it to support postscript names in the TrueType 'post' table,
    *     but will not synthesize a missing Unicode charmap.
    *
    *   - The Type~1 driver will not be able to synthesize a Unicode charmap
@@ -344,7 +344,7 @@
    *   (mac dfont, mac resource, macbinary containing a mac resource) on
    *   non-Mac platforms.
    *
-   *   Note that the `FOND` resource isn't checked.
+   *   Note that the 'FOND' resource isn't checked.
    */
 /* #define FT_CONFIG_OPTION_MAC_FONTS */
 
@@ -529,7 +529,7 @@
   /**************************************************************************
    *
    * Define `TT_CONFIG_OPTION_COLOR_LAYERS` if you want to support coloured
-   * outlines (from the `COLR`/`CPAL` tables) in all formats using the 'sfnt'
+   * outlines (from the 'COLR'/'CPAL' tables) in all formats using the 'sfnt'
    * module (namely TrueType~& OpenType).
    */
 #define TT_CONFIG_OPTION_COLOR_LAYERS
@@ -686,8 +686,8 @@
   /**************************************************************************
    *
    * Define `TT_CONFIG_OPTION_GX_VAR_SUPPORT` if you want to include support
-   * for Apple's distortable font technology (`fvar`, `gvar`, `cvar`, and
-   * `avar` tables).  Tagged 'Font Variations', this is now part of OpenType
+   * for Apple's distortable font technology ('fvar', 'gvar', 'cvar', and
+   * 'avar' tables).  Tagged 'Font Variations', this is now part of OpenType
    * also.  This has many similarities to Type~1 Multiple Masters support.
    */
 #define TT_CONFIG_OPTION_GX_VAR_SUPPORT
@@ -696,7 +696,7 @@
   /**************************************************************************
    *
    * Define `TT_CONFIG_OPTION_BDF` if you want to include support for an
-   * embedded `BDF ` table within SFNT-based bitmap formats.
+   * embedded 'BDF~' table within SFNT-based bitmap formats.
    */
 /* #define TT_CONFIG_OPTION_BDF */
 
@@ -749,8 +749,8 @@
    * `T1_MAX_CHARSTRING_OPERANDS` is the charstring stack's capacity.  A
    * minimum of~16 is required.
    *
-   * The Chinese font 'MingTiEG-Medium' (covering a CNS 11643 character set)
-   * needs 256.
+   * The Chinese font 'MingTiEG-Medium' (covering the CNS 11643 character
+   * set) needs 256.
    */
 #define T1_MAX_CHARSTRINGS_OPERANDS  512
 
diff --git a/third_party/harfbuzz-ng/BUILD.gn b/third_party/harfbuzz-ng/BUILD.gn
index a743dd7..d9d34e8 100644
--- a/third_party/harfbuzz-ng/BUILD.gn
+++ b/third_party/harfbuzz-ng/BUILD.gn
@@ -33,6 +33,31 @@
   source_set("harfbuzz_source") {
     visibility = [ "//third_party:freetype_harfbuzz" ]
 
+    public = [
+      "src/src/hb-blob.h",
+      "src/src/hb-buffer.h",
+      "src/src/hb-common.h",
+      "src/src/hb-deprecated.h",
+      "src/src/hb-face.h",
+      "src/src/hb-font.h",
+      "src/src/hb-ft.h",
+      "src/src/hb-icu.h",
+      "src/src/hb-map.h",
+      "src/src/hb-ot-font.h",
+      "src/src/hb-ot-layout.h",
+      "src/src/hb-ot-math.h",
+      "src/src/hb-ot-shape.h",
+      "src/src/hb-ot-var.h",
+      "src/src/hb-ot.h",
+      "src/src/hb-set.h",
+      "src/src/hb-shape-plan.h",
+      "src/src/hb-shape.h",
+      "src/src/hb-subset.h",
+      "src/src/hb-unicode.h",
+      "src/src/hb-version.h",
+      "src/src/hb.h",
+    ]
+
     sources = [
       "src/src/hb-aat-fdsc-table.hh",
       "src/src/hb-aat-layout-ankr-table.hh",
@@ -48,13 +73,11 @@
       "src/src/hb-array.hh",
       "src/src/hb-atomic.hh",
       "src/src/hb-blob.cc",
-      "src/src/hb-blob.h",
       "src/src/hb-blob.hh",
       "src/src/hb-buffer-deserialize-json.hh",
       "src/src/hb-buffer-deserialize-text.hh",
       "src/src/hb-buffer-serialize.cc",
       "src/src/hb-buffer.cc",
-      "src/src/hb-buffer.h",
       "src/src/hb-buffer.hh",
       "src/src/hb-cache.hh",
       "src/src/hb-cff-interp-common.hh",
@@ -63,20 +86,16 @@
       "src/src/hb-cff1-interp-cs.hh",
       "src/src/hb-cff2-interp-cs.hh",
       "src/src/hb-common.cc",
-      "src/src/hb-common.h",
       "src/src/hb-debug.hh",
-      "src/src/hb-deprecated.h",
       "src/src/hb-dsalgs.hh",
       "src/src/hb-face.cc",
-      "src/src/hb-face.h",
       "src/src/hb-face.hh",
       "src/src/hb-font.cc",
-      "src/src/hb-font.h",
       "src/src/hb-font.hh",
       "src/src/hb-ft.cc",
-      "src/src/hb-ft.h",
       "src/src/hb-icu.cc",
-      "src/src/hb-icu.h",
+      "src/src/hb-map.cc",
+      "src/src/hb-map.hh",
       "src/src/hb-mutex.hh",
       "src/src/hb-object.hh",
       "src/src/hb-open-file.hh",
@@ -89,11 +108,9 @@
       "src/src/hb-ot-color-cbdt-table.hh",
       "src/src/hb-ot-color-colr-table.hh",
       "src/src/hb-ot-color-cpal-table.hh",
-      "src/src/hb-ot-deprecated.h",
       "src/src/hb-ot-face.cc",
       "src/src/hb-ot-face.hh",
       "src/src/hb-ot-font.cc",
-      "src/src/hb-ot-font.h",
       "src/src/hb-ot-gasp-table.hh",
       "src/src/hb-ot-hdmx-table.hh",
       "src/src/hb-ot-head-table.hh",
@@ -107,13 +124,11 @@
       "src/src/hb-ot-layout-gsub-table.hh",
       "src/src/hb-ot-layout-gsubgpos.hh",
       "src/src/hb-ot-layout.cc",
-      "src/src/hb-ot-layout.h",
       "src/src/hb-ot-layout.hh",
       "src/src/hb-ot-map.cc",
       "src/src/hb-ot-map.hh",
       "src/src/hb-ot-math-table.hh",
       "src/src/hb-ot-math.cc",
-      "src/src/hb-ot-math.h",
       "src/src/hb-ot-maxp-table.hh",
       "src/src/hb-ot-name-language.cc",
       "src/src/hb-ot-name-language.hh",
@@ -151,7 +166,6 @@
       "src/src/hb-ot-shape-normalize.cc",
       "src/src/hb-ot-shape-normalize.hh",
       "src/src/hb-ot-shape.cc",
-      "src/src/hb-ot-shape.h",
       "src/src/hb-ot-shape.hh",
       "src/src/hb-ot-tag.cc",
       "src/src/hb-ot-var-avar-table.hh",
@@ -159,17 +173,12 @@
       "src/src/hb-ot-var-hvar-table.hh",
       "src/src/hb-ot-var-mvar-table.hh",
       "src/src/hb-ot-var.cc",
-      "src/src/hb-ot-var.h",
-      "src/src/hb-ot.h",
       "src/src/hb-set-digest.hh",
       "src/src/hb-set.cc",
-      "src/src/hb-set.h",
       "src/src/hb-set.hh",
       "src/src/hb-shape-plan.cc",
-      "src/src/hb-shape-plan.h",
       "src/src/hb-shape-plan.hh",
       "src/src/hb-shape.cc",
-      "src/src/hb-shape.h",
       "src/src/hb-shaper-impl.hh",
       "src/src/hb-shaper-list.hh",
       "src/src/hb-shaper.cc",
@@ -180,18 +189,19 @@
       "src/src/hb-subset-cff-common.hh",
       "src/src/hb-subset-cff1.cc",
       "src/src/hb-subset-cff2.cc",
+      "src/src/hb-subset-glyf.cc",
       "src/src/hb-subset-glyf.hh",
+      "src/src/hb-subset-input.cc",
+      "src/src/hb-subset-input.hh",
+      "src/src/hb-subset-plan.cc",
       "src/src/hb-subset-plan.hh",
-      "src/src/hb-subset.h",
+      "src/src/hb-subset.cc",
       "src/src/hb-subset.hh",
       "src/src/hb-unicode-emoji-table.hh",
       "src/src/hb-unicode.cc",
-      "src/src/hb-unicode.h",
       "src/src/hb-unicode.hh",
       "src/src/hb-utf.hh",
-      "src/src/hb-version.h",
       "src/src/hb-warning.cc",
-      "src/src/hb.h",
       "src/src/hb.hh",
     ]
 
@@ -242,10 +252,8 @@
     # CoreText AAT. Remove this backstop and the whole hb-coretext build clause
     # here once we have reliably switched to HarfBuzz AAT for one stable cycle.
     if (false) {
-      sources += [
-        "src/src/hb-coretext.cc",
-        "src/src/hb-coretext.h",
-      ]
+      public += [ "src/src/hb-coretext.h" ]
+      sources += [ "src/src/hb-coretext.cc" ]
       defines += [ "HAVE_CORETEXT" ]
       libs = [
         "CoreFoundation.framework",
@@ -256,10 +264,8 @@
 
     if (use_glib) {
       configs += [ "//build/config/linux:glib" ]
-      sources += [
-        "src/src/hb-glib.cc",
-        "src/src/hb-glib.h",
-      ]
+      public += [ "src/src/hb-glib.h" ]
+      sources += [ "src/src/hb-glib.cc" ]
     }
   }
 }
diff --git a/third_party/harfbuzz-ng/README.chromium b/third_party/harfbuzz-ng/README.chromium
index a40413b..8733cd56f 100644
--- a/third_party/harfbuzz-ng/README.chromium
+++ b/third_party/harfbuzz-ng/README.chromium
@@ -41,20 +41,10 @@
     hb-directwrite.cc
     hb-directwrite.h
     hb-fallback-shape.cc
-    hb-iter-private.hh
-    hb-machinery-private.hh
-    hb-map-private.hh
-    hb-map.cc
-    hb-map.h
     hb-null.hh
     hb-ot-color-sbix-table.hh
     hb-ot-color-svg-table.hh
     hb-ot-color.cc
-    hb-subset-glyf.cc
-    hb-subset-input.cc
-    hb-subset-plan.cc
-    hb-subset.cc
     hb-uniscribe.cc
     hb-uniscribe.h
-    hb-vector-private.hh
     test-unicode-ranges.cc
diff --git a/third_party/inspector_protocol/BUILD.gn b/third_party/inspector_protocol/BUILD.gn
index 6b82c845..0cf1a98 100644
--- a/third_party/inspector_protocol/BUILD.gn
+++ b/third_party/inspector_protocol/BUILD.gn
@@ -36,9 +36,9 @@
     ":json_parser",
     ":linux_dev_platform",
     "//base",
-    "//testing/gmock:gmock",
-    "//testing/gtest:gtest",
-    "//testing/gtest:gtest_main",
+    "//third_party/googletest:gmock",
+    "//third_party/googletest:gtest",
+    "//third_party/googletest:gtest_main",
   ]
 }
 
@@ -61,12 +61,13 @@
   ]
   deps = [
     ":cbor",
+    ":json_parser",
     ":json_std_string_writer",
     ":linux_dev_platform",
     "//base",
-    "//testing/gmock:gmock",
-    "//testing/gtest:gtest",
-    "//testing/gtest:gtest_main",
+    "//third_party/googletest:gmock",
+    "//third_party/googletest:gtest",
+    "//third_party/googletest:gtest_main",
   ]
 }
 
@@ -77,9 +78,9 @@
   ]
   deps = [
     "//base",
-    "//testing/gmock:gmock",
-    "//testing/gtest:gtest",
-    "//testing/gtest:gtest_main",
+    "//third_party/googletest:gmock",
+    "//third_party/googletest:gtest",
+    "//third_party/googletest:gtest_main",
   ]
 }
 
@@ -88,6 +89,9 @@
     "encoding/json_parser_handler.h",
     "encoding/json_std_string_writer.cc",
     "encoding/json_std_string_writer.h",
+    "encoding/platform.h",
+    "encoding/span.h",
+    "encoding/status.h",
   ]
 }
 
@@ -99,8 +103,8 @@
     ":json_std_string_writer",
     ":linux_dev_platform",
     "//base",
-    "//testing/gmock:gmock",
-    "//testing/gtest:gtest",
-    "//testing/gtest:gtest_main",
+    "//third_party/googletest:gmock",
+    "//third_party/googletest:gtest",
+    "//third_party/googletest:gtest_main",
   ]
 }
diff --git a/third_party/inspector_protocol/README.chromium b/third_party/inspector_protocol/README.chromium
index 55ce19f..3c6581d 100644
--- a/third_party/inspector_protocol/README.chromium
+++ b/third_party/inspector_protocol/README.chromium
@@ -2,7 +2,7 @@
 Short Name: inspector_protocol
 URL: https://chromium.googlesource.com/deps/inspector_protocol/
 Version: 0
-Revision: c1f864824e508e943c4e8a3b3e1bad4d3af4a1e6
+Revision: 1b870d0b2606aaf04a15577297f7476b0cc52780
 License: BSD
 License File: LICENSE
 Security Critical: no
diff --git a/third_party/inspector_protocol/encoding/cbor.cc b/third_party/inspector_protocol/encoding/cbor.cc
index 259cfb1..f771f5b 100644
--- a/third_party/inspector_protocol/encoding/cbor.cc
+++ b/third_party/inspector_protocol/encoding/cbor.cc
@@ -4,10 +4,8 @@
 
 #include "cbor.h"
 
-#include <stdio.h>
 #include <cassert>
 #include <limits>
-#include <string>
 #include "json_parser_handler.h"
 
 namespace inspector_protocol {
diff --git a/third_party/inspector_protocol/encoding/span_test.cc b/third_party/inspector_protocol/encoding/span_test.cc
index 8533746..48ee76d 100644
--- a/third_party/inspector_protocol/encoding/span_test.cc
+++ b/third_party/inspector_protocol/encoding/span_test.cc
@@ -11,7 +11,7 @@
 class SpanTest : public ::testing::Test {};
 
 using TestTypes = ::testing::Types<uint8_t, uint16_t>;
-TYPED_TEST_CASE(SpanTest, TestTypes);
+TYPED_TEST_SUITE(SpanTest, TestTypes);
 
 TYPED_TEST(SpanTest, Empty) {
   span<TypeParam> empty;
diff --git a/third_party/inspector_protocol/roll.py b/third_party/inspector_protocol/roll.py
index 7ab04bb..59699e6 100755
--- a/third_party/inspector_protocol/roll.py
+++ b/third_party/inspector_protocol/roll.py
@@ -3,6 +3,7 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
+from __future__ import print_function
 import argparse
 import sys
 import os
@@ -125,7 +126,7 @@
     CheckRepoIsNotAtMasterBranch(downstream)
     src_dir = upstream
     dest_dir = os.path.join(downstream, 'third_party/inspector_protocol')
-  print 'Rolling %s into %s ...' % (src_dir, dest_dir)
+  print('Rolling %s into %s ...' % (src_dir, dest_dir))
   src_files = set(FindFilesToSyncIn(src_dir))
   dest_files = set(FindFilesToSyncIn(dest_dir))
   to_add = [f for f in src_files if f not in dest_files]
@@ -133,16 +134,16 @@
   to_copy = [f for f in src_files
              if (f in dest_files and not FilesAreEqual(
                  os.path.join(src_dir, f), os.path.join(dest_dir, f)))]
-  print 'To add: %s' % to_add
-  print 'To delete: %s' % to_delete
-  print 'To copy: %s' % to_copy
+  print('To add: %s' % to_add)
+  print('To delete: %s' % to_delete)
+  print('To copy: %s' % to_copy)
   if not to_add and not to_delete and not to_copy:
-    print 'Nothing to do. You\'re good.'
+    print('Nothing to do. You\'re good.')
     sys.exit(0)
   if not args.force:
-    print 'Rerun with --force if you wish the modifications to be done.'
+    print('Rerun with --force if you wish the modifications to be done.')
     sys.exit(1)
-  print 'You said --force ... as you wish, modifying the destination.'
+  print('You said --force ... as you wish, modifying the destination.')
   for f in to_add + to_copy:
     shutil.copyfile(os.path.join(src_dir, f), os.path.join(dest_dir, f))
     shutil.copymode(os.path.join(src_dir, f), os.path.join(dest_dir, f))
diff --git a/third_party/libaom/BUILD.gn b/third_party/libaom/BUILD.gn
index 2a4cde71..1fb5975 100644
--- a/third_party/libaom/BUILD.gn
+++ b/third_party/libaom/BUILD.gn
@@ -161,7 +161,12 @@
         cflags = [ "-mfpu=neon" ]
       }
       configs += [ ":libaom_config" ]
-      sources = aom_av1_common_intrin_neon
+
+      # https://bugs.chromium.org/p/aomedia/issues/detail?id=2294
+      sources = [
+        "//third_party/libaom/source/libaom/aom_ports/arm_cpudetect.c",
+      ]
+      sources += aom_av1_common_intrin_neon
       sources += aom_dsp_common_intrin_neon
     }
   }
diff --git a/third_party/metrics_proto/README.chromium b/third_party/metrics_proto/README.chromium
index 47ec08a1..ee80601 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: 235268166
-Date: 2019/02/22 UTC
+Version: 236649832
+Date: 2019/03/04 UTC
 License: BSD
 Security Critical: Yes
 
diff --git a/third_party/metrics_proto/ukm/source.proto b/third_party/metrics_proto/ukm/source.proto
index f9fdba3..93105d32 100644
--- a/third_party/metrics_proto/ukm/source.proto
+++ b/third_party/metrics_proto/ukm/source.proto
@@ -9,7 +9,7 @@
 package ukm;
 
 // Source contains data related to a top-level navigation.
-// Next tag: 13
+// Next tag: 15
 message Source {
   // The URL scheme, such as HTTP, HTTPS, CHROME_EXTENSION, etc.
   enum UrlScheme {
@@ -26,10 +26,18 @@
   // An identifier for the source. This should be unique within a session.
   optional int64 id = 1;
 
-  // The previous source id for the tab this source is in. Should only be set
-  // for navigation sources.
+  // The previous source id for the tab this source is in. Should only
+  // be set for navigation sources. Note that only non-same-document
+  // sources are included here. If you want to include same-document
+  // navigations, use previous_same_document_source_id.
   optional int64 previous_source_id = 11;
 
+  // The source id for the previous same document navigation, if the
+  // previously committed source was a same document navigation. If
+  // the previously committed source was not a same document
+  // navigation, this field will be unset.
+  optional int64 previous_same_document_source_id = 14;
+
   // For sources representing the first navigation in a new tab, this id marks
   // the source which opened the tab. Should only be set for the first
   // navigation source in a tab.
@@ -51,6 +59,11 @@
   // or partial redirect chains for main frame navigations.
   repeated UrlInfo urls = 8;
 
+  // Whether this source is for a same document navigation. Examples of same
+  // document navigations are fragment navigations, pushState/replaceState,
+  // and same page history navigation.
+  optional bool is_same_document_navigation = 13;
+
   // The URL of the source, as recorded in history. If this URL has not been
   // discovered by Google's crawler, it should not be recorded.
   optional string url = 2;
diff --git a/third_party/webdriver/atoms.cc b/third_party/webdriver/atoms.cc
index fb76a42..0870ff4 100644
--- a/third_party/webdriver/atoms.cc
+++ b/third_party/webdriver/atoms.cc
@@ -6242,12 +6242,12 @@
     "rentNode;wb&&b instanceof ShadowRoot&&(b=a.host);return b}for(var f=z(a",
     "),g=e(a),l;g&&g!=f.documentElement&&g!=f.body;)l=xb(g,a),d(g,new x(g.cl",
     "ientWidth,\ng.clientHeight),l,b,c),g=e(g);l=vb(a);a=ra(a?new A(z(a)):r|",
-    "|(r=new A));d(f.documentElement,a,l,b,c);d(f.body,a,l,b,c)};aa(\"_\",fu",
-    "nction(a,b,c){c||(c=new qb(0,0,a.offsetWidth,a.offsetHeight));yb(a,c,b)",
-    ";a=vb(a);return new w(a.x+c.left,a.y+c.top)});; return this._.apply(nul",
-    "l,arguments);}.apply({navigator:typeof window!='undefined'?window.navig",
-    "ator:null,document:typeof window!='undefined'?window.document:null}, ar",
-    "guments);}",
+    "|(r=new A));d(f.documentElement,a,l,b,c);f.body&&d(f.body,a,l,b,c)};aa(",
+    "\"_\",function(a,b,c){c||(c=new qb(0,0,a.offsetWidth,a.offsetHeight));y",
+    "b(a,c,b);a=vb(a);return new w(a.x+c.left,a.y+c.top)});; return this._.a",
+    "pply(null,arguments);}.apply({navigator:typeof window!='undefined'?wind",
+    "ow.navigator:null,document:typeof window!='undefined'?window.document:n",
+    "ull}, arguments);}",
     NULL
 };
 
diff --git a/third_party/webdriver/patch.diff b/third_party/webdriver/patch.diff
index 90863e2..6d10313ee 100644
--- a/third_party/webdriver/patch.diff
+++ b/third_party/webdriver/patch.diff
@@ -81,20 +81,23 @@
      buttonValueMap[bot.events.EventType.CLICK] = [0, 1, 2, null];
      buttonValueMap[bot.events.EventType.CONTEXTMENU] = [null, null, 2, null];
 diff --git a/javascript/chrome-driver/atoms.js b/javascript/chrome-driver/atoms.js
-index 5cf4416460..329390cf1f 100644
+index 5cf4416460..7cd02f0507 100644
 --- a/javascript/chrome-driver/atoms.js
 +++ b/javascript/chrome-driver/atoms.js
-@@ -142,6 +142,10 @@ webdriver.chrome.scrollIntoView_ = function(elem, region, center) {
+@@ -142,7 +142,12 @@ webdriver.chrome.scrollIntoView_ = function(elem, region, center) {
  
    offset = goog.style.getClientPosition(elem);
    var windowSize = goog.dom.getDomHelper(elem).getViewportSize();
+-  scrollHelper(doc.body, windowSize, offset, region, center);
 +  // Chrome uses either doc.documentElement or doc.body, depending on
 +  // compatibility settings. For reliability, call scrollHelper on both.
 +  // Calling scrollHelper on the wrong object is harmless.
 +  scrollHelper(doc.documentElement, windowSize, offset, region, center);
-   scrollHelper(doc.body, windowSize, offset, region, center);
++  if (doc.body)
++    scrollHelper(doc.body, windowSize, offset, region, center);
  };
  
+ 
 diff --git a/rake-tasks/crazy_fun/mappings/javascript.rb b/rake-tasks/crazy_fun/mappings/javascript.rb
 index 1ac2b2066a..dfa11fbbc2 100644
 --- a/rake-tasks/crazy_fun/mappings/javascript.rb
diff --git a/third_party/yara/README.chromium b/third_party/yara/README.chromium
deleted file mode 100644
index fa54425e..0000000
--- a/third_party/yara/README.chromium
+++ /dev/null
@@ -1,21 +0,0 @@
-Name: Yara
-URL: https://github.com/virustotal/yara
-Version: 74734418a256c5304ccaf1d322c57e305ff75362
-Date: (OPTIONAL if version is supplied) The date that the package was updated
-Revision: (OPTIONAL if version is supplied) The current revision of the package
-License: BSD 3-Clause
-License File: src/COPYING
-Security Critical: Yes
-
-Description: YARA is a tool aimed at (but not limited to) helping malware
-researchers to identify and classify malware samples. With YARA you can create
-descriptions of malware families (or whatever you want to describe) based on
-textual or binary patterns. Each description, a.k.a rule, consists of a set of
-strings and a boolean expression which determine its logic.
-
-Yara is shipped with the chrome_cleaner component downloaded by Google Chrome
-and the software_reporter_tool component fetched by the component updater. It is
-not linked directly into Chromium binaries.
-
-Local Modifications:
-None
diff --git a/third_party/yara/src/COPYING b/third_party/yara/src/COPYING
deleted file mode 100644
index 81b0eed..0000000
--- a/third_party/yara/src/COPYING
+++ /dev/null
@@ -1,26 +0,0 @@
-Copyright (c) 2007-2016. The YARA Authors. All Rights Reserved.
-
-Redistribution and use in source and binary forms, with or without modification,
-are permitted provided that the following conditions are met:
-
-1. Redistributions of source code must retain the above copyright notice, this
-list of conditions and the following disclaimer.
-
-2. Redistributions in binary form must reproduce the above copyright notice,
-this list of conditions and the following disclaimer in the documentation and/or
-other materials provided with the distribution.
-
-3. Neither the name of the copyright holder nor the names of its contributors
-may be used to endorse or promote products derived from this software without
-specific prior written permission.
-
-THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
-ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
-WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
-DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
-ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
-(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
-LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
-ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
-SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/tools/cygprofile/profile_android_startup.py b/tools/cygprofile/profile_android_startup.py
index ab22198c..3a027619 100755
--- a/tools/cygprofile/profile_android_startup.py
+++ b/tools/cygprofile/profile_android_startup.py
@@ -27,6 +27,7 @@
 from devil.android import forwarder
 from devil.android.ndk import abis
 from devil.android.sdk import intent
+from devil.android.sdk import version_codes
 
 sys.path.append(os.path.join(_SRC_PATH, 'build', 'android'))
 import devil_chromium
@@ -206,14 +207,16 @@
     if device is None:
       devices = device_utils.DeviceUtils.HealthyDevices()
       assert devices, 'Expected at least one connected device'
-      # In case of many connected devices, favor the device running arm64.
-      arm_64_device = ''
+      # Favor device running Android version[K,L] if exists.
+      # Code ordering is not yet supported on Monochrome.
+      preferred_device = None
       for device in devices:
-        if device.GetABI() == abis.ARM_64:
-         arm_64_device = device
+        device_version = device.build_version_sdk
+        if (device_version >= version_codes.KITKAT
+            and device_version <= version_codes.LOLLIPOP_MR1):
+         preferred_device = device
          break
-
-      self._device = arm_64_device if arm_64_device else devices[0]
+      self._device = preferred_device if preferred_device else devices[0]
     else:
       self._device = device_utils.DeviceUtils(device)
     self._cygprofile_tests = os.path.join(
diff --git a/tools/json_schema_compiler/js_externs_generator.py b/tools/json_schema_compiler/js_externs_generator.py
index af7d190..36670cd3 100644
--- a/tools/json_schema_compiler/js_externs_generator.py
+++ b/tools/json_schema_compiler/js_externs_generator.py
@@ -133,7 +133,7 @@
     elif is_constructor:
       c.Comment('@constructor', comment_prefix = '', wrap_indent=4)
       c.Comment('@private', comment_prefix = '', wrap_indent=4)
-    else:
+    elif js_type.jsexterns is None:
       self._AppendTypedef(c, js_type.properties)
 
     self._js_util.AppendSeeLink(c, self._namespace.name, 'type',
@@ -159,11 +159,13 @@
   def _AppendTypedef(self, c, properties):
     """Given an OrderedDict of properties, Appends code containing a @typedef.
     """
-    if not properties: return
 
     c.Append('@typedef {')
-    self._js_util.AppendObjectDefinition(c, self._namespace.name, properties,
-                                         new_line=False)
+    if properties:
+        self._js_util.AppendObjectDefinition(
+                c, self._namespace.name, properties, new_line=False)
+    else:
+        c.Append('Object', new_line=False)
     c.Append('}', new_line=False)
 
   def _AppendFunction(self, c, function):
diff --git a/tools/json_schema_compiler/js_externs_generator_test.py b/tools/json_schema_compiler/js_externs_generator_test.py
index ac8f677a..6bd4179 100755
--- a/tools/json_schema_compiler/js_externs_generator_test.py
+++ b/tools/json_schema_compiler/js_externs_generator_test.py
@@ -297,12 +297,19 @@
   {
     "namespace": "fakeJson",
     "description": "Fake JSON API Stuff",
-    "types": [ {
-      "id": "CrazyEnum",
-      "type": "string",
-      "enum": ["camelCaseEnum", "Non-Characters", "5NumFirst", \
+    "types": [
+      {
+        "id": "CrazyEnum",
+        "type": "string",
+        "enum": ["camelCaseEnum", "Non-Characters", "5NumFirst", \
 "3Just-plainOld_MEAN"]
-    } ],
+      },
+      {
+        "id": "CrazyObject",
+        "type": "object",
+        "additionalProperties": {"type": "string"}
+      }
+    ],
     "functions": [ {
       "name": "funcWithInlineObj",
       "type": "function",
@@ -393,6 +400,12 @@
 };
 
 /**
+ * @typedef {Object}
+ * @see https://developer.chrome.com/extensions/fakeJson#type-CrazyObject
+ */
+chrome.fakeJson.CrazyObject;
+
+/**
  * @param {{
  *   foo: (boolean|undefined),
  *   bar: number,
diff --git a/tools/licenses.py b/tools/licenses.py
index 75e3de1..4d609162d 100755
--- a/tools/licenses.py
+++ b/tools/licenses.py
@@ -332,7 +332,6 @@
     os.path.join('third_party', 'usb_ids'),
     os.path.join('third_party', 'v8-i18n'),
     os.path.join('third_party', 'wtl'),
-    os.path.join('third_party', 'yara'),
     os.path.join('third_party', 'yasm'),
     os.path.join('v8', 'strongtalk'),
 ])
diff --git a/tools/mac/dump-static-initializers.py b/tools/mac/dump-static-initializers.py
index 339cd44..061cf523 100755
--- a/tools/mac/dump-static-initializers.py
+++ b/tools/mac/dump-static-initializers.py
@@ -39,7 +39,8 @@
   static initializers.
   """
 
-  child = subprocess.Popen(['dsymutil', '-s', binary], stdout=subprocess.PIPE)
+  child = subprocess.Popen(['tools/clang/dsymutil/bin/dsymutil', '-s', binary],
+     stdout=subprocess.PIPE)
   for line in child.stdout:
     file_match = dsymutil_file_re.search(line)
     if file_match:
diff --git a/tools/mb/mb_config.pyl b/tools/mb/mb_config.pyl
index 998593c2..94276ce 100644
--- a/tools/mb/mb_config.pyl
+++ b/tools/mb/mb_config.pyl
@@ -122,7 +122,6 @@
       'CFI Linux ToT': 'clang_tot_cfi_full_cfi_diag_thin_lto_release_static_dcheck_always_on',
       'CrWinAsan': 'asan_clang_fuzzer_static_v8_heap_minimal_symbols_release_tot',
       'CrWinAsan(dll)': 'asan_clang_shared_v8_heap_minimal_symbols_release_tot',
-      'CrWinAsanCov': 'asan_clang_edge_fuzzer_static_v8_heap_minimal_symbols_release_tot',
 
       'linux-win_cross-rel': 'clang_tot_win_release_cross',
       'ToTAndroid': 'android_clang_tot_release_minimal_symbols',
@@ -1048,10 +1047,6 @@
       'android_without_codecs', 'release_trybot', 'minimal_symbols', 'strip_debug_info',
     ],
 
-    'asan_clang_edge_fuzzer_static_v8_heap_minimal_symbols_release_tot': [
-      'asan', 'clang_tot', 'edge', 'fuzzer', 'static', 'v8_heap', 'minimal_symbols', 'release',
-    ],
-
     'asan_clang_shared_v8_heap_minimal_symbols_release_tot': [
       'asan', 'clang_tot', 'shared', 'v8_heap', 'minimal_symbols', 'release',
     ],
@@ -1842,7 +1837,7 @@
     ],
 
     'win32_arm64_release_bot': [
-      'arm64', 'disable_nacl', 'release_bot',
+      'arm64', 'disable_nacl', 'minimal_symbols', 'release_bot',
     ],
 
     'clang_tot_win_release_cross': [
diff --git a/tools/metrics/histograms/enums.xml b/tools/metrics/histograms/enums.xml
index b805324..4c5e61c 100644
--- a/tools/metrics/histograms/enums.xml
+++ b/tools/metrics/histograms/enums.xml
@@ -15599,6 +15599,7 @@
   <int value="530" label="BrowserSwitcherChromePath"/>
   <int value="531" label="BrowserSwitcherChromeParameters"/>
   <int value="532" label="DeviceWilcoDtcAllowed"/>
+  <int value="533" label="AllowPopupsDuringPageUnload"/>
 </enum>
 
 <enum name="EnterprisePolicyInvalidations">
@@ -16999,7 +17000,7 @@
   <int value="294" label="SOCKET_READ"/>
   <int value="295" label="DELETED_EXPERIMENTAL_PROCESSES_TERMINATE"/>
   <int value="296" label="METRICSPRIVATE_RECORDTIME"/>
-  <int value="297" label="BOOKMARKMANAGERPRIVATE_GETSTRINGS"/>
+  <int value="297" label="DELETED_BOOKMARKMANAGERPRIVATE_GETSTRINGS"/>
   <int value="298" label="USB_ISOCHRONOUSTRANSFER"/>
   <int value="299" label="PERMISSIONS_REMOVE"/>
   <int value="300" label="MANAGEMENT_UNINSTALL"/>
@@ -21781,6 +21782,10 @@
   <int value="2817" label="CSSValueAppearanceProgressBarForOthersRendered"/>
   <int value="2818" label="CSSValueAppearancePushButton"/>
   <int value="2819" label="CSSValueAppearanceSquareButton"/>
+  <int value="2820" label="CSSValueAppearanceSearchCancel"/>
+  <int value="2821" label="CSSValueAppearanceTextarea"/>
+  <int value="2822" label="CSSValueAppearanceTextFieldForOthersRendered"/>
+  <int value="2823" label="CSSValueAppearanceTextFieldForTemporalRendered"/>
 </enum>
 
 <enum name="FeaturePolicyFeature">
@@ -30904,6 +30909,7 @@
   <int value="-2098243118" label="OmniboxTailSuggestions:disabled"/>
   <int value="-2097895488" label="NotificationScrollBar:enabled"/>
   <int value="-2097515669" label="disable-cast"/>
+  <int value="-2092067251" label="NativeFilesystemAPI:disabled"/>
   <int value="-2091404586" label="TabStripKeyboardFocus:enabled"/>
   <int value="-2090484194" label="ContextualSearchUrlActions:disabled"/>
   <int value="-2088472280" label="KeyboardShortcutViewerApp:disabled"/>
@@ -30929,6 +30935,7 @@
       label="AutofillSaveCreditCardUsesStrikeSystemV2:disabled"/>
   <int value="-2059771509" label="NTPTilesLowerResolutionFavicons:disabled"/>
   <int value="-2058656447" label="ContextualSearchUrlActions:enabled"/>
+  <int value="-2054612904" label="BuiltInModuleInfra:enabled"/>
   <int value="-2053860791" label="XGEOVisibleNetworks:enabled"/>
   <int value="-2048927838" label="AutoplayWhitelistSettings:enabled"/>
   <int value="-2048732429" label="enable-alternative-services"/>
@@ -31128,6 +31135,7 @@
   <int value="-1772942854" label="LongPressBackForHistory:enabled"/>
   <int value="-1772172557" label="enable-osk-overscroll"/>
   <int value="-1768672408" label="ChromeDuplex:disabled"/>
+  <int value="-1768308156" label="OmniboxDedupeGoogleDriveURLs:enabled"/>
   <int value="-1767470652" label="out-of-process-pdf"/>
   <int value="-1766129470" label="DataSaverLiteModeRebranding:disabled"/>
   <int value="-1758468685" label="DownloadHomeV2:disabled"/>
@@ -31355,6 +31363,7 @@
   <int value="-1416758392" label="migrate-linux-to-logindb:disabled"/>
   <int value="-1416754663" label="enable-mac-views-native-app-windows"/>
   <int value="-1416184931" label="TranslateRankerEnforcement:enabled"/>
+  <int value="-1411733990" label="OmniboxDedupeGoogleDriveURLs:disabled"/>
   <int value="-1411003295" label="disable-encrypted-media"/>
   <int value="-1409643943" label="enable-child-account-detection"/>
   <int value="-1408869905"
@@ -31537,6 +31546,7 @@
   <int value="-1144501989" label="UserActivityPredictionMlService:disabled"/>
   <int value="-1143496217" label="enable-oop-rasterization"/>
   <int value="-1143007275" label="EnableNewStyleLauncher:disabled"/>
+  <int value="-1142034548" label="BuiltInModuleKvStorage:enabled"/>
   <int value="-1137696948" label="enable-chromeos-account-manager"/>
   <int value="-1137442543" label="enable-slimming-paint"/>
   <int value="-1136627751" label="ignore-autocomplete-off-autofill"/>
@@ -31984,6 +31994,7 @@
   <int value="-367474066" label="DialogTouchBar:enabled"/>
   <int value="-366949535" label="KeyboardShortcutViewerApp:enabled"/>
   <int value="-365920680" label="ImageCaptureAPI:disabled"/>
+  <int value="-365806433" label="BuiltInModuleAll:disabled"/>
   <int value="-364587218" label="ResourceLoadScheduler:enabled"/>
   <int value="-364325011" label="enable-files-quick-view"/>
   <int value="-364267715" label="disable-native-cups"/>
@@ -32090,6 +32101,7 @@
   <int value="-183246373" label="enable-multilingual-spellchecker"/>
   <int value="-181590721"
       label="AutofillEnforceMinRequiredFieldsForHeuristics:enabled"/>
+  <int value="-181279574" label="NativeFilesystemAPI:enabled"/>
   <int value="-181093956" label="ScrollAnchoring:enabled"/>
   <int value="-174706795"
       label="WebPaymentsPerMethodCanMakePaymentQuota:enabled"/>
@@ -32142,6 +32154,7 @@
   <int value="-99781021" label="disable-roboto-font-ui"/>
   <int value="-97145978" label="DesktopPWAsStayInWindow:enabled"/>
   <int value="-92116820" label="InlineUpdateFlow:enabled"/>
+  <int value="-90032223" label="BuiltInModuleInfra:disabled"/>
   <int value="-89690053" label="MaterialDesignUserManager:enabled"/>
   <int value="-88822940" label="ssl-version-min"/>
   <int value="-88273414" label="ContentSuggestionsShowSummary:enabled"/>
@@ -32495,6 +32508,7 @@
   <int value="513356954" label="InstantTethering:disabled"/>
   <int value="513372959" label="ViewsProfileChooser:enabled"/>
   <int value="517568645" label="AnimatedAppMenuIcon:disabled"/>
+  <int value="520982116" label="BuiltInModuleAll:enabled"/>
   <int value="530828403" label="AllowStartingServiceManagerOnly:disabled"/>
   <int value="533064367" label="WebRtcHideLocalIpsWithMdns:disabled"/>
   <int value="535131384" label="OmniboxTailSuggestions:enabled"/>
@@ -33343,6 +33357,7 @@
   <int value="1913263516" label="OculusVR:enabled"/>
   <int value="1913298816" label="OverlayScrollbar:enabled"/>
   <int value="1913926782" label="ChromeModernAlternateCardLayout:disabled"/>
+  <int value="1915028326" label="BuiltInModuleKvStorage:disabled"/>
   <int value="1915178511" label="disable-blink-features"/>
   <int value="1918984253"
       label="OmniboxUIExperimentBlueSearchLoopAndSearchQuery:disabled"/>
@@ -37216,6 +37231,7 @@
   <int value="7" label="Discarded: unsampled failure"/>
   <int value="8" label="Queued"/>
   <int value="9" label="Discarded: include_subdomains without DNS error"/>
+  <int value="10" label="Discarded: IP address mismatch"/>
 </enum>
 
 <enum name="NetPreconnectUtilization">
@@ -46120,7 +46136,7 @@
   <int value="8" label="FileThread"/>
   <int value="9" label="DatabaseThread"/>
   <int value="10" label="WebAudioThread"/>
-  <int value="11" label="ScriptStreamerThread"/>
+  <int value="11" label="ScriptStreamerThread (obsolete)"/>
   <int value="12" label="OfflineAudioRenderThread"/>
   <int value="13" label="ReverbConvolutionBackgroundThread"/>
   <int value="14" label="HRTFDatabaseLoaderThread"/>
diff --git a/tools/metrics/histograms/histograms.xml b/tools/metrics/histograms/histograms.xml
index f660f03c..7fd239bd 100644
--- a/tools/metrics/histograms/histograms.xml
+++ b/tools/metrics/histograms/histograms.xml
@@ -1756,6 +1756,16 @@
   </summary>
 </histogram>
 
+<histogram name="Android.DownloadManager.Chips.Enabled" units="chips"
+    expires_after="2020-01-30">
+  <owner>shaktisahu@chromium.org</owner>
+  <owner>clank-downloads@google.com</owner>
+  <summary>
+    Records the number of chips being shown on download home. Recorded during
+    initialization and also during any list change.
+  </summary>
+</histogram>
+
 <histogram name="Android.DownloadManager.Filter"
     enum="AndroidDownloadFilterType">
   <owner>dtrainor@chromium.org</owner>
@@ -2012,6 +2022,20 @@
   <summary>The percentage of total storage downloads consume.</summary>
 </histogram>
 
+<histogram base="true"
+    name="Android.DownloadManager.Thumbnail.MaxRequiredStretch" units="%"
+    expires_after="2020-01-30">
+<!-- Name completed by histogram_suffixes
+     name="AndroidDownloadTypes" -->
+
+  <owner>shaktisahu@chromium.org</owner>
+  <owner>clank-downloads@google.com</owner>
+  <summary>
+    Records the required stretch of the smaller dimension before displaying an
+    image on download home. Measured in percentage.
+  </summary>
+</histogram>
+
 <histogram name="Android.DownloadManager.ViewRetentionTime.Audio"
     units="minutes" expires_after="2020-01-30">
   <owner>xingliu@chromium.org</owner>
@@ -2991,6 +3015,51 @@
   <summary>The number of bytes written for the tab metadata file.</summary>
 </histogram>
 
+<histogram name="Android.ThumbnailDiskStorage.CachedBitmap.Found"
+    enum="BooleanFound" expires_after="2020-01-30">
+  <owner>shaktisahu@chromium.org</owner>
+  <owner>clank-downloads@google.com</owner>
+  <summary>
+    Records the status of a thumbnail query from the thumbnail disk storage.
+  </summary>
+</histogram>
+
+<histogram name="Android.ThumbnailDiskStorage.Size" units="KB"
+    expires_after="2020-01-30">
+  <owner>shaktisahu@chromium.org</owner>
+  <owner>clank-downloads@google.com</owner>
+  <summary>
+    Measures the size of the thumbnail disk storage used. Recorded during
+    initialization.
+  </summary>
+</histogram>
+
+<histogram base="true" name="Android.ThumbnailProvider.BitmapCache.Size"
+    units="KB" expires_after="2020-01-30">
+<!-- Name completed by histogram_suffixes
+     name="ThumbnailProvider.ClientType" -->
+
+  <owner>shaktisahu@chromium.org</owner>
+  <owner>clank-downloads@google.com</owner>
+  <summary>
+    Measures the size of the bitmap cache used. Recorded whenever a bitmap is
+    added to the cache.
+  </summary>
+</histogram>
+
+<histogram base="true" name="Android.ThumbnailProvider.CachedBitmap.Found"
+    enum="BooleanFound" expires_after="2020-01-30">
+<!-- Name completed by histogram_suffixes
+     name="ThumbnailProvider.ClientType" -->
+
+  <owner>shaktisahu@chromium.org</owner>
+  <owner>clank-downloads@google.com</owner>
+  <summary>
+    Records the status of a thumbnail query from the thumbnail provider
+    in-memory cache.
+  </summary>
+</histogram>
+
 <histogram name="Android.WebView.LoadDataWithBaseUrl.BaseUrl"
     enum="WebViewUrlScheme" expires_after="2019-05-05">
   <owner>ntfschr@chromium.org</owner>
@@ -8947,6 +9016,17 @@
   </summary>
 </histogram>
 
+<histogram
+    name="Autofill.StrikeDatabase.StrikesPresentWhenLocalCardMigrationAccepted"
+    units="strikes" expires_after="2020-03-18">
+  <owner>jsaul@google.com</owner>
+  <owner>annelim@google.com</owner>
+  <summary>
+    Records the number of &quot;strikes&quot; a user has when local card
+    migration is accepted.
+  </summary>
+</histogram>
+
 <histogram name="Autofill.StrikeDatabase.StrikesPresentWhenLocalCardSaved"
     units="strikes" expires_after="2020-03-18">
   <owner>jsaul@google.com</owner>
@@ -8967,6 +9047,16 @@
   </summary>
 </histogram>
 
+<histogram name="Autofill.StrikeDatabase.StrikesPresentWhenStrikeExpired"
+    units="strikes" expires_after="2020-03-18">
+  <owner>jsaul@google.com</owner>
+  <owner>annelim@google.com</owner>
+  <summary>
+    Records the number of &quot;strikes&quot; present in a StrikeDatabase entry
+    when a strike expires.
+  </summary>
+</histogram>
+
 <histogram name="Autofill.SubmittedCardState" enum="AutofillSubmittedCardState">
   <owner>dlkumar@google.com</owner>
   <summary>
@@ -11448,14 +11538,14 @@
 </histogram>
 
 <histogram name="Blink.ResourceFetcher.StaleWhileRevalidate"
-    enum="BooleanAttempted" expires_after="M74">
+    enum="BooleanAttempted" expires_after="M77">
   <owner>dtapuska@chromium.org</owner>
   <owner>kenjibaheux@google.com</owner>
   <summary>Count of resources attempted Stale Revalidation.</summary>
 </histogram>
 
 <histogram name="Blink.ResourceFetcher.StaleWhileRevalidateDuration" units="ms"
-    expires_after="M74">
+    expires_after="M77">
   <owner>dtapuska@chromium.org</owner>
   <owner>kenjibaheux@google.com</owner>
   <summary>Duration of completed stale revalidation attempts.</summary>
@@ -16972,6 +17062,49 @@
   </summary>
 </histogram>
 
+<histogram name="ContentCapture.CaptureContentDelayTime" units="ms">
+  <owner>michaelbai@chromium.org</owner>
+  <summary>
+    The time between a change in content and when the new capture is sent to the
+    browser process.
+  </summary>
+  <details>
+    A specific content change is hard to track. This is roughly calculated as
+    the interval from the first content change after a prior capturing content
+    to the first content being sent after the next capture of content.
+  </details>
+</histogram>
+
+<histogram name="ContentCapture.CaptureContentTime" units="microseconds">
+  <owner>michaelbai@chromium.org</owner>
+  <summary>The time taken to capture the on-screen content.</summary>
+</histogram>
+
+<histogram name="ContentCapture.CaptureOneContentTime" units="microseconds"
+    expires_after="M75">
+  <owner>michaelbai@chromium.org</owner>
+  <summary>The estimated time taken to capture one on-screen content.</summary>
+</histogram>
+
+<histogram name="ContentCapture.GetBoundingBox" units="microseconds">
+  <owner>michaelbai@chromium.org</owner>
+  <summary>
+    The time taken to get the bounding box of the batch of captured content.
+  </summary>
+</histogram>
+
+<histogram name="ContentCapture.SendContentTime" units="microseconds">
+  <owner>michaelbai@chromium.org</owner>
+  <summary>
+    The time taken to send the content to the browser process in batch.
+  </summary>
+</histogram>
+
+<histogram name="ContentCapture.SentContentCount" units="count">
+  <owner>michaelbai@chromium.org</owner>
+  <summary>The total number of content captures sent for a document.</summary>
+</histogram>
+
 <histogram name="ContentSettings.DefaultAutoplaySetting" enum="ContentSetting">
   <owner>mlamouri@chromium.org</owner>
   <summary>The default autoplay setting at profile open.</summary>
@@ -53216,6 +53349,18 @@
   </summary>
 </histogram>
 
+<histogram name="Memory.NativeLibrary.MappedAndResidentMemoryFootprint"
+    units="KB" expires_after="2019-07-01">
+  <owner>msalama@google.com</owner>
+  <owner>lizeb@chromium.org</owner>
+  <summary>
+    The size of the resident memory for the native library code across all
+    processes. This metric is computed by parsing proc/self/pagemap and counting
+    native library pages that are mapped and present in RAM for at least one
+    Chrome process. Recorded once per UMA ping. Available only on Android.
+  </summary>
+</histogram>
+
 <histogram name="Memory.OOMKill.Contents.MemAllocatedMB" units="MB">
   <owner>oshima@chromium.org</owner>
   <summary>
@@ -62191,9 +62336,25 @@
   <owner>chlily@chromium.org</owner>
   <summary>
     When Network Error Logging observes a completed request that might generate
-    a report, what happens to it. NEL observes all requests, since it can send
-    reports for a fraction of successful requests as well so error rates can be
-    calculated. Insecure requests are discarded for having an insecure origin,
+    a report, what happens to it. NEL observes all requests, both successful and
+    unsuccessful. It sends reports for successful and unsuccessful requests at
+    specified sampling rates so that error rates can be calculated. Insecure
+    requests are discarded for having an insecure origin regardless of existence
+    of a policy for the origin.
+  </summary>
+</histogram>
+
+<histogram name="Net.NetworkErrorLogging.SignedExchangeRequestOutcome"
+    enum="NetNetworkErrorLoggingRequestOutcome" expires_after="2020-02-28">
+  <owner>horo@chromium.org</owner>
+  <owner>ksakamoto@chromium.org</owner>
+  <owner>kouhei@chromium.org</owner>
+  <summary>
+    When Network Error Logging observes a completed request of signed exchange
+    that might generate a report, what happens to it. NEL observes all requests,
+    both successful and unsuccessful. It sends reports for successful and
+    unsuccessful requests at specified sampling rates so that error rates can be
+    calculated. Insecure requests are discarded for having an insecure origin
     regardless of existence of a policy for the origin.
   </summary>
 </histogram>
@@ -81451,7 +81612,7 @@
 </histogram>
 
 <histogram name="PasswordManager.ButtonTitlePerformance.HasFormTag"
-    expires_after="2019-03-01">
+    expires_after="2019-07-01">
   <owner>kolos@chromium.org</owner>
   <summary>
     The time (ms) it takes to infer button titles within a form tag.
@@ -81459,7 +81620,7 @@
 </histogram>
 
 <histogram name="PasswordManager.ButtonTitlePerformance.NoFormTag"
-    expires_after="2019-03-01">
+    expires_after="2019-07-01">
   <owner>kolos@chromium.org</owner>
   <summary>
     The time (ms) it takes to infer button titles outside of form tags.
@@ -82747,7 +82908,7 @@
 </histogram>
 
 <histogram name="PasswordManager.SuccessfulLoginHappened"
-    enum="BooleanSuccessfulLoginHappenedOnHttps" expires_after="M74">
+    enum="BooleanSuccessfulLoginHappenedOnHttps" expires_after="M78">
   <owner>nepper@chromium.org</owner>
   <owner>battre@chromium.org</owner>
   <owner>kolos@chromium.org</owner>
@@ -90001,6 +90162,9 @@
 </histogram>
 
 <histogram name="Prerender.PerceivedTTFCPRecorded" enum="BooleanRecorded">
+  <obsolete>
+    Obsoleted in Feb 2019.
+  </obsolete>
   <owner>droger@chromium.org</owner>
   <owner>mattcary@chromium.org</owner>
   <owner>pasko@chromium.org</owner>
@@ -133923,6 +134087,8 @@
   <suffix name="Video"/>
   <affected-histogram name="Android.DownloadManager.InitialCount"/>
   <affected-histogram name="Android.DownloadManager.InitialCount.Viewed"/>
+  <affected-histogram
+      name="Android.DownloadManager.Thumbnail.MaxRequiredStretch"/>
 </histogram_suffixes>
 
 <histogram_suffixes name="AndroidFeatureModuleName" separator=".">
@@ -134310,6 +134476,13 @@
   <affected-histogram name="Autofill.StoredServerCreditCardCount"/>
 </histogram_suffixes>
 
+<histogram_suffixes name="AutofillStrikesExpiredProjectType" separator=".">
+  <suffix name="CreditCardSave" label="Credit card save"/>
+  <suffix name="LocalCardMigration" label="Local card migration"/>
+  <affected-histogram
+      name="Autofill.StrikeDatabase.StrikesPresentWhenStrikeExpired"/>
+</histogram_suffixes>
+
 <histogram_suffixes name="AutofillSyncState" separator=".">
   <suffix name="SignedIn" label="Signed in"/>
   <suffix name="SignedInAndSyncFeature" label="Signed in and sync feature"/>
@@ -146821,6 +146994,13 @@
   <affected-histogram name="Thumbnails.CaptureOutcome"/>
 </histogram_suffixes>
 
+<histogram_suffixes name="ThumbnailProvider.ClientType" separator=".">
+  <suffix name="DownloadHome" label="Download home"/>
+  <suffix name="NTPSnippets" label="NTP snippets"/>
+  <affected-histogram name="Android.ThumbnailProvider.BitmapCache.Size"/>
+  <affected-histogram name="Android.ThumbnailProvider.CachedBitmap.Found"/>
+</histogram_suffixes>
+
 <histogram_suffixes name="TimestampTimebaseProcess" separator=".">
   <obsolete>
     Deprecated as of 11/2017 in Issue 650338. Using a DCHECK instead.
diff --git a/tools/perf/cli_tools/flakiness_cli/analysis.py b/tools/perf/cli_tools/flakiness_cli/analysis.py
index 7c33e61..e6941f3 100644
--- a/tools/perf/cli_tools/flakiness_cli/analysis.py
+++ b/tools/perf/cli_tools/flakiness_cli/analysis.py
@@ -4,11 +4,8 @@
 
 import fnmatch
 
-import numpy  # pylint: disable=import-error
-try:
-  import pandas  # pylint: disable=import-error
-except ImportError:
-  pass
+from core.external_modules import numpy
+from core.external_modules import pandas
 
 
 SECONDS_IN_A_DAY = 60 * 60 * 24
diff --git a/tools/perf/cli_tools/flakiness_cli/analysis_unittest.py b/tools/perf/cli_tools/flakiness_cli/analysis_unittest.py
index 8322a16..5a5627f 100644
--- a/tools/perf/cli_tools/flakiness_cli/analysis_unittest.py
+++ b/tools/perf/cli_tools/flakiness_cli/analysis_unittest.py
@@ -4,13 +4,13 @@
 
 import unittest
 
-from telemetry import decorators
 from cli_tools.flakiness_cli import analysis
 from cli_tools.flakiness_cli import frames
+from core.external_modules import pandas
 
 
+@unittest.skipIf(pandas is None, 'pandas not available')
 class TestAnalysis(unittest.TestCase):
-  @decorators.Disabled('chromeos')  # crbug.com/921762
   def testFilterBy(self):
     builders = frames.pandas.DataFrame.from_records([
       ['chromium.perf', 'my-mac-bot', 'common_tests'],
diff --git a/tools/perf/cli_tools/flakiness_cli/core.py b/tools/perf/cli_tools/flakiness_cli/cached_api.py
similarity index 100%
rename from tools/perf/cli_tools/flakiness_cli/core.py
rename to tools/perf/cli_tools/flakiness_cli/cached_api.py
diff --git a/tools/perf/cli_tools/flakiness_cli/frames.py b/tools/perf/cli_tools/flakiness_cli/frames.py
index 1b949eb..f8ca832a 100644
--- a/tools/perf/cli_tools/flakiness_cli/frames.py
+++ b/tools/perf/cli_tools/flakiness_cli/frames.py
@@ -7,10 +7,7 @@
 import datetime
 import os
 
-try:
-  import pandas  # pylint: disable=import-error
-except ImportError:
-  pass
+from core.external_modules import pandas
 
 
 CACHE_DIR = os.path.abspath(os.path.join(
diff --git a/tools/perf/cli_tools/flakiness_cli/frames_unittest.py b/tools/perf/cli_tools/flakiness_cli/frames_unittest.py
index 7df46bb..d671e23 100644
--- a/tools/perf/cli_tools/flakiness_cli/frames_unittest.py
+++ b/tools/perf/cli_tools/flakiness_cli/frames_unittest.py
@@ -10,12 +10,12 @@
 
 import mock
 
-from telemetry import decorators
 from cli_tools.flakiness_cli import frames
+from core.external_modules import pandas
 
 
+@unittest.skipIf(pandas is None, 'pandas not available')
 class TestDataFrames(unittest.TestCase):
-  @decorators.Disabled('chromeos')  # crbug.com/921762
   def testBuildersDataFrame(self):
     sample_data = {
         'masters': [
@@ -109,7 +109,6 @@
     self.assertItemsEqual(
         list(frames._IterTestResults(tests_dict)), expected)
 
-  @decorators.Disabled('chromeos')  # crbug.com/921762
   def testTestResultsDataFrame(self):
     data = {
         'android-bot': {
@@ -154,7 +153,6 @@
     self.assertEqual(len(selection), 1)
     self.assertTrue(selection.iloc[0]['result'], 'N')
 
-  @decorators.Disabled('chromeos')  # crbug.com/921762
   def testTestResultsDataFrame_empty(self):
     data = {
         'android-bot': {
@@ -181,7 +179,6 @@
     with self.assertRaises(AssertionError):
       frames.TestResultsDataFrame(data)
 
-  @decorators.Disabled('chromeos')  # crbug.com/921762
   def testGetWithCache(self):
     def make_frame_1():
       # test_2 was failing.
diff --git a/tools/perf/cli_tools/flakiness_cli/main.py b/tools/perf/cli_tools/flakiness_cli/main.py
index 3b14aff1..6b5db33 100644
--- a/tools/perf/cli_tools/flakiness_cli/main.py
+++ b/tools/perf/cli_tools/flakiness_cli/main.py
@@ -7,7 +7,7 @@
 import argparse
 
 from cli_tools.flakiness_cli import analysis
-from cli_tools.flakiness_cli import core
+from cli_tools.flakiness_cli import cached_api
 
 
 def Main():
@@ -32,7 +32,7 @@
       ' with flakiness above this level.')
   args = parser.parse_args()
 
-  configs = core.GetBuilders()
+  configs = cached_api.GetBuilders()
   configs = analysis.FilterBy(configs, master=args.master,
                               builder=args.builder, test_type=args.test_type)
   if configs.empty:
@@ -40,7 +40,7 @@
 
   dfs = []
   for row in configs.itertuples():
-    df = core.GetTestResults(row.master, row.builder, row.test_type)
+    df = cached_api.GetTestResults(row.master, row.builder, row.test_type)
     df = analysis.FilterBy(df, test_suite=args.test_suite)
     if df.empty:
       continue
diff --git a/tools/perf/core/cli_helpers.py b/tools/perf/core/cli_helpers.py
index f23a984c..54d2cdd 100644
--- a/tools/perf/core/cli_helpers.py
+++ b/tools/perf/core/cli_helpers.py
@@ -80,7 +80,9 @@
     answers: List or dictinary describing user choices. In case of a dictionary,
         the keys are the options display to the user and values are the return
         values for this method. In case of a list, returned values are same as
-        options displayed to the user. Defaults to {'yes': True, 'no', False}.
+        options displayed to the user. When presenting to the user, the order of
+        answers is preserved if list is used, otherwise answers are sorted
+        alphabetically. Defaults to {'yes': True, 'no': False}.
     default: Default option chosen on empty answer. Defaults to 'yes' if default
         value is used for answers parameter or to lack of default answer
         otherwise.
@@ -93,7 +95,10 @@
     answers = {'yes': True, 'no': False}
     default = 'yes'
   if isinstance(answers, list):
+    ordered_answers = answers
     answers = {v: v for v in answers}
+  else:
+    ordered_answers = sorted(answers)
 
   # Generate a set of prefixes for all answers such that the user can type just
   # the minimum number of characters required, e.g. 'y' or 'ye' can be used for
@@ -112,10 +117,9 @@
         inputs[inp] = retval
 
   if default is None:
-    prompt = ' [%s] ' % '/'.join(answers)
+    prompt = ' [%s] ' % '/'.join(ordered_answers)
   elif default in answers:
-    ans_with_def = [a if a != default else a.upper()
-                    for a in sorted(answers.keys())]
+    ans_with_def = (a if a != default else a.upper() for a in ordered_answers)
     prompt = ' [%s] ' % '/'.join(ans_with_def)
   else:
     raise ValueError('invalid default answer: "%s"' % default)
diff --git a/tools/perf/core/cli_helpers_unittest.py b/tools/perf/core/cli_helpers_unittest.py
index 1b3b02d..36a07dc6 100644
--- a/tools/perf/core/cli_helpers_unittest.py
+++ b/tools/perf/core/cli_helpers_unittest.py
@@ -50,6 +50,8 @@
 
   @mock.patch('__builtin__.print')
   @mock.patch('__builtin__.raw_input')
+  # https://crbug.com/938575.
+  @decorators.Disabled('chromeos')
   def testAskAgainOnInvalidAnswer(self, raw_input_mock, print_mock):
     raw_input_mock.side_effect = ['foobar', 'y']
     self.assertTrue(cli_helpers.Ask('Ready?'))
@@ -61,6 +63,8 @@
 
   @mock.patch('__builtin__.print')
   @mock.patch('__builtin__.raw_input')
+  # https://crbug.com/938575.
+  @decorators.Disabled('chromeos')
   def testAskWithCustomAnswersAndDefault(self, raw_input_mock, print_mock):
     raw_input_mock.side_effect = ['']
     self.assertFalse(
@@ -70,6 +74,8 @@
 
   @mock.patch('__builtin__.print')
   @mock.patch('__builtin__.raw_input')
+  # https://crbug.com/938575.
+  @decorators.Disabled('chromeos')
   def testAskNoDefaultCustomAnswersAsList(self, raw_input_mock, print_mock):
     raw_input_mock.side_effect = ['', 'FoO']
     self.assertEqual(cli_helpers.Ask('Ready?', ['foo', 'bar']), 'foo')
diff --git a/tools/perf/core/external_modules.py b/tools/perf/core/external_modules.py
new file mode 100644
index 0000000..4bb59731
--- /dev/null
+++ b/tools/perf/core/external_modules.py
@@ -0,0 +1,59 @@
+# Copyright 2019 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+"""Allow importing external modules which may be missing in some platforms.
+
+These modules are normally provided by the vpython environment manager. But
+some platforms, e.g. CromeOs, do not have access to this facility.
+
+To be safe, instead of e.g.:
+
+    import pandas
+
+clients should do:
+
+    from core.external_modules import pandas
+
+Tests that require pandas to work can be skipped as follows:
+
+    from core.external_modules import pandas
+
+
+    @unittest.skipIf(pandas is None, 'pandas not available')
+    class TestsForMyModule(unittest.TestCase):
+      def testSomeBehavior(self):
+        # test some behavior that requires pandas module.
+
+Finally, scripts that to work properly require any of these external
+dependencies should call:
+
+    from core import external_modules
+
+    if __name__ == '__main__':
+      external_modules.RequireModules()
+      # the rest of your script here.
+
+to exit early with a suitable error message if the dependencies are not
+satisfied.
+"""
+
+import sys
+
+try:
+  import numpy  # pylint: disable=import-error
+except ImportError:
+  numpy = None
+
+try:
+  import pandas  # pylint: disable=import-error
+except ImportError:
+  pandas = None
+
+
+def RequireModules():
+  if numpy is None or pandas is None:
+    sys.exit(
+        'ERROR: Some required python modules are not available.\n\n'
+        'Make sure to run this script using vpython or ensure that '
+        'module dependencies listed in src/.vpython are satisfied.')
diff --git a/tools/perf/core/perf_data_generator.py b/tools/perf/core/perf_data_generator.py
index ea6f73e5..0305318 100755
--- a/tools/perf/core/perf_data_generator.py
+++ b/tools/perf/core/perf_data_generator.py
@@ -892,6 +892,7 @@
 
 def add_common_test_properties(test_entry):
   test_entry['trigger_script'] = {
+      'requires_simultaneous_shard_dispatch': True,
       'script': '//testing/trigger_scripts/perf_device_trigger.py',
       'args': [
           '--multiple-dimension-script-verbose',
diff --git a/tools/perf/core/perf_data_generator_unittest.py b/tools/perf/core/perf_data_generator_unittest.py
index 00009d3..3f326e7 100644
--- a/tools/perf/core/perf_data_generator_unittest.py
+++ b/tools/perf/core/perf_data_generator_unittest.py
@@ -78,6 +78,7 @@
             '--multiple-dimension-script-verbose',
             'True'
           ],
+          'requires_simultaneous_shard_dispatch': True,
           'script': '//testing/trigger_scripts/perf_device_trigger.py'
         },
         'merge': {
@@ -128,6 +129,7 @@
             '--multiple-dimension-script-verbose',
             'True'
           ],
+          'requires_simultaneous_shard_dispatch': True,
           'script': '//testing/trigger_scripts/perf_device_trigger.py'
         },
         'merge': {
@@ -176,6 +178,7 @@
             '--multiple-dimension-script-verbose',
             'True'
           ],
+          'requires_simultaneous_shard_dispatch': True,
           'script': '//testing/trigger_scripts/perf_device_trigger.py'
         },
         'merge': {
diff --git a/tools/perf/flakiness_cli b/tools/perf/flakiness_cli
index 8cdc846..86f4483 100755
--- a/tools/perf/flakiness_cli
+++ b/tools/perf/flakiness_cli
@@ -6,8 +6,11 @@
 """This tool provides a command line interface for the flakiness dashboard."""
 
 import sys
+
+from core import external_modules
 from cli_tools import flakiness_cli
 
 
 if __name__ == '__main__':
+  external_modules.RequireModules()
   sys.exit(flakiness_cli.Main())
diff --git a/tools/traffic_annotation/auditor/BUILD.gn b/tools/traffic_annotation/auditor/BUILD.gn
index 6e2e467..5f01c32 100644
--- a/tools/traffic_annotation/auditor/BUILD.gn
+++ b/tools/traffic_annotation/auditor/BUILD.gn
@@ -22,10 +22,7 @@
 
   use_protobuf_full = true
 
-  deps = [
-    "//components/policy:full_runtime_code_generate",
-    "//third_party/protobuf:protobuf_full",
-  ]
+  proto_deps = [ "//components/policy:full_runtime_code_generate" ]
 }
 
 proto_library("traffic_annotation") {
@@ -35,9 +32,9 @@
 
   import_dirs = [ "$root_gen_dir" + "/components/policy/proto" ]
 
-  deps = [
-    ":chrome_settings_full_runtime",
-  ]
+  proto_deps = [ "//components/policy:full_runtime_code_generate" ]
+
+  link_deps = [ ":chrome_settings_full_runtime" ]
 
   use_protobuf_full = true
 }
diff --git a/tools/traffic_annotation/summary/annotations.xml b/tools/traffic_annotation/summary/annotations.xml
index 74098bde..ce82cb44 100644
--- a/tools/traffic_annotation/summary/annotations.xml
+++ b/tools/traffic_annotation/summary/annotations.xml
@@ -144,8 +144,6 @@
  <item id="lib_address_input" hash_code="50816767" type="0" content_hash_code="57977576" os_list="linux,windows" file_path="third_party/libaddressinput/chromium/chrome_metadata_source.cc"/>
  <item id="logo_service" hash_code="35473769" type="0" content_hash_code="20271299" os_list="linux,windows" file_path="components/search_provider_logos/logo_service_impl.cc"/>
  <item id="logo_tracker" hash_code="36859107" type="0" deprecated="2018-12-07" content_hash_code="67588075" file_path=""/>
- <item id="mdns_client" hash_code="26832436" type="0" content_hash_code="133518607" os_list="linux,windows" file_path="net/dns/mdns_client_impl.cc"/>
- <item id="mdns_responder" hash_code="64387947" type="0" content_hash_code="54201453" os_list="linux,windows" file_path="services/network/mdns_responder.cc"/>
  <item id="metrics_report_ukm" hash_code="727478" type="0" content_hash_code="40919254" os_list="linux,windows" file_path="components/metrics/net/net_metrics_log_uploader.cc"/>
  <item id="metrics_report_uma" hash_code="727528" type="0" content_hash_code="10176197" os_list="linux,windows" file_path="components/metrics/net/net_metrics_log_uploader.cc"/>
  <item id="mirroring_get_setup_info" hash_code="78447809" type="0" content_hash_code="112561099" os_list="linux,windows" file_path="components/mirroring/service/session_monitor.cc"/>
diff --git a/ui/accessibility/ax_tree_update.h b/ui/accessibility/ax_tree_update.h
index 4481fd76..a3c1418 100644
--- a/ui/accessibility/ax_tree_update.h
+++ b/ui/accessibility/ax_tree_update.h
@@ -115,6 +115,26 @@
   return result;
 }
 
+// Two tree updates can be merged into one if the second one
+// doesn't clear a subtree, doesn't have new tree data, and
+// doesn't have a new root id - in other words the second tree
+// update consists of only changes to nodes.
+template <typename AXNodeData, typename AXTreeData>
+bool TreeUpdatesCanBeMerged(
+    const AXTreeUpdateBase<AXNodeData, AXTreeData>& u1,
+    const AXTreeUpdateBase<AXNodeData, AXTreeData>& u2) {
+  if (u2.node_id_to_clear)
+    return false;
+
+  if (u2.has_tree_data && u2.tree_data != u1.tree_data)
+    return false;
+
+  if (u2.root_id != u1.root_id)
+    return false;
+
+  return true;
+}
+
 }  // namespace ui
 
 #endif  // UI_ACCESSIBILITY_AX_TREE_UPDATE_H_
diff --git a/ui/accessibility/platform/ax_platform_node_auralinux.cc b/ui/accessibility/platform/ax_platform_node_auralinux.cc
index 2e1fcd2..08b685c 100644
--- a/ui/accessibility/platform/ax_platform_node_auralinux.cc
+++ b/ui/accessibility/platform/ax_platform_node_auralinux.cc
@@ -31,6 +31,7 @@
 #include "ui/accessibility/platform/atk_util_auralinux.h"
 #include "ui/accessibility/platform/ax_platform_atk_hyperlink.h"
 #include "ui/accessibility/platform/ax_platform_node_delegate.h"
+#include "ui/accessibility/platform/ax_platform_node_delegate_base.h"
 #include "ui/gfx/geometry/rect_conversions.h"
 
 namespace ui {
@@ -1020,8 +1021,19 @@
   // need to convert this input value.
   offset = obj->UnicodeToUTF16OffsetInText(offset);
 
-  std::vector<int32_t> unused_line_start_offsets = std::vector<int32_t>();
   base::string16 text = obj->GetText();
+  AXPlatformNodeDelegate::EnclosingBoundaryOffsets boundaries =
+      obj->GetDelegate()->FindTextBoundariesAtOffset(
+          boundary_type, offset, ax::mojom::TextAffinity::kDownstream);
+  if (boundaries.has_value()) {
+    *start_offset_ptr = obj->UTF16ToUnicodeOffsetInText(boundaries->first);
+    *end_offset_ptr = obj->UTF16ToUnicodeOffsetInText(boundaries->second);
+    base::string16 substr =
+        text.substr(boundaries->first, boundaries->second - boundaries->first);
+    return g_strdup(base::UTF16ToUTF8(substr).c_str());
+  }
+
+  std::vector<int32_t> unused_line_start_offsets;
   size_t start_offset = static_cast<int>(FindAccessibleTextBoundary(
       text, unused_line_start_offsets, boundary_type, offset,
       BACKWARDS_DIRECTION, ax::mojom::TextAffinity::kDownstream));
@@ -1067,7 +1079,7 @@
     case ATK_TEXT_BOUNDARY_SENTENCE_END:
       return base::nullopt;
     case ATK_TEXT_BOUNDARY_LINE_START:
-      return base::nullopt;
+      return LINE_BOUNDARY;
     case ATK_TEXT_BOUNDARY_LINE_END:
       return base::nullopt;
   }
@@ -1086,8 +1098,7 @@
     case ATK_TEXT_GRANULARITY_SENTENCE:
       return SENTENCE_BOUNDARY;
     case ATK_TEXT_GRANULARITY_LINE:
-      return base::nullopt;  // TODO(mrobinson): We need support for line
-                             // granularity.
+      return LINE_BOUNDARY;
     case ATK_TEXT_GRANULARITY_PARAGRAPH:
       return PARAGRAPH_BOUNDARY;
   }
@@ -1157,6 +1168,9 @@
     AtkTextGranularity atk_granularity,
     int* start_offset,
     int* end_offset) {
+  *start_offset = -1;
+  *end_offset = -1;
+
   if (atk_granularity == ATK_TEXT_GRANULARITY_CHAR) {
     return AXPlatformNodeAuraLinuxGetCharacter(atk_text, offset, start_offset,
                                                end_offset);
diff --git a/ui/accessibility/platform/ax_platform_node_delegate.h b/ui/accessibility/platform/ax_platform_node_delegate.h
index 7596667..1ec22a1 100644
--- a/ui/accessibility/platform/ax_platform_node_delegate.h
+++ b/ui/accessibility/platform/ax_platform_node_delegate.h
@@ -6,9 +6,12 @@
 #define UI_ACCESSIBILITY_PLATFORM_AX_PLATFORM_NODE_DELEGATE_H_
 
 #include <set>
+#include <utility>
+#include <vector>
 
 #include "ui/accessibility/ax_enums.mojom.h"
 #include "ui/accessibility/ax_export.h"
+#include "ui/accessibility/ax_text_utils.h"
 #include "ui/accessibility/platform/ax_unique_id.h"
 #include "ui/gfx/geometry/vector2d.h"
 #include "ui/gfx/native_widget_types.h"
@@ -126,6 +129,15 @@
 
   virtual const AXUniqueId& GetUniqueId() const = 0;
 
+  // This method finds text boundaries in the text used for platform text APIs.
+  // Implementations may use side-channel data such as line or word indices to
+  // produce appropriate results.
+  using EnclosingBoundaryOffsets = base::Optional<std::pair<size_t, size_t>>;
+  virtual EnclosingBoundaryOffsets FindTextBoundariesAtOffset(
+      TextBoundaryType boundary_type,
+      int offset,
+      ax::mojom::TextAffinity affinity) const = 0;
+
   //
   // Tables. All of these should be called on a node that's a table-like
   // role.
diff --git a/ui/accessibility/platform/ax_platform_node_delegate_base.cc b/ui/accessibility/platform/ax_platform_node_delegate_base.cc
index bb661c84..91dd3a1 100644
--- a/ui/accessibility/platform/ax_platform_node_delegate_base.cc
+++ b/ui/accessibility/platform/ax_platform_node_delegate_base.cc
@@ -261,6 +261,14 @@
   return *dummy_unique_id;
 }
 
+AXPlatformNodeDelegate::EnclosingBoundaryOffsets
+AXPlatformNodeDelegateBase::FindTextBoundariesAtOffset(
+    TextBoundaryType boundary_type,
+    int offset,
+    ax::mojom::TextAffinity affinity) const {
+  return base::nullopt;
+}
+
 bool AXPlatformNodeDelegateBase::IsOrderedSetItem() const {
   return false;
 }
diff --git a/ui/accessibility/platform/ax_platform_node_delegate_base.h b/ui/accessibility/platform/ax_platform_node_delegate_base.h
index 87117aa..d027544 100644
--- a/ui/accessibility/platform/ax_platform_node_delegate_base.h
+++ b/ui/accessibility/platform/ax_platform_node_delegate_base.h
@@ -104,6 +104,11 @@
 
   const AXUniqueId& GetUniqueId() const override;
 
+  AXPlatformNodeDelegate::EnclosingBoundaryOffsets FindTextBoundariesAtOffset(
+      TextBoundaryType boundary_type,
+      int offset,
+      ax::mojom::TextAffinity affinity) const override;
+
   //
   // Tables. All of these should be called on a node that's a table-like
   // role.
@@ -190,4 +195,4 @@
 
 }  // namespace ui
 
-#endif  // UI_ACCESSIBILITY_PLATFORM_AX_PLATFORM_NODE_DELEGATE_H_
+#endif  // UI_ACCESSIBILITY_PLATFORM_AX_PLATFORM_NODE_DELEGATE_BASE_H_
diff --git a/ui/android/delegated_frame_host_android.cc b/ui/android/delegated_frame_host_android.cc
index 67ebc8c..82c97a31 100644
--- a/ui/android/delegated_frame_host_android.cc
+++ b/ui/android/delegated_frame_host_android.cc
@@ -58,8 +58,7 @@
       client_(client),
       begin_frame_source_(this),
       enable_surface_synchronization_(enable_surface_synchronization),
-      enable_viz_(
-          base::FeatureList::IsEnabled(features::kVizDisplayCompositor)),
+      enable_viz_(features::IsVizDisplayCompositorEnabled()),
       frame_evictor_(std::make_unique<viz::FrameEvictor>(this)) {
   DCHECK(view_);
   DCHECK(client_);
diff --git a/ui/android/java/res/values/colors.xml b/ui/android/java/res/values/colors.xml
index b4f1051..3b66f4d 100644
--- a/ui/android/java/res/values/colors.xml
+++ b/ui/android/java/res/values/colors.xml
@@ -9,7 +9,9 @@
     <!-- Common text colors -->
     <color name="default_text_color">@color/modern_grey_900</color>
     <color name="default_text_color_secondary">@color/modern_grey_700</color>
+    <!-- Text color for non-clickable blue text. -->
     <color name="default_text_color_blue" tools:ignore="UnusedResources">@color/modern_blue_600</color>
+    <!-- Text color for clickable text. -->
     <color name="default_text_color_link">@color/modern_blue_600</color>
     <color name="disabled_text_color">@color/black_alpha_38</color>
 
diff --git a/ui/android/java/src/org/chromium/ui/text/NoUnderlineClickableSpan.java b/ui/android/java/src/org/chromium/ui/text/NoUnderlineClickableSpan.java
index 52898413..62ce287 100644
--- a/ui/android/java/src/org/chromium/ui/text/NoUnderlineClickableSpan.java
+++ b/ui/android/java/src/org/chromium/ui/text/NoUnderlineClickableSpan.java
@@ -4,6 +4,7 @@
 
 package org.chromium.ui.text;
 
+import android.content.res.Resources;
 import android.support.annotation.ColorRes;
 import android.text.TextPaint;
 import android.text.style.ClickableSpan;
@@ -11,7 +12,6 @@
 
 import org.chromium.base.ApiCompatibilityUtils;
 import org.chromium.base.Callback;
-import org.chromium.base.ContextUtils;
 import org.chromium.ui.R;
 
 /**
@@ -21,13 +21,22 @@
     private final int mColor;
     private final Callback<View> mOnClick;
 
-    public NoUnderlineClickableSpan(Callback<View> onClickCallback) {
-        this(R.color.default_text_color_blue, onClickCallback);
+    /**
+     * @param resources The {@link Resources} used for accessing colors.
+     * @param onClickCallback The callback notified when the span is clicked.
+     */
+    public NoUnderlineClickableSpan(Resources resources, Callback<View> onClickCallback) {
+        this(resources, R.color.default_text_color_link, onClickCallback);
     }
 
-    public NoUnderlineClickableSpan(@ColorRes int colorResId, Callback<View> onClickCallback) {
-        mColor = ApiCompatibilityUtils.getColor(
-                ContextUtils.getApplicationContext().getResources(), colorResId);
+    /**
+     * @param resources The {@link Resources} used for accessing colors.
+     * @param colorResId The {@link ColorRes} of this clickable span.
+     * @param onClickCallback The callback notified when the span is clicked.
+     */
+    public NoUnderlineClickableSpan(
+            Resources resources, @ColorRes int colorResId, Callback<View> onClickCallback) {
+        mColor = ApiCompatibilityUtils.getColor(resources, colorResId);
         mOnClick = onClickCallback;
     }
 
diff --git a/ui/base/cocoa/menu_controller_unittest.mm b/ui/base/cocoa/menu_controller_unittest.mm
index a4a0627..c104f9ea 100644
--- a/ui/base/cocoa/menu_controller_unittest.mm
+++ b/ui/base/cocoa/menu_controller_unittest.mm
@@ -18,7 +18,7 @@
 #import "ui/base/test/cocoa_helper.h"
 #include "ui/events/test/cocoa_test_event_utils.h"
 #include "ui/gfx/image/image.h"
-#include "ui/resources/grit/ui_resources.h"
+#include "ui/gfx/image/image_unittest_util.h"
 #include "ui/strings/grit/ui_strings.h"
 
 using base::ASCIIToUTF16;
@@ -553,9 +553,7 @@
   // Now update the item to have a label of "second" and an icon.
   base::string16 second = ASCIIToUTF16("second");
   delegate.SetDynamicLabel(second);
-  const gfx::Image& icon =
-      ResourceBundle::GetSharedInstance().GetNativeImageNamed(
-          IDR_DEFAULT_FAVICON);
+  const gfx::Image& icon = gfx::test::CreateImage(32, 32);
   delegate.SetDynamicIcon(icon);
   // Simulate opening the menu and validate that the item label + icon changes.
   Validate(menu.get(), [menu menu]);
diff --git a/ui/base/win/lock_state.cc b/ui/base/win/lock_state.cc
index c56eae4..e90a8de9 100644
--- a/ui/base/win/lock_state.cc
+++ b/ui/base/win/lock_state.cc
@@ -5,10 +5,41 @@
 #include "ui/base/win/lock_state.h"
 
 #include <windows.h>
+#include <wtsapi32.h>
+
+#include "base/logging.h"
+#include "base/win/windows_version.h"
 
 namespace ui {
 
-bool IsWorkstationLocked() {
+namespace {
+
+// Checks if the current session is locked. Note: This API is only available
+// starting Windows 7 and up. For Windows 7 level1->SessionFlags has inverted
+// logic: https://msdn.microsoft.com/en-us/library/windows/desktop/ee621019.
+bool IsSessionLocked() {
+  DCHECK_GT(base::win::GetVersion(), base::win::VERSION_WIN7);
+  bool is_locked = false;
+  LPWSTR buffer = nullptr;
+  DWORD buffer_length = 0;
+  if (::WTSQuerySessionInformation(WTS_CURRENT_SERVER, WTS_CURRENT_SESSION,
+                                   WTSSessionInfoEx, &buffer, &buffer_length) &&
+      buffer_length >= sizeof(WTSINFOEXW)) {
+    auto* info = reinterpret_cast<WTSINFOEXW*>(buffer);
+    is_locked =
+        info->Data.WTSInfoExLevel1.SessionFlags == WTS_SESSIONSTATE_LOCK;
+  }
+  if (buffer)
+    ::WTSFreeMemory(buffer);
+  return is_locked;
+}
+
+// Checks if the current desktop is called "default" to see if we are on the
+// lock screen or on the desktop. This returns true on Windows 10 if we are on
+// the lock screen before the password prompt. Use IsSessionLocked for
+// Windows 10 and above.
+bool IsDesktopNameDefault() {
+  DCHECK_LE(base::win::GetVersion(), base::win::VERSION_WIN7);
   bool is_locked = true;
   HDESK input_desk = ::OpenInputDesktop(0, 0, GENERIC_READ);
   if (input_desk) {
@@ -23,4 +54,12 @@
   return is_locked;
 }
 
+}  // namespace
+
+bool IsWorkstationLocked() {
+  return base::win::GetVersion() <= base::win::VERSION_WIN7
+             ? IsDesktopNameDefault()
+             : IsSessionLocked();
+}
+
 }  // namespace ui
diff --git a/ui/file_manager/file_manager/foreground/elements/files_quick_view.html b/ui/file_manager/file_manager/foreground/elements/files_quick_view.html
index dcb8755..949d7712 100644
--- a/ui/file_manager/file_manager/foreground/elements/files_quick_view.html
+++ b/ui/file_manager/file_manager/foreground/elements/files_quick_view.html
@@ -25,7 +25,7 @@
         <div id="file-path">[[filePath]]</div>
         <div class="buttons-group">
            <paper-button id="open-button" on-tap="onOpenInNewButtonTap" hidden$="[[!hasTask]]" aria-label="$i18n{QUICK_VIEW_OPEN_IN_NEW_BUTTON_LABEL}" tabindex="0" has-tooltip>
-             <iron-icon icon="files:open-in-new"></iron-icon>
+             <span>$i18n{QUICK_VIEW_OPEN_IN_NEW_BUTTON_LABEL}</span>
            </paper-button>
            <files-icon-button toggles id="metadata-button" on-tap="onMetadataButtonTap_" active="{{metadataBoxActive}}" aria-label="$i18n{QUICK_VIEW_TOGGLE_METADATA_BOX_BUTTON_LABEL}" tabindex="0" has-tooltip>
            </files-icon-button>
diff --git a/ui/file_manager/file_manager/main.html b/ui/file_manager/file_manager/main.html
index fb36826..0d80b7d 100644
--- a/ui/file_manager/file_manager/main.html
+++ b/ui/file_manager/file_manager/main.html
@@ -339,7 +339,7 @@
       <div id="location-breadcrumbs" class="breadcrumbs"></div>
       <div id="cancel-selection-button-wrapper">
         <paper-button id="cancel-selection-button" class="menu-button" tabindex="8"
-              aria-label="$i18n{CANCEL_SELECTION_BUTTON_LABEL}">
+              aria-label="$i18n{CANCEL_SELECTION_BUTTON_LABEL}" has-tooltip>
           <span class="icon-arrow-back"></span>
           <span id="cancel-selection-label">$i18n{CANCEL_SELECTION_BUTTON_LABEL}</span>
         </paper-button>
diff --git a/ui/file_manager/integration_tests/file_manager/background.js b/ui/file_manager/integration_tests/file_manager/background.js
index 57ca6ac2..c635458b 100644
--- a/ui/file_manager/integration_tests/file_manager/background.js
+++ b/ui/file_manager/integration_tests/file_manager/background.js
@@ -423,3 +423,32 @@
   ];
   steps.shift()();
 });
+
+/**
+ * Creates a folder shortcut to |directoryName| using the context menu. Note the
+ * current directory must be a parent of the given |directoryName|.
+ *
+ * @param {string} appId Files app windowId.
+ * @param {string} directoryName Directory of shortcut to be created.
+ * @return {Promise} Promise fulfilled on success.
+ */
+async function createShortcut(appId, directoryName) {
+  chrome.test.assertTrue(await remoteCall.callRemoteTestUtil(
+      'selectFile', appId, [directoryName]));
+
+  await remoteCall.waitForElement(appId, ['.table-row[selected]']);
+  chrome.test.assertTrue(await remoteCall.callRemoteTestUtil(
+      'fakeMouseRightClick', appId, ['.table-row[selected]']));
+
+
+  await remoteCall.waitForElement(appId, '#file-context-menu:not([hidden])');
+  await remoteCall.waitForElement(
+      appId,
+      '[command="#create-folder-shortcut"]:not([hidden]):not([disabled])');
+  chrome.test.assertTrue(await remoteCall.callRemoteTestUtil(
+      'fakeMouseClick', appId,
+      ['[command="#create-folder-shortcut"]:not([hidden]):not([disabled])']));
+
+  await remoteCall.waitForElement(
+      appId, `.tree-item[label="${directoryName}"]`);
+}
diff --git a/ui/file_manager/integration_tests/file_manager/directory_tree_context_menu.js b/ui/file_manager/integration_tests/file_manager/directory_tree_context_menu.js
index 7a0c8e7b..ef5fabb 100644
--- a/ui/file_manager/integration_tests/file_manager/directory_tree_context_menu.js
+++ b/ui/file_manager/integration_tests/file_manager/directory_tree_context_menu.js
@@ -3,6 +3,7 @@
 // found in the LICENSE file.
 
 'use strict';
+(() => {
 
 /**
  * Sets up for directory tree context menu test. In addition to normal setup, we
@@ -58,7 +59,6 @@
           'fakeMouseRightClick', appId, [pathQuery]),
       'fakeMouseRightClick failed');
 
-
   // Check: context menu item |id| should be shown enabled.
   await remoteCall.waitForElement(
       appId, `${contextMenu} [command="#${id}"]:not([disabled])`);
@@ -149,7 +149,6 @@
     useKeyboardShortcut, changeCurrentDirectory) {
   const appId = await setupForDirectoryTreeContextMenuTest();
 
-
   if (changeCurrentDirectory) {
     await remoteCall.navigateWithDirectoryTree(
         appId, RootPath.DOWNLOADS_PATH + '/photos', 'My files/Downloads');
@@ -189,6 +188,79 @@
 }
 
 /**
+ * Checks all visible items in the context menu for directory tree.
+ * @param {!string} appId
+ * @param {!string} treeItemQuery Query to item to be tested with context menu.
+ * @param {!Array<!Array<string|boolean>>} menuStates Mapping each command to
+ *     it's enabled state.
+ * @param {boolean=} rootsMenu True if the item uses #roots-context-menu instead
+ *     of #directory-tree-context-menu
+ */
+async function checkContextMenu(appId, treeItemQuery, menuStates, rootsMenu) {
+  // Focus the directory tree.
+  chrome.test.assertTrue(
+      !!await remoteCall.callRemoteTestUtil(
+          'focus', appId, ['#directory-tree']),
+      'focus failed: #directory-tree');
+
+  // Right click desired item in the directory tree.
+  chrome.test.assertTrue(
+      !!await remoteCall.callRemoteTestUtil(
+          'fakeMouseRightClick', appId, [treeItemQuery]),
+      'fakeMouseRightClick failed');
+
+  // Selector for a both context menu used on directory tree, only one should be
+  // visible at the time.
+  const menuQuery = rootsMenu ?
+      '#roots-context-menu:not([hidden]) cr-menu-item:not([hidden])' :
+      '#directory-tree-context-menu:not([hidden]) cr-menu-item:not([hidden])';
+
+  // Wait for each menu item to be in the desired state.
+  for (let [command, enabled] of menuStates) {
+    const menuItemQuery = menuQuery +
+        (enabled ? ':not([disabled])' : '[disabled]') +
+        `[command="${command}"]`;
+    await remoteCall.waitForElement(appId, menuItemQuery);
+  }
+
+  function stateString(state) {
+    return state ? 'enabled' : 'disabled';
+  }
+
+  // Grab all commands together and check they are in the expected order and
+  // state.
+  const actualItems = await remoteCall.callRemoteTestUtil(
+      'queryAllElements', appId, [menuQuery]);
+  let isDiff = false;
+  let msg = '\nContext menu in the wrong order/state:';
+  for (let i = 0; i < Math.max(menuStates.length, actualItems.length); i++) {
+    let expectedCommand = undefined;
+    let expectedState = undefined;
+    let actualCommand = undefined;
+    let actualState = undefined;
+    if (menuStates[i]) {
+      expectedCommand = menuStates[i][0];
+      expectedState = menuStates[i][1];
+    }
+    if (actualItems[i]) {
+      actualCommand = actualItems[i].attributes['command'];
+      actualState = actualItems[i].attributes['disabled'] ? false : true;
+    }
+    msg += '\n';
+    if (expectedCommand !== actualCommand || expectedState !== actualState) {
+      isDiff = true;
+    }
+    msg += ` index: ${i}`;
+    msg += `\n\t expected: ${expectedCommand} ${stateString(expectedState)}`;
+    msg += `\n\t      got: ${actualCommand} ${stateString(actualState)}`;
+  }
+
+  if (isDiff) {
+    chrome.test.assertTrue(false, msg);
+  }
+}
+
+/**
  * Tests copying a directory from directory tree with context menu.
  */
 testcase.dirCopyWithContextMenu = async () => {
@@ -383,7 +455,6 @@
       await remoteCall.callRemoteTestUtil('fakeKeyDown', appId, enterKey),
       'Enter key failed');
 
-
   // Confirm that current directory is now My files or /Downloads, because it
   // can't find the previously selected folder /Downloads/photos/child-folder,
   // since its path/parent has been renamed.
@@ -476,3 +547,55 @@
       false /* Do not use keyboard shortcut */,
       false /* Do not change current directory */);
 };
+
+/**
+ * Tests context menu for Recent root, currently it doesn't show context menu.
+ */
+testcase.dirContextMenuRecent = async () => {
+  const query = '#directory-tree [dir-type="FakeItem"][entry-label="Recent"]';
+
+  // Open Files app on Downloads.
+  const appId = await setupAndWaitUntilReady(RootPath.DOWNLOADS);
+
+  // Focus the directory tree.
+  chrome.test.assertTrue(
+      !!await remoteCall.callRemoteTestUtil(
+          'focus', appId, ['#directory-tree']),
+      'focus failed: #directory-tree');
+
+  // Right click Recent root.
+  chrome.test.assertTrue(
+      !!await remoteCall.callRemoteTestUtil(
+          'fakeMouseRightClick', appId, [query]),
+      'fakeMouseRightClick failed');
+
+  // Check that both menus are still hidden.
+  await remoteCall.waitForElement(appId, '#roots-context-menu[hidden]');
+  await remoteCall.waitForElement(
+      appId, '#directory-tree-context-menu[hidden]');
+};
+
+/**
+ * Tests context menu for Shortcut roots.
+ */
+testcase.dirContextMenuShortcut = async () => {
+  const menus = [
+      ['#rename', false],
+      ['#remove-folder-shortcut', true],
+      ['#share-with-linux', true],
+  ];
+  const entry = ENTRIES.directoryD;
+  const query =
+      `#directory-tree [dir-type='ShortcutItem'][label='${entry.nameText}']`;
+
+  // Open Files app on Drive.
+  const appId = await setupAndWaitUntilReady(RootPath.DRIVE, [], [entry]);
+
+  // Create a shortcut to directory D.
+  await createShortcut(appId, entry.nameText);
+
+  // Check the context menu is on desired state.
+  await checkContextMenu(appId, query, menus, true /* rootMenu */);
+};
+
+})();
diff --git a/ui/file_manager/integration_tests/file_manager/files_tooltip.js b/ui/file_manager/integration_tests/file_manager/files_tooltip.js
index 6d548a7..321278e49 100644
--- a/ui/file_manager/integration_tests/file_manager/files_tooltip.js
+++ b/ui/file_manager/integration_tests/file_manager/files_tooltip.js
@@ -11,6 +11,7 @@
 const searchButton = '#search-button[has-tooltip]';
 const viewButton = '#view-button[has-tooltip]';
 const breadcrumbs = '#breadcrumb-path-0';
+const cancelButton = '#cancel-selection-button[has-tooltip]';
 
 /**
  * Tests that tooltip is displayed when focusing an element with tooltip.
@@ -46,6 +47,20 @@
 
   // The tooltip should be hidden.
   tooltip = await remoteCall.waitForElement(appId, tooltipQueryHidden);
+
+  // Select all the files to enable the cancel selection button.
+  const ctrlA = ['#file-list', 'a', true, false, false];
+  await remoteCall.callRemoteTestUtil('fakeKeyDown', appId, ctrlA);
+
+  // Focus the cancel selection button.
+  chrome.test.assertTrue(
+      await remoteCall.callRemoteTestUtil('focus', appId, [cancelButton]));
+
+  // The tooltip should be visible.
+  tooltip = await remoteCall.waitForElement(appId, tooltipQueryVisible);
+  label =
+      await remoteCall.waitForElement(appId, [tooltipQueryVisible, '#label']);
+  chrome.test.assertEq('Cancel selection', label.text);
 };
 
 /**
@@ -106,7 +121,7 @@
   chrome.test.assertTrue(
       await remoteCall.callRemoteTestUtil('fakeMouseClick', appId, ['body']));
 
-  // The tooltip should be visible.
+  // The tooltip should be hidden.
   tooltip = await remoteCall.waitForElement(appId, tooltipQueryHidden);
 };
 })();
diff --git a/ui/file_manager/integration_tests/file_manager/folder_shortcuts.js b/ui/file_manager/integration_tests/file_manager/folder_shortcuts.js
index f4db181..9e19d9d 100644
--- a/ui/file_manager/integration_tests/file_manager/folder_shortcuts.js
+++ b/ui/file_manager/integration_tests/file_manager/folder_shortcuts.js
@@ -120,35 +120,6 @@
 }
 
 /**
- * Creates a folder shortcut to |directory| using the context menu. Note the
- * current directory must be a parent of the given |directory|.
- *
- * @param {string} appId Files app windowId.
- * @param {Object} directory Directory of shortcut to be created.
- * @return {Promise} Promise fulfilled on success.
- */
-async function createShortcut(appId, directory) {
-  chrome.test.assertTrue(await remoteCall.callRemoteTestUtil(
-      'selectFile', appId, [directory.name]));
-
-  await remoteCall.waitForElement(appId, ['.table-row[selected]']);
-  chrome.test.assertTrue(await remoteCall.callRemoteTestUtil(
-      'fakeMouseRightClick', appId, ['.table-row[selected]']));
-
-
-  await remoteCall.waitForElement(appId, '#file-context-menu:not([hidden])');
-  await remoteCall.waitForElement(
-      appId,
-      '[command="#create-folder-shortcut"]:not([hidden]):not([disabled])');
-  chrome.test.assertTrue(await remoteCall.callRemoteTestUtil(
-      'fakeMouseClick', appId,
-      ['[command="#create-folder-shortcut"]:not([hidden]):not([disabled])']));
-
-
-  await remoteCall.waitForElement(appId, directory.navItem);
-}
-
-/**
  * Removes the folder shortcut to |directory|. Note the current directory must
  * be a parent of the given |directory|.
  *
@@ -222,13 +193,13 @@
   await expandDirectoryTree(appId);
 
   // Create a shortcut to directory D.
-  await createShortcut(appId, DIRECTORY.D);
+  await createShortcut(appId, DIRECTORY.D.name);
 
   // Navigate to directory B.
   await navigateToDirectory(appId, DIRECTORY.B);
 
   // Create a shortcut to directory C.
-  await createShortcut(appId, DIRECTORY.C);
+  await createShortcut(appId, DIRECTORY.C.name);
 
   // Click the Drive root (My Drive) shortcut.
   await clickShortcut(appId, DIRECTORY.Drive);
@@ -283,7 +254,7 @@
   const appId2 = await openFilesAppOnDrive();
 
   // Create a shortcut to D.
-  await createShortcut(appId1, DIRECTORY.D);
+  await createShortcut(appId1, DIRECTORY.D.name);
 
   // Click the shortcut to D.
   await clickShortcut(appId1, DIRECTORY.D);
@@ -292,7 +263,7 @@
   await expectSelection(appId1, DIRECTORY.D, DIRECTORY.D);
 
   // Create a shortcut to A from the other window.
-  await createShortcut(appId2, DIRECTORY.A);
+  await createShortcut(appId2, DIRECTORY.A.name);
 
   // Check: current directory and selection should still be D.
   await expectSelection(appId1, DIRECTORY.D, DIRECTORY.D);
diff --git a/ui/gl/android/android_surface_control_compat.cc b/ui/gl/android/android_surface_control_compat.cc
index f54594a..d1fea97e 100644
--- a/ui/gl/android/android_surface_control_compat.cc
+++ b/ui/gl/android/android_surface_control_compat.cc
@@ -10,6 +10,7 @@
 #include "base/bind.h"
 #include "base/memory/ptr_util.h"
 #include "base/no_destructor.h"
+#include "ui/gfx/color_space.h"
 
 extern "C" {
 typedef struct ASurfaceTransactionStats ASurfaceTransactionStats;
@@ -35,6 +36,14 @@
   ASURFACE_TRANSACTION_VISIBILITY_SHOW = 1,
 };
 
+enum {
+  ADATASPACE_UNKNOWN = 0,
+  ADATASPACE_SCRGB_LINEAR = 406913024,
+  ADATASPACE_SRGB = 142671872,
+  ADATASPACE_DISPLAY_P3 = 143261696,
+  ADATASPACE_BT2020_PQ = 163971072,
+};
+
 // ASurfaceTransaction
 using pASurfaceTransaction_create = ASurfaceTransaction* (*)(void);
 using pASurfaceTransaction_delete = void (*)(ASurfaceTransaction*);
@@ -66,6 +75,10 @@
              ASurfaceControl* surface,
              const ARect rects[],
              uint32_t count);
+using pASurfaceTransaction_setBufferDataSpace =
+    void (*)(ASurfaceTransaction* transaction,
+             ASurfaceControl* surface,
+             uint64_t data_space);
 
 // ASurfaceTransactionStats
 using pASurfaceTransactionStats_getPresentFenceFd =
@@ -121,6 +134,7 @@
     LOAD_FUNCTION(main_dl_handle, ASurfaceTransaction_setGeometry);
     LOAD_FUNCTION(main_dl_handle, ASurfaceTransaction_setBufferTransparency);
     LOAD_FUNCTION(main_dl_handle, ASurfaceTransaction_setDamageRegion);
+    LOAD_FUNCTION(main_dl_handle, ASurfaceTransaction_setBufferDataSpace);
 
     LOAD_FUNCTION(main_dl_handle, ASurfaceTransactionStats_getPresentFenceFd);
     LOAD_FUNCTION(main_dl_handle, ASurfaceTransactionStats_getASurfaceControls);
@@ -150,6 +164,8 @@
   pASurfaceTransaction_setBufferTransparency
       ASurfaceTransaction_setBufferTransparencyFn;
   pASurfaceTransaction_setDamageRegion ASurfaceTransaction_setDamageRegionFn;
+  pASurfaceTransaction_setBufferDataSpace
+      ASurfaceTransaction_setBufferDataSpaceFn;
 
   // TransactionStats methods.
   pASurfaceTransactionStats_getPresentFenceFd
@@ -188,6 +204,21 @@
   return ANATIVEWINDOW_TRANSFORM_IDENTITY;
 }
 
+uint64_t ColorSpaceToADataSpace(const gfx::ColorSpace& color_space) {
+  if (!color_space.IsValid() || color_space == gfx::ColorSpace::CreateSRGB())
+    return ADATASPACE_SRGB;
+
+  if (color_space == gfx::ColorSpace::CreateSCRGBLinear())
+    return ADATASPACE_SCRGB_LINEAR;
+
+  if (color_space == gfx::ColorSpace::CreateDisplayP3D65())
+    return ADATASPACE_DISPLAY_P3;
+
+  // TODO(khushalsagar): Check if we can support BT2020 using
+  // ADATASPACE_BT2020_PQ.
+  return ADATASPACE_UNKNOWN;
+}
+
 SurfaceControl::TransactionStats ToTransactionStats(
     ASurfaceTransactionStats* stats) {
   SurfaceControl::TransactionStats transaction_stats;
@@ -241,18 +272,17 @@
 
 // static
 bool SurfaceControl::IsSupported() {
-#if 0
-  // TODO(khushalsagar): Enable this code when the frame is correctly reporting
-  // SDK version for P+.
-  const int sdk_int = base::android::BuildInfo::GetInstance()->sdk_int();
-  if (sdk_int < 29) {
-    LOG(ERROR) << "SurfaceControl not supported on sdk: " << sdk_int;
+  if (!base::android::BuildInfo::GetInstance()->is_at_least_q()) {
+    LOG(ERROR) << "SurfaceControl requires at least Q";
     return false;
   }
-#endif
   return SurfaceControlMethods::Get().supported;
 }
 
+bool SurfaceControl::SupportsColorSpace(const gfx::ColorSpace& color_space) {
+  return ColorSpaceToADataSpace(color_space) != ADATASPACE_UNKNOWN;
+}
+
 SurfaceControl::Surface::Surface() = default;
 
 SurfaceControl::Surface::Surface(const Surface& parent, const char* name) {
@@ -339,6 +369,13 @@
       transaction_, surface.surface(), &a_rect, 1u);
 }
 
+void SurfaceControl::Transaction::SetColorSpace(
+    const Surface& surface,
+    const gfx::ColorSpace& color_space) {
+  SurfaceControlMethods::Get().ASurfaceTransaction_setBufferDataSpaceFn(
+      transaction_, surface.surface(), ColorSpaceToADataSpace(color_space));
+}
+
 void SurfaceControl::Transaction::SetOnCompleteCb(
     OnCompleteCb cb,
     scoped_refptr<base::SingleThreadTaskRunner> task_runner) {
diff --git a/ui/gl/android/android_surface_control_compat.h b/ui/gl/android/android_surface_control_compat.h
index 003818c..580621d 100644
--- a/ui/gl/android/android_surface_control_compat.h
+++ b/ui/gl/android/android_surface_control_compat.h
@@ -22,12 +22,19 @@
 typedef struct ASurfaceTransaction ASurfaceTransaction;
 }
 
+namespace gfx {
+class ColorSpace;
+}  // namespace gfx
+
 namespace gl {
 
 class GL_EXPORT SurfaceControl {
  public:
   static bool IsSupported();
 
+  // Returns true if overlays with |color_space| are supported by the platform.
+  static bool SupportsColorSpace(const gfx::ColorSpace& color_space);
+
   class GL_EXPORT Surface : public base::RefCounted<Surface> {
    public:
     Surface();
@@ -92,6 +99,8 @@
                      gfx::OverlayTransform transform);
     void SetOpaque(const Surface& surface, bool opaque);
     void SetDamageRect(const Surface& surface, const gfx::Rect& rect);
+    void SetColorSpace(const Surface& surface,
+                       const gfx::ColorSpace& color_space);
 
     // Sets the callback which will be dispatched when the transaction is acked
     // by the framework.
diff --git a/ui/gl/gl_image.cc b/ui/gl/gl_image.cc
index 9a45aed..0ab4af7 100644
--- a/ui/gl/gl_image.cc
+++ b/ui/gl/gl_image.cc
@@ -15,6 +15,10 @@
   return false;
 }
 
+void GLImage::SetColorSpace(const gfx::ColorSpace& color_space) {
+  color_space_ = color_space;
+}
+
 bool GLImage::EmulatingRGB() const {
   return false;
 }
diff --git a/ui/gl/gl_image.h b/ui/gl/gl_image.h
index 95db99f..786f60f 100644
--- a/ui/gl/gl_image.h
+++ b/ui/gl/gl_image.h
@@ -100,7 +100,8 @@
       std::unique_ptr<gfx::GpuFence> gpu_fence) = 0;
 
   // Set the color space when image is used as an overlay.
-  virtual void SetColorSpace(const gfx::ColorSpace& color_space) = 0;
+  virtual void SetColorSpace(const gfx::ColorSpace& color_space);
+  const gfx::ColorSpace& color_space() const { return color_space_; }
 
   // Flush any preceding rendering for the image.
   virtual void Flush() = 0;
@@ -140,6 +141,8 @@
  protected:
   virtual ~GLImage() {}
 
+  gfx::ColorSpace color_space_;
+
  private:
   friend class base::RefCounted<GLImage>;
 
diff --git a/ui/gl/gl_image_ahardwarebuffer.h b/ui/gl/gl_image_ahardwarebuffer.h
index febdedc..d37f141 100644
--- a/ui/gl/gl_image_ahardwarebuffer.h
+++ b/ui/gl/gl_image_ahardwarebuffer.h
@@ -41,7 +41,6 @@
                             const gfx::RectF& crop_rect,
                             bool enable_blend,
                             std::unique_ptr<gfx::GpuFence> gpu_fence) override;
-  void SetColorSpace(const gfx::ColorSpace& color_space) override {}
   void Flush() override;
   void OnMemoryDump(base::trace_event::ProcessMemoryDump* pmd,
                     uint64_t process_tracing_id,
diff --git a/ui/gl/gl_image_dxgi.cc b/ui/gl/gl_image_dxgi.cc
index 3fdd1b01..d966b7d 100644
--- a/ui/gl/gl_image_dxgi.cc
+++ b/ui/gl/gl_image_dxgi.cc
@@ -244,10 +244,6 @@
   return false;
 }
 
-void GLImageDXGI::SetColorSpace(const gfx::ColorSpace& color_space) {
-  color_space_ = color_space;
-}
-
 bool GLImageDXGI::InitializeHandle(base::win::ScopedHandle handle,
                                    uint32_t level,
                                    gfx::BufferFormat format) {
diff --git a/ui/gl/gl_image_dxgi.h b/ui/gl/gl_image_dxgi.h
index aab1031..c1337c1 100644
--- a/ui/gl/gl_image_dxgi.h
+++ b/ui/gl/gl_image_dxgi.h
@@ -48,7 +48,6 @@
                             const gfx::RectF& crop_rect,
                             bool enable_blend,
                             std::unique_ptr<gfx::GpuFence> gpu_fence) override;
-  void SetColorSpace(const gfx::ColorSpace& color_space) override;
 
   const gfx::ColorSpace& color_space() const { return color_space_; }
   Microsoft::WRL::ComPtr<IDXGIKeyedMutex> keyed_mutex() { return keyed_mutex_; }
@@ -65,7 +64,6 @@
   ~GLImageDXGI() override;
 
   gfx::BufferFormat buffer_format_ = gfx::BufferFormat::BGRA_8888;
-  gfx::ColorSpace color_space_;
   base::win::ScopedHandle handle_;
   Microsoft::WRL::ComPtr<IDXGIKeyedMutex> keyed_mutex_;
   size_t level_ = 0;
diff --git a/ui/gl/gl_image_glx.h b/ui/gl/gl_image_glx.h
index 646753e..069bab3 100644
--- a/ui/gl/gl_image_glx.h
+++ b/ui/gl/gl_image_glx.h
@@ -38,7 +38,6 @@
                             const gfx::RectF& crop_rect,
                             bool enable_blend,
                             std::unique_ptr<gfx::GpuFence> gpu_fence) override;
-  void SetColorSpace(const gfx::ColorSpace& color_space) override {}
   void Flush() override {}
   void OnMemoryDump(base::trace_event::ProcessMemoryDump* pmd,
                     uint64_t process_tracing_id,
diff --git a/ui/gl/gl_image_io_surface.h b/ui/gl/gl_image_io_surface.h
index 2592478..3661759 100644
--- a/ui/gl/gl_image_io_surface.h
+++ b/ui/gl/gl_image_io_surface.h
@@ -110,10 +110,6 @@
   base::ScopedCFTypeRef<CVPixelBufferRef> cv_pixel_buffer_;
   gfx::GenericSharedMemoryId io_surface_id_;
 
-  // Cache the color space, because re-assigning the same value can be
-  // expensive.
-  gfx::ColorSpace color_space_;
-
   base::ThreadChecker thread_checker_;
   // The default value of Rec. 601 is based on historical shader code.
   gfx::ColorSpace color_space_for_yuv_to_rgb_ = gfx::ColorSpace::CreateREC601();
diff --git a/ui/gl/gl_image_io_surface.mm b/ui/gl/gl_image_io_surface.mm
index 751fa4cf..3d6467e 100644
--- a/ui/gl/gl_image_io_surface.mm
+++ b/ui/gl/gl_image_io_surface.mm
@@ -453,7 +453,7 @@
 void GLImageIOSurface::SetColorSpace(const gfx::ColorSpace& color_space) {
   if (color_space_ == color_space)
     return;
-  color_space_ = color_space;
+  GLImage::SetColorSpace(color_space);
 
   // Prefer to use data from DisplayICCProfiles, which will give a byte-for-byte
   // match for color spaces of the system displays. Note that DisplayICCProfiles
diff --git a/ui/gl/gl_image_memory.h b/ui/gl/gl_image_memory.h
index 9ba081b1..558faee5 100644
--- a/ui/gl/gl_image_memory.h
+++ b/ui/gl/gl_image_memory.h
@@ -44,7 +44,6 @@
                             const gfx::RectF& crop_rect,
                             bool enable_blend,
                             std::unique_ptr<gfx::GpuFence> gpu_fence) override;
-  void SetColorSpace(const gfx::ColorSpace& color_space) override {}
   void Flush() override {}
   Type GetType() const override;
 
diff --git a/ui/gl/gl_image_stub.h b/ui/gl/gl_image_stub.h
index 60fb0ed1..42e60e1 100644
--- a/ui/gl/gl_image_stub.h
+++ b/ui/gl/gl_image_stub.h
@@ -34,7 +34,6 @@
                             const gfx::RectF& crop_rect,
                             bool enable_blend,
                             std::unique_ptr<gfx::GpuFence> gpu_fence) override;
-  void SetColorSpace(const gfx::ColorSpace& color_space) override {}
   void Flush() override {}
   void OnMemoryDump(base::trace_event::ProcessMemoryDump* pmd,
                     uint64_t process_tracing_id,
diff --git a/ui/gl/gl_image_surface_texture.h b/ui/gl/gl_image_surface_texture.h
index 31a589e..12b09bbc 100644
--- a/ui/gl/gl_image_surface_texture.h
+++ b/ui/gl/gl_image_surface_texture.h
@@ -40,7 +40,6 @@
                             const gfx::RectF& crop_rect,
                             bool enable_blend,
                             std::unique_ptr<gfx::GpuFence> gpu_fence) override;
-  void SetColorSpace(const gfx::ColorSpace& color_space) override {}
   void Flush() override {}
   void OnMemoryDump(base::trace_event::ProcessMemoryDump* pmd,
                     uint64_t process_tracing_id,
diff --git a/ui/gl/gl_surface_egl_surface_control.cc b/ui/gl/gl_surface_egl_surface_control.cc
index 8dfa5bde..0804a3a 100644
--- a/ui/gl/gl_surface_egl_surface_control.cc
+++ b/ui/gl/gl_surface_egl_surface_control.cc
@@ -177,6 +177,11 @@
     const gfx::RectF& crop_rect,
     bool enable_blend,
     std::unique_ptr<gfx::GpuFence> gpu_fence) {
+  if (!SurfaceControl::SupportsColorSpace(image->color_space())) {
+    LOG(ERROR) << "Not supported color space used with overlay : "
+               << image->color_space().ToString();
+  }
+
   if (!pending_transaction_)
     pending_transaction_.emplace();
 
@@ -252,6 +257,12 @@
     pending_transaction_->SetOpaque(*surface_state.surface, opaque);
   }
 
+  if (uninitialized || surface_state.color_space != image->color_space()) {
+    surface_state.color_space = image->color_space();
+    pending_transaction_->SetColorSpace(*surface_state.surface,
+                                        image->color_space());
+  }
+
   return true;
 }
 
diff --git a/ui/gl/gl_surface_egl_surface_control.h b/ui/gl/gl_surface_egl_surface_control.h
index ec5e93b..3e1d7279 100644
--- a/ui/gl/gl_surface_egl_surface_control.h
+++ b/ui/gl/gl_surface_egl_surface_control.h
@@ -98,6 +98,7 @@
     gfx::Rect src;
     gfx::OverlayTransform transform = gfx::OVERLAY_TRANSFORM_NONE;
     bool opaque = true;
+    gfx::ColorSpace color_space;
 
     // Indicates whether buffer for this layer was updated in the currently
     // pending transaction, or the last transaction submitted if there isn't
diff --git a/ui/ozone/platform/wayland/wayland_surface_factory.cc b/ui/ozone/platform/wayland/wayland_surface_factory.cc
index dc4d719..f3f49a2 100644
--- a/ui/ozone/platform/wayland/wayland_surface_factory.cc
+++ b/ui/ozone/platform/wayland/wayland_surface_factory.cc
@@ -36,23 +36,7 @@
       gfx::AcceleratedWidget widget) override;
 
   scoped_refptr<gl::GLSurface> CreateSurfacelessViewGLSurface(
-      gfx::AcceleratedWidget window) override {
-    // Only EGLGLES2 is supported with surfaceless view gl.
-    if (gl::GetGLImplementation() != gl::kGLImplementationEGLGLES2)
-      return nullptr;
-
-#if defined(WAYLAND_GBM)
-    // If there is a gbm device available, use surfaceless gl surface.
-    if (!connection_->gbm_device())
-      return nullptr;
-    return gl::InitializeGLSurface(new GbmSurfacelessWayland(
-        static_cast<WaylandSurfaceFactory*>(
-            OzonePlatform::GetInstance()->GetSurfaceFactoryOzone()),
-        window));
-#else
-    return nullptr;
-#endif
-  }
+      gfx::AcceleratedWidget window) override;
 
   scoped_refptr<gl::GLSurface> CreateOffscreenGLSurface(
       const gfx::Size& size) override;
@@ -86,6 +70,25 @@
   return gl::InitializeGLSurface(new GLSurfaceWayland(std::move(egl_window)));
 }
 
+scoped_refptr<gl::GLSurface> GLOzoneEGLWayland::CreateSurfacelessViewGLSurface(
+    gfx::AcceleratedWidget window) {
+  // Only EGLGLES2 is supported with surfaceless view gl.
+  if (gl::GetGLImplementation() != gl::kGLImplementationEGLGLES2)
+    return nullptr;
+
+#if defined(WAYLAND_GBM)
+  // If there is a gbm device available, use surfaceless gl surface.
+  if (!connection_->gbm_device())
+    return nullptr;
+  return gl::InitializeGLSurface(new GbmSurfacelessWayland(
+      static_cast<WaylandSurfaceFactory*>(
+          OzonePlatform::GetInstance()->GetSurfaceFactoryOzone()),
+      window));
+#else
+  return nullptr;
+#endif
+}
+
 scoped_refptr<gl::GLSurface> GLOzoneEGLWayland::CreateOffscreenGLSurface(
     const gfx::Size& size) {
   if (gl::GLSurfaceEGL::IsEGLSurfacelessContextSupported() &&
diff --git a/ui/shell_dialogs/select_file_dialog_win.cc b/ui/shell_dialogs/select_file_dialog_win.cc
index 470a0fd..6740efa 100644
--- a/ui/shell_dialogs/select_file_dialog_win.cc
+++ b/ui/shell_dialogs/select_file_dialog_win.cc
@@ -57,8 +57,8 @@
 //   1. only files that have 'file_ext' as their extension
 //   2. all files (only added if 'include_all_files' is true)
 // If a description is not provided for a file extension, it will be retrieved
-// from the registry. If the file extension does not exist in the registry, it
-// will be omitted from the filter, as it is likely a bogus extension.
+// from the registry. If the file extension does not exist in the registry, a
+// default description will be created (e.g. "qqq" yields "QQQ File").
 std::vector<FileFilterSpec> FormatFilterForExtensions(
     const std::vector<base::string16>& file_ext,
     const std::vector<base::string16>& ext_desc,
@@ -73,10 +73,7 @@
     include_all_files = true;
 
   std::vector<FileFilterSpec> result;
-
-  // Precompute the final size of the resulting vector.
-  size_t final_size = file_ext.size() + (include_all_files ? 1 : 0);
-  result.resize(final_size);
+  result.reserve(file_ext.size() + 1);
 
   for (size_t i = 0; i < file_ext.size(); ++i) {
     base::string16 ext = file_ext[i];
@@ -107,21 +104,20 @@
       if (!GetRegistryDescriptionFromExtension(first_extension, &desc)) {
         // The extension doesn't exist in the registry. Create a description
         // based on the unknown extension type (i.e. if the extension is .qqq,
-        // the we create a description "QQQ File (.qqq)").
+        // then we create a description "QQQ File").
+        desc = l10n_util::GetStringFUTF16(IDS_APP_SAVEAS_EXTENSION_FORMAT,
+                                          base::i18n::ToUpper(ext_name));
         include_all_files = true;
-        desc =
-            l10n_util::GetStringFUTF16(IDS_APP_SAVEAS_EXTENSION_FORMAT,
-                                       base::i18n::ToUpper(ext_name), ext_name);
       }
       if (desc.empty())
         desc = L"*." + ext_name;
     }
 
-    result[i] = {desc, ext};
+    result.push_back({desc, ext});
   }
 
   if (include_all_files)
-    result.back() = {all_desc, all_ext};
+    result.push_back({all_desc, all_ext});
 
   return result;
 }
diff --git a/ui/strings/ui_strings.grd b/ui/strings/ui_strings.grd
index 4ceede17..bcedfbf 100644
--- a/ui/strings/ui_strings.grd
+++ b/ui/strings/ui_strings.grd
@@ -319,6 +319,13 @@
         <message name="IDS_APP_SAVEAS_ALL_FILES" desc="Save As dialog box default text">
           All Files
         </message>
+      </if>
+      <if expr="is_win">
+        <message name="IDS_APP_SAVEAS_EXTENSION_FORMAT" desc="Save As dialog box extension format text">
+          <ph name="SAVEAS_EXTENSION_TYPE">$1<ex>EXE</ex></ph> File
+        </message>
+      </if>
+      <if expr="is_macosx">
         <message name="IDS_APP_SAVEAS_EXTENSION_FORMAT" desc="Save As dialog box extension format text">
           <ph name="SAVEAS_EXTENSION_TYPE">$1<ex>EXE</ex></ph> File (.<ph name="SAVEAS_EXTENSION_NAME">$2<ex>exe</ex></ph>)
         </message>
@@ -742,6 +749,9 @@
       <message name="IDS_MESSAGE_NOTIFICATION_SETTINGS_BUTTON_ACCESSIBLE_NAME" desc="The spoken feedback text for the settings button in a notification. Usually 'button' is suffixed to this text automatically.">
         Notification settings
       </message>
+      <message name="IDS_MESSAGE_NOTIFICATION_SEND_TAB_TO_SELF_DEVICE_INFO_PREFIX" desc="For SEND_TAB_TO_SELF notification, this will be a prefix of device info.">
+        Shared from 
+      </message>
       <if expr="chromeos">
         <message name="IDS_MESSAGE_CENTER_NOTIFIER_SCREENSHOT_NAME" desc="The name of screenshot notifier that is a system component">
           Screenshot
diff --git a/ui/views/layout/flex_layout.cc b/ui/views/layout/flex_layout.cc
index daf765dc..efc6b844 100644
--- a/ui/views/layout/flex_layout.cc
+++ b/ui/views/layout/flex_layout.cc
@@ -817,7 +817,7 @@
   // Do the initial layout update, calculating spacing between children.
   ChildViewSpacing child_spacing(
       base::BindRepeating(&FlexLayoutInternal::CalculateChildSpacing,
-                          base::Unretained(this), base::ConstRef(*layout)));
+                          base::Unretained(this), std::cref(*layout)));
   UpdateLayoutFromChildren(layout.get(), &child_spacing, bounds);
 
   if (main_axis_bounded && !order_to_view_index.empty()) {